UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

123 lines (119 loc) 4.34 kB
import { defineAnimation, getReduceMotionForAnimation } from './util'; import { isWeb } from '../PlatformChecker'; const IS_WEB = isWeb(); // TODO TYPESCRIPT This is a temporary type to get rid of .d.ts file. export const withDecay = function (userConfig, callback) { 'worklet'; return defineAnimation(0, () => { 'worklet'; const config = { deceleration: 0.998, velocityFactor: 1, velocity: 0, rubberBandFactor: 0.6 }; if (userConfig) { Object.keys(userConfig).forEach(key => config[key] = userConfig[key]); } const VELOCITY_EPS = IS_WEB ? 1 / 20 : 1; const DERIVATIVE_EPS = 0.1; const SLOPE_FACTOR = 0.1; let decay; if (config.rubberBandEffect) { decay = (animation, now) => { const { lastTimestamp, startTimestamp, current, velocity } = animation; const deltaTime = Math.min(now - lastTimestamp, 64); const clampIndex = Math.abs(current - config.clamp[0]) < Math.abs(current - config.clamp[1]) ? 0 : 1; let derivative = 0; if (current < config.clamp[0] || current > config.clamp[1]) { derivative = current - config.clamp[clampIndex]; } const v = velocity * Math.exp(-(1 - config.deceleration) * (now - startTimestamp) * SLOPE_FACTOR) - derivative * config.rubberBandFactor; if (Math.abs(derivative) > DERIVATIVE_EPS) { animation.springActive = true; } else if (animation.springActive) { animation.current = config.clamp[clampIndex]; return true; } else if (Math.abs(v) < VELOCITY_EPS) { return true; } animation.current = current + v * config.velocityFactor * deltaTime / 1000; animation.velocity = v; animation.lastTimestamp = now; return false; }; } else { decay = (animation, now) => { const { lastTimestamp, startTimestamp, initialVelocity, current, velocity } = animation; const deltaTime = Math.min(now - lastTimestamp, 64); const v = velocity * Math.exp(-(1 - config.deceleration) * (now - startTimestamp) * SLOPE_FACTOR); animation.current = current + v * config.velocityFactor * deltaTime / 1000; animation.velocity = v; animation.lastTimestamp = now; if (config.clamp) { if (initialVelocity < 0 && animation.current <= config.clamp[0]) { animation.current = config.clamp[0]; return true; } else if (initialVelocity > 0 && animation.current >= config.clamp[1]) { animation.current = config.clamp[1]; return true; } } return Math.abs(v) < VELOCITY_EPS; }; } function validateConfig() { if (config.clamp) { if (!Array.isArray(config.clamp)) { throw new Error(`[Reanimated] \`config.clamp\` must be an array but is ${typeof config.clamp}.`); } if (config.clamp.length !== 2) { throw new Error(`[Reanimated] \`clamp array\` must contain 2 items but is given ${config.clamp.length}.`); } } if (config.velocityFactor <= 0) { throw new Error(`[Reanimated] \`config.velocityFactor\` must be greather then 0 but is ${config.velocityFactor}.`); } if (config.rubberBandEffect && !config.clamp) { throw new Error('[Reanimated] You need to set `clamp` property when using `rubberBandEffect`.'); } } function onStart(animation, value, now) { animation.current = value; animation.lastTimestamp = now; animation.startTimestamp = now; animation.initialVelocity = config.velocity; validateConfig(); if (animation.reduceMotion && config.clamp) { if (value < config.clamp[0]) { animation.current = config.clamp[0]; } else if (value > config.clamp[1]) { animation.current = config.clamp[1]; } } } return { onFrame: decay, onStart, callback, velocity: config.velocity ?? 0, initialVelocity: 0, current: 0, lastTimestamp: 0, startTimestamp: 0, reduceMotion: getReduceMotionForAnimation(config.reduceMotion) }; }); }; //# sourceMappingURL=decay.js.map