UNPKG

react-rebound

Version:

High-performance spring animations in React.

524 lines (500 loc) 18.7 kB
import React from 'react'; import rebound, { SpringConfig } from 'rebound'; var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var performanceNow = createCommonjsModule(function (module) { // Generated by CoffeeScript 1.12.2 (function() { var getNanoSeconds, hrtime, loadTime, moduleLoadTime, nodeLoadTime, upTime; if ((typeof performance !== "undefined" && performance !== null) && performance.now) { module.exports = function() { return performance.now(); }; } else if ((typeof process !== "undefined" && process !== null) && process.hrtime) { module.exports = function() { return (getNanoSeconds() - nodeLoadTime) / 1e6; }; hrtime = process.hrtime; getNanoSeconds = function() { var hr; hr = hrtime(); return hr[0] * 1e9 + hr[1]; }; moduleLoadTime = getNanoSeconds(); upTime = process.uptime() * 1e9; nodeLoadTime = moduleLoadTime - upTime; } else if (Date.now) { module.exports = function() { return Date.now() - loadTime; }; loadTime = Date.now(); } else { module.exports = function() { return new Date().getTime() - loadTime; }; loadTime = new Date().getTime(); } }).call(commonjsGlobal); }); var root = typeof window === 'undefined' ? commonjsGlobal : window , vendors = ['moz', 'webkit'] , suffix = 'AnimationFrame' , raf = root['request' + suffix] , caf = root['cancel' + suffix] || root['cancelRequest' + suffix]; for(var i = 0; !raf && i < vendors.length; i++) { raf = root[vendors[i] + 'Request' + suffix]; caf = root[vendors[i] + 'Cancel' + suffix] || root[vendors[i] + 'CancelRequest' + suffix]; } // Some versions of FF have rAF but not cAF if(!raf || !caf) { var last = 0 , id = 0 , queue = [] , frameDuration = 1000 / 60; raf = function(callback) { if(queue.length === 0) { var _now = performanceNow() , next = Math.max(0, frameDuration - (_now - last)); last = next + _now; setTimeout(function() { var cp = queue.slice(0); // Clear queue here to prevent // callbacks from appending listeners // to the current frame's queue queue.length = 0; for(var i = 0; i < cp.length; i++) { if(!cp[i].cancelled) { try{ cp[i].callback(last); } catch(e) { setTimeout(function() { throw e }, 0); } } } }, Math.round(next)); } queue.push({ handle: ++id, callback: callback, cancelled: false }); return id }; caf = function(handle) { for(var i = 0; i < queue.length; i++) { if(queue[i].handle === handle) { queue[i].cancelled = true; } } }; } var raf_1 = function(fn) { // Wrap in a new function to prevent // `cancel` potentially being assigned // to the native rAF function return raf.call(root, fn) }; var cancel = function() { caf.apply(root, arguments); }; var polyfill = function(object) { if (!object) { object = root; } object.requestAnimationFrame = raf; object.cancelAnimationFrame = caf; }; raf_1.cancel = cancel; raf_1.polyfill = polyfill; var MultiSpring = /** @class */ (function () { function MultiSpring(system, config) { this.springs = []; this.system = system; this.config = config; } MultiSpring.prototype.destroy = function () { for (var _i = 0, _a = this.springs; _i < _a.length; _i++) { var spring = _a[_i]; spring.destroy(); } }; MultiSpring.prototype.getCurrentValue = function () { return this.springs.map(function (s) { return s.getCurrentValue(); }); }; MultiSpring.prototype.setCurrentValue = function (value, skipSetAtRest) { for (var i = 0; i < value.length; i++) { if (!this.springs[i]) this.springs[i] = this.system.createSpring(); this.springs[i].setCurrentValue(value[i], skipSetAtRest); } }; MultiSpring.prototype.getEndValue = function () { return this.springs.map(function (s) { return s.getEndValue(); }); }; MultiSpring.prototype.setEndValue = function (value) { for (var i = 0; i < value.length; i++) { if (!this.springs[i]) this.springs[i] = this.system.createSpringWithConfig(this.config); this.springs[i].setEndValue(value[i]); } }; MultiSpring.prototype.getVelocity = function () { return this.springs.map(function (s) { return s.getVelocity(); }); }; MultiSpring.prototype.setAtRest = function () { for (var _i = 0, _a = this.springs; _i < _a.length; _i++) { var spring = _a[_i]; spring.setAtRest(); } }; MultiSpring.prototype.setVelocity = function (value) { for (var _i = 0, _a = this.springs; _i < _a.length; _i++) { var spring = _a[_i]; spring.setVelocity(value); } }; MultiSpring.prototype.setRestSpeedThreshold = function (value) { for (var _i = 0, _a = this.springs; _i < _a.length; _i++) { var spring = _a[_i]; spring.setRestSpeedThreshold(value); } }; MultiSpring.prototype.setRestDisplacementThreshold = function (value) { for (var _i = 0, _a = this.springs; _i < _a.length; _i++) { var spring = _a[_i]; spring.setRestDisplacementThreshold(value); } }; MultiSpring.prototype.setOvershootClampingEnabled = function (value) { for (var _i = 0, _a = this.springs; _i < _a.length; _i++) { var spring = _a[_i]; spring.setOvershootClampingEnabled(value); } }; MultiSpring.prototype.addListener = function (value) { for (var _i = 0, _a = this.springs; _i < _a.length; _i++) { var spring = _a[_i]; spring.addListener(value); } }; MultiSpring.prototype.removeListener = function (value) { for (var _i = 0, _a = this.springs; _i < _a.length; _i++) { var spring = _a[_i]; spring.removeListener(value); } }; MultiSpring.prototype.removeAllListeners = function () { for (var _i = 0, _a = this.springs; _i < _a.length; _i++) { var spring = _a[_i]; spring.removeAllListeners(); } }; return MultiSpring; }()); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) t[p[i]] = s[p[i]]; return t; } function cssFunction(name) { var params = []; for (var _i = 1; _i < arguments.length; _i++) { params[_i - 1] = arguments[_i]; } return function () { var values = []; for (var _i = 0; _i < arguments.length; _i++) { values[_i] = arguments[_i]; } return name + "(" + params.map(function (p, i) { return p(values[i]); }) + ")"; }; } function px(n) { return (n || 0) + "px"; } function alpha(x) { return "" + (x < 0 ? 0 : x > 1 ? 1 : x); } function ratio(n) { return "" + n; } function deg(n) { return n + "deg"; } function colorComponent(x) { return "" + (x < 0 ? 0 : x > 255 ? 255 : Math.round(x)); } var rgb = cssFunction('rgb', colorComponent, colorComponent, colorComponent); var rgba = cssFunction('rgba', colorComponent, colorComponent, colorComponent, alpha); var translate = cssFunction('translate', px, px); var translate3d = cssFunction('translate3d', px, px, px); var scale = cssFunction('scale', ratio, ratio); var rotate = cssFunction('rotate', deg, deg); var rotateZ = cssFunction('rotateZ', deg); var skew = cssFunction('skew', deg, deg); function color(_a) { var r = _a[0], g = _a[1], b = _a[2], a = _a[3]; return typeof a === 'undefined' ? rgb(r, g, b) : rgba(r, g, b, a); } var numericalProperties = { top: px, left: px, right: px, bottom: px, width: px, height: px, opacity: alpha, color: color, background: color, backgroundColor: color, borderBottomColor: color, borderColor: color, borderLeftColor: color, borderRightColor: color, borderTopColor: color, outlineColor: color, textDecorationColor: color, fontSize: px, lineHeight: px, letterSpacing: px, }; var transformProperties = { translateX: true, translateY: true, translateZ: true, scaleX: true, scaleY: true, rotateX: true, rotateY: true, rotateZ: true, skewX: true, skewY: true, }; function toTransformStyle(_a) { var tx = _a.translateX, ty = _a.translateY, tz = _a.translateZ, sx = _a.scaleX, sy = _a.scaleY, rx = _a.rotateX, ry = _a.rotateY, rz = _a.rotateZ, kx = _a.skewX, ky = _a.skewY; var transforms = []; if (tz !== undefined) { transforms.push(translate3d(tx || 0, ty || 0, tz || 0)); } else if (tx !== undefined || ty !== undefined) { transforms.push(translate(tx || 0, ty || 0)); } if (sx !== undefined || sy !== undefined) { transforms.push(scale(sx || 1, sy || 1)); } if (rx !== undefined || ry !== undefined) { transforms.push(rotate(rx || 0, ry || 0)); } if (rz !== undefined) { transforms.push(rotateZ(rz || 0)); } if (kx !== undefined || ky !== undefined) { transforms.push(skew(kx || 0, ky || 0)); } if (transforms.length === 0) return 'none'; return transforms.join(' '); } function toStyle(props) { var transformProps = {}; var style = {}; for (var p in props) { var val = props[p]; if (val === undefined || val === null) continue; if (p in transformProperties) { var prop = p; transformProps[prop] = val; } else if (p in numericalProperties) { var prop = p; // eslint-disable-next-line @typescript-eslint/no-explicit-any style[prop] = numericalProperties[prop](val); } else { // eslint-disable-next-line no-console console.warn('Unsupported prop', p); } } return __assign({}, style, { transform: toTransformStyle(transformProps) }); } var springSystem = new rebound.SpringSystem(); function usePersisted(value) { var ref = React.useRef(value); ref.current = value; return ref; } function createSpring(startValue, tension, friction) { var spring; if (Array.isArray(startValue)) { spring = new MultiSpring(springSystem, new SpringConfig(tension, friction)); spring.setCurrentValue(startValue); } else { spring = springSystem.createSpringWithConfig(new SpringConfig(tension, friction)); spring.setCurrentValue(startValue); } return spring; } function useAnimation(ref, props, _a) { var _b = _a === void 0 ? {} : _a, _c = _b.animate, animate = _c === void 0 ? true : _c, _d = _b.tension, tension = _d === void 0 ? 230 : _d, _e = _b.friction, friction = _e === void 0 ? 22 : _e, _f = _b.delay, delay = _f === void 0 ? 0 : _f, _g = _b.displacementThreshold, displacementThreshold = _g === void 0 ? 0.001 : _g, _h = _b.speedThreshold, speedThreshold = _h === void 0 ? 0.001 : _h, _j = _b.clamp, clamp = _j === void 0 ? false : _j, onStart = _b.onStart, onEnd = _b.onEnd; var springs = React.useRef({}); var animating = React.useRef(0); var onStartRef = usePersisted(onStart); var onSpringActivate = React.useCallback(function () { animating.current += 1; animating.current === 1 && onStartRef.current && onStartRef.current(); }, [onStartRef]); var onEndRef = usePersisted(onEnd); var onSpringAtRest = React.useCallback(function () { animating.current -= 1; animating.current === 0 && onEndRef.current && onEndRef.current(); }, [onEndRef]); var request = React.useRef(null); var onSpringUpdate = React.useCallback(function () { function performUpdate() { if (!ref.current) return; request.current = null; var currentValues = {}; for (var prop in springs.current) { currentValues[prop] = springs.current[prop].getCurrentValue(); } var style = toStyle(currentValues); for (var p in style) { var prop = p; ref.current.style[prop] = style[prop]; } } if (!request.current) { request.current = raf_1(performUpdate); } }, [ref]); React.useEffect(function () { var _loop_1 = function (prop) { var value = props[prop]; if (value === undefined) return "continue"; var spring = springs.current[prop]; if (!spring) { spring = springs.current[prop] = createSpring(value, tension, friction); spring.setRestSpeedThreshold(speedThreshold); spring.setRestDisplacementThreshold(displacementThreshold); spring.setOvershootClampingEnabled(clamp); spring.addListener({ onSpringActivate: onSpringActivate, onSpringAtRest: onSpringAtRest, onSpringUpdate: onSpringUpdate }); } if (!animate) { spring.setCurrentValue(value); return "continue"; } if (delay) { setTimeout(function () { return spring.setEndValue(value); }, delay); } else { spring.setEndValue(value); } }; for (var prop in props) { _loop_1(prop); } }); // Cleanup React.useEffect(function () { }, []); return springs.current; } var Animate = React.forwardRef(function (_a, forwardedRef) { var _b = _a.animate, animate = _b === void 0 ? true : _b, _c = _a.tension, tension = _c === void 0 ? 230 : _c, _d = _a.friction, friction = _d === void 0 ? 22 : _d, _e = _a.delay, delay = _e === void 0 ? 0 : _e, _f = _a.displacementThreshold, displacementThreshold = _f === void 0 ? 0.001 : _f, _g = _a.speedThreshold, speedThreshold = _g === void 0 ? 0.001 : _g, _h = _a.clamp, clamp = _h === void 0 ? false : _h, onStart = _a.onStart, onEnd = _a.onEnd, children = _a.children, props = __rest(_a, ["animate", "tension", "friction", "delay", "displacementThreshold", "speedThreshold", "clamp", "onStart", "onEnd", "children"]); var ref = React.useRef(); var animating = React.useRef(false); var _j = React.useState(null), setState = _j[1]; var latestChildren = React.useRef(children); latestChildren.current = children; var springs = useAnimation(ref, props, { animate: animate, tension: tension, friction: friction, delay: delay, displacementThreshold: displacementThreshold, speedThreshold: speedThreshold, clamp: clamp, onStart: function () { animating.current = true; if (typeof latestChildren.current === 'function') { setState(null); // Trigger a re-render } onStart && onStart(); }, onEnd: function () { animating.current = false; if (typeof latestChildren.current === 'function') { setState(null); // Trigger a re-render } onEnd && onEnd(); }, }); React.useImperativeHandle(forwardedRef, function () { return ({ setVelocity: function (prop, value) { var spring = springs[prop]; // eslint-disable-next-line @typescript-eslint/no-explicit-any spring && spring.setVelocity(value); }, setCurrentValue: function (prop, value) { var spring = springs[prop]; // eslint-disable-next-line @typescript-eslint/no-explicit-any spring && spring.setCurrentValue(value); }, getCurrentValue: function (prop) { var spring = springs[prop]; return spring && spring.getCurrentValue(); }, }); }, [springs]); if (typeof children === 'function') { children = children(animating.current); } var child = React.Children.only(children); return React.cloneElement(child, { ref: function (element) { ref.current = element; // Hack to forward ref to caller if (child.ref && 'current' in child.ref) { child.ref.current = element; } else if (typeof child.ref === 'function') { child.ref(element); } }, }); }); Animate.displayName = 'Animate'; export { Animate, useAnimation };