UNPKG

addimated

Version:

An always interruptable, declarative animation library for React

203 lines (173 loc) 8.29 kB
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck"; import _createClass from "@babel/runtime/helpers/esm/createClass"; import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf"; import _inherits from "@babel/runtime/helpers/esm/inherits"; import invariant from "invariant"; import { Animation } from "./Animation"; import { fromBouncinessAndSpeed, fromOrigamiTensionAndFriction } from "./SpringConfig"; import { withDefault } from "./WithDefault"; var SpringAnimation = /*#__PURE__*/ function (_Animation) { _inherits(SpringAnimation, _Animation); function SpringAnimation(config) { var _this; _classCallCheck(this, SpringAnimation); _this = _possibleConstructorReturn(this, _getPrototypeOf(SpringAnimation).call(this)); _this.overshootClamping = withDefault(config.overshootClamping, false); _this.restDisplacementThreshold = withDefault(config.restDisplacementThreshold, 0.001); _this.restSpeedThreshold = withDefault(config.restSpeedThreshold, 0.001); _this.initialVelocity = withDefault(config.velocity, NaN); _this.lastVelocity = withDefault(config.velocity, NaN); _this.toValue = config.toValue; _this.delay = withDefault(config.delay, 0); if (config.stiffness !== undefined || config.damping !== undefined || config.mass !== undefined) { !(config.bounciness === undefined && config.speed === undefined && config.tension === undefined && config.friction === undefined) ? process.env.NODE_ENV !== "production" ? invariant(false, "You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one") : invariant(false) : void 0; _this.stiffness = withDefault(config.stiffness, 100); _this.damping = withDefault(config.damping, 10); _this.mass = withDefault(config.mass, 1); } else if (config.bounciness !== undefined || config.speed !== undefined) { // Convert the origami bounciness/speed values to stiffness/damping // We assume mass is 1. !(config.tension === undefined && config.friction === undefined && config.stiffness === undefined && config.damping === undefined && config.mass === undefined) ? process.env.NODE_ENV !== "production" ? invariant(false, "You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one") : invariant(false) : void 0; var springConfig = fromBouncinessAndSpeed(withDefault(config.bounciness, 8), withDefault(config.speed, 12)); _this.stiffness = springConfig.stiffness; _this.damping = springConfig.damping; _this.mass = 1; } else { // Convert the origami tension/friction values to stiffness/damping // We assume mass is 1. var _springConfig = fromOrigamiTensionAndFriction(withDefault(config.tension, 40), withDefault(config.friction, 7)); _this.stiffness = _springConfig.stiffness; _this.damping = _springConfig.damping; _this.mass = 1; } !(_this.stiffness > 0) ? process.env.NODE_ENV !== "production" ? invariant(false, "Stiffness value must be greater than 0") : invariant(false) : void 0; !(_this.damping > 0) ? process.env.NODE_ENV !== "production" ? invariant(false, "Damping value must be greater than 0") : invariant(false) : void 0; !(_this.mass > 0) ? process.env.NODE_ENV !== "production" ? invariant(false, "Mass value must be greater than 0") : invariant(false) : void 0; return _this; } _createClass(SpringAnimation, [{ key: "getInternalState", value: function getInternalState() { return { lastPosition: this.lastPosition, lastVelocity: this.lastVelocity, lastTime: this.lastTime }; } }, { key: "nextFrame", value: function nextFrame(now) { // TODO: Rethink delay handling here if (now <= this.lastTime) return [this.startPosition, false]; var deltaTime = (now - this.lastTime) / 1000; this.frameTime += deltaTime; var c = this.damping; var m = this.mass; var k = this.stiffness; var v0 = -this.initialVelocity; var zeta = c / (2 * Math.sqrt(k * m)); // damping ratio var omega0 = Math.sqrt(k / m); // undamped angular frequency of the oscillator (rad/ms) var omega1 = omega0 * Math.sqrt(1.0 - zeta * zeta); // exponential decay var x0 = this.toValue - this.startPosition; // calculate the oscillation from x0 = 1 to x = 0 var position = 0.0; var velocity = 0.0; var t = this.frameTime; if (zeta < 1) { // Under damped var envelope = Math.exp(-zeta * omega0 * t); position = this.toValue - envelope * ((v0 + zeta * omega0 * x0) / omega1 * Math.sin(omega1 * t) + x0 * Math.cos(omega1 * t)); // This looks crazy -- it's actually just the derivative of the // oscillation function velocity = zeta * omega0 * envelope * (Math.sin(omega1 * t) * (v0 + zeta * omega0 * x0) / omega1 + x0 * Math.cos(omega1 * t)) - envelope * (Math.cos(omega1 * t) * (v0 + zeta * omega0 * x0) - omega1 * x0 * Math.sin(omega1 * t)); } else { // Critically damped var _envelope = Math.exp(-omega0 * t); position = this.toValue - _envelope * (x0 + (v0 + omega0 * x0) * t); velocity = _envelope * (v0 * (t * omega0 - 1) + t * x0 * (omega0 * omega0)); } this.lastTime = now; this.lastPosition = position; this.lastVelocity = velocity; // Conditions for stopping the spring animation var finished = false; var isOvershooting = false; if (this.overshootClamping && this.stiffness !== 0) { if (this.startPosition < this.toValue) { isOvershooting = position > this.toValue; } else { isOvershooting = position < this.toValue; } } var isVelocity = Math.abs(velocity) <= this.restSpeedThreshold; var isDisplacement = true; if (this.stiffness !== 0) { isDisplacement = Math.abs(this.toValue - position) <= this.restDisplacementThreshold; } if (isOvershooting || isVelocity && isDisplacement) { if (this.stiffness !== 0) { // Ensure that we end up with a round value this.lastPosition = this.toValue; this.lastVelocity = 0; position = this.toValue; } finished = true; } return [position, finished]; } }, { key: "start", value: function start(animatedVal, _fromValue, onEnd) { var currentVal = animatedVal.__getValue(); animatedVal.model = this.toValue; this.active = true; this.fromValue = currentVal - this.toValue; this.toValue = 0; this.endCallback = onEnd; if (isNaN(this.initialVelocity) || isNaN(this.lastVelocity)) { var velocity = animatedVal.velocity != null ? animatedVal.velocity * 1000 : 0; this.initialVelocity = velocity; this.lastVelocity = velocity; } this.startPosition = this.fromValue; this.lastPosition = this.startPosition; this.currentValue = currentVal; this.lastTime = performance.now() + this.delay; this.frameTime = 0; animatedVal.animations.forEach(function (anim) { return anim.stop(false); }); return [this]; } }, { key: "step", value: function step(timestamp) { var _this$nextFrame = this.nextFrame(timestamp), _this$nextFrame2 = _slicedToArray(_this$nextFrame, 2), currentValue = _this$nextFrame2[0], finished = _this$nextFrame2[1]; this.currentValue = currentValue; if (finished) { this.stop(true); } } }, { key: "getValue", value: function getValue() { return this.currentValue; } }, { key: "stop", value: function stop() { var finished = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; this.ended = true; this.endCallback && this.endCallback({ finished: finished }); } }]); return SpringAnimation; }(Animation); export { SpringAnimation }; //# sourceMappingURL=SpringAnimation.js.map