victory-core
Version:
141 lines (133 loc) • 5.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.VictoryAnimation = void 0;
var _react = _interopRequireDefault(require("react"));
var d3Ease = _interopRequireWildcard(require("victory-vendor/d3-ease"));
var _util = require("./util");
var _timerContext = _interopRequireDefault(require("../victory-util/timer-context"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Single animation object to interpolate
*/
/**
* Animation styles to interpolate
*/
/** d3-ease changed the naming scheme for ease from "linear" -> "easeLinear" etc. */
const formatAnimationName = name => {
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
return `ease${capitalizedName}`;
};
const DEFAULT_DURATION = 1000;
const VictoryAnimation = _ref => {
let {
duration = DEFAULT_DURATION,
easing = "quadInOut",
delay = 0,
data,
children,
onEnd
} = _ref;
const [state, setState] = _react.default.useState({
data: Array.isArray(data) ? data[0] : data,
animationInfo: {
progress: 0,
animating: false
}
});
const timer = _react.default.useContext(_timerContext.default).animationTimer;
const queue = _react.default.useRef(Array.isArray(data) ? data.slice(1) : []);
const interpolator = _react.default.useRef(null);
const loopID = _react.default.useRef(undefined);
const ease = d3Ease[formatAnimationName(easing)];
_react.default.useEffect(() => {
// Length check prevents us from triggering `onEnd` in `traverseQueue`.
if (queue.current.length) {
traverseQueue();
}
// Clean up the animation loop
return () => {
if (loopID.current) {
timer.unsubscribe(loopID.current);
} else {
timer.stop();
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
_react.default.useEffect(() => {
// If the previous animation didn't finish, force it to complete before starting a new one
if (interpolator.current && state.animationInfo && state.animationInfo.progress < 1) {
setState({
data: interpolator.current(1),
animationInfo: {
progress: 1,
animating: false,
terminating: true
}
});
} else {
// Cancel existing loop if it exists
timer.unsubscribe(loopID.current);
// Set the tween queue to the new data
queue.current = Array.isArray(data) ? data : [data];
// Start traversing the tween queue
traverseQueue();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data]);
const traverseQueue = () => {
if (queue.current.length) {
const nextData = queue.current[0];
// Compare cached version to next props
interpolator.current = (0, _util.victoryInterpolator)(state.data, nextData);
// Reset step to zero
if (delay) {
setTimeout(() => {
loopID.current = timer.subscribe(functionToBeRunEachFrame, duration);
}, delay);
} else {
loopID.current = timer.subscribe(functionToBeRunEachFrame, duration);
}
} else if (onEnd) {
onEnd();
}
};
const functionToBeRunEachFrame = elapsed => {
if (!interpolator.current) return;
// Step can generate imprecise values, sometimes greater than 1
// if this happens set the state to 1 and return, cancelling the timer
const step = duration ? elapsed / duration : 1;
if (step >= 1) {
setState({
data: interpolator.current(1),
animationInfo: {
progress: 1,
animating: false,
terminating: true
}
});
if (loopID.current) {
timer.unsubscribe(loopID.current);
}
queue.current.shift();
traverseQueue();
return;
}
// If we're not at the end of the timer, set the state by passing
// current step value that's transformed by the ease function to the
// interpolator, which is cached for performance whenever props are received
setState({
data: interpolator.current(ease(step)),
animationInfo: {
progress: step,
animating: step < 1
}
});
};
return children(state.data, state.animationInfo);
};
exports.VictoryAnimation = VictoryAnimation;