victory-animation
Version:
animation wrapper for victory components
211 lines (182 loc) • 9.31 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
var _d3Ease = require("d3-ease");
var _d3Ease2 = _interopRequireDefault(_d3Ease);
var _d3Interpolate = require("d3-interpolate");
var _d3Interpolate2 = _interopRequireDefault(_d3Interpolate);
var _d3Timer = require("d3-timer");
var _util = require("../util");
// Nearly all animation libraries are duration-based, not velocity-based.
// In other words, you say "I want the animation to take this long", not
// "I want things to move this fast". Using velocity will make the animation
// take different amounts of time on computers of different speed, since
// they'll have a different framerate but still adjust values by the same
// velocity each frame. But for now we still support velocity as we have code
// using it. Since we use `d3-timer` now and it's duration-based, choose a
// velocity multiplier here that just happens to result in animations going
// approximately the same speed on systems getting around 60 fps.
var VELOCITY_MULTIPLIER = 16.5; // ~ 1 / 60
(0, _util.addVictoryInterpolator)();
var VictoryAnimation = (function (_React$Component) {
_inherits(VictoryAnimation, _React$Component);
_createClass(VictoryAnimation, null, [{
key: "propTypes",
value: {
/**
* The child of should be a function that takes an object of tweened values
* and returns a component to render.
*/
children: _react2["default"].PropTypes.func,
/**
* The velocity prop specifies how fast the animation should run.
*/
velocity: _react2["default"].PropTypes.number,
/**
* The easing prop specifies an easing function name to use for tweening.
*/
easing: _react2["default"].PropTypes.oneOf(["back", "backIn", "backOut", "backInOut", "bounce", "bounceIn", "bounceOut", "bounceInOut", "circle", "circleIn", "circleOut", "circleInOut", "linear", "linearIn", "linearOut", "linearInOut", "cubic", "cubicIn", "cubicOut", "cubicInOut", "elastic", "elasticIn", "elasticOut", "elasticInOut", "exp", "expIn", "expOut", "expInOut", "poly", "polyIn", "polyOut", "polyInOut", "quad", "quadIn", "quadOut", "quadInOut", "sin", "sinIn", "sinOut", "sinInOut"]),
/**
* The delay prop specifies a delay in milliseconds before the animation
* begins. If multiple values are in the animation queue, it is the delay
* between each animation.
*/
delay: _react2["default"].PropTypes.number,
/**
* The onEnd prop specifies a function to run when the animation ends. If
* multiple animations are in the queue, it is called after the last
* animation.
*/
onEnd: _react2["default"].PropTypes.func,
/**
* The data prop specifies the latest set of values to tween to. When this
* prop changes, VictoryAnimation will begin animating from the current
* value to the new value.
*/
data: _react2["default"].PropTypes.oneOfType([_react2["default"].PropTypes.object, _react2["default"].PropTypes.array])
},
enumerable: true
}, {
key: "defaultProps",
value: {
/* velocity modifies step each frame */
velocity: 0.02,
/* easing modifies step each frame */
easing: "quadInOut",
/* delay between transitions */
delay: 0,
/* we got nothin' */
data: {}
},
enumerable: true
}]);
function VictoryAnimation(props) {
_classCallCheck(this, VictoryAnimation);
_get(Object.getPrototypeOf(VictoryAnimation.prototype), "constructor", this).call(this, props);
/* defaults */
this.state = Array.isArray(this.props.data) ? this.props.data[0] : this.props.data;
this.interpolator = null;
this.queue = Array.isArray(this.props.data) ? this.props.data.slice(1) : [];
/* build easing function */
this.ease = _d3Ease2["default"][this.props.easing];
/*
unlike React.createClass({}), there is no autobinding of this in ES6 classes
so we bind functionToBeRunEachFrame to current instance of victory animation class
*/
this.functionToBeRunEachFrame = this.functionToBeRunEachFrame.bind(this);
}
_createClass(VictoryAnimation, [{
key: "componentDidMount",
value: function componentDidMount() {
// Length check prevents us from triggering `onEnd` in `traverseQueue`.
if (this.queue.length) {
this.traverseQueue();
}
}
/* lifecycle */
}, {
key: "componentWillReceiveProps",
value: function componentWillReceiveProps(nextProps) {
/* cancel existing loop if it exists */
if (this.timer) {
this.timer.stop();
}
/* If an object was supplied */
if (!Array.isArray(nextProps.data)) {
// Replace the tween queue. Could set `this.queue = [nextProps.data]`,
// but let's reuse the same array.
this.queue.length = 0;
this.queue.push(nextProps.data);
/* If an array was supplied */
} else {
var _queue;
/* Extend the tween queue */
(_queue = this.queue).push.apply(_queue, _toConsumableArray(nextProps.data));
}
/* Start traversing the tween queue */
this.traverseQueue();
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
if (this.timer) {
this.timer.stop();
}
}
/* Traverse the tween queue */
}, {
key: "traverseQueue",
value: function traverseQueue() {
if (this.queue.length) {
/* Get the next index */
var data = this.queue[0];
/* compare cached version to next props */
this.interpolator = _d3Interpolate2["default"].value(this.state, data);
/* reset step to zero */
this.timer = (0, _d3Timer.timer)(this.functionToBeRunEachFrame, this.props.delay);
} else if (this.props.onEnd) {
this.props.onEnd();
}
}
/* every frame we... */
}, {
key: "functionToBeRunEachFrame",
value: function functionToBeRunEachFrame(elapsed) {
/*
step can generate imprecise values, sometimes greater than 1
if this happens set the state to 1 and return, cancelling the timer
*/
var step = elapsed / (VELOCITY_MULTIPLIER / this.props.velocity);
if (step >= 1) {
this.setState(this.interpolator(1));
this.timer.stop();
this.queue.shift();
this.traverseQueue(); // Will take care of calling `onEnd`.
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
*/
this.setState(this.interpolator(this.ease(step)));
}
}, {
key: "render",
value: function render() {
return this.props.children(this.state);
}
}]);
return VictoryAnimation;
})(_react2["default"].Component);
exports["default"] = VictoryAnimation;
module.exports = exports["default"];