UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

160 lines (147 loc) 5.09 kB
'use strict'; import type { AnimatableValue, Animation, AnimationCallback, EasingFunction, ReduceMotion, Timestamp, } from '../commonTypes'; import type { EasingFunctionFactory } from '../Easing'; import { Easing } from '../Easing'; import { assertEasingIsWorklet, defineAnimation, getReduceMotionForAnimation, } from './util'; /** * The timing animation configuration. * * @param duration - Length of the animation (in milliseconds). Defaults to 300. * @param easing - An easing function which defines the animation curve. * Defaults to `Easing.inOut(Easing.quad)`. * @param reduceMotion - Determines how the animation responds to the device's * reduced motion accessibility setting. Default to `ReduceMotion.System` - * {@link ReduceMotion}. * @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withTiming#config- */ interface TimingConfig { duration?: number; reduceMotion?: ReduceMotion; easing?: EasingFunction | EasingFunctionFactory; } export type WithTimingConfig = TimingConfig; export interface TimingAnimation extends Animation<TimingAnimation> { type: string; easing: EasingFunction; startValue: AnimatableValue; startTime: Timestamp; progress: number; toValue: AnimatableValue; current: AnimatableValue; } interface InnerTimingAnimation extends Omit<TimingAnimation, 'toValue' | 'current'> { toValue: number; current: number; } // TODO TYPESCRIPT This is temporary type put in here to get rid of our .d.ts file type withTimingType = <T extends AnimatableValue>( toValue: T, userConfig?: TimingConfig, callback?: AnimationCallback ) => T; /** * Lets you create an animation based on duration and easing. * * @param toValue - The value on which the animation will come at rest - * {@link AnimatableValue}. * @param config - The timing animation configuration - {@link TimingConfig}. * @param callback - A function called on animation complete - * {@link AnimationCallback}. * @returns An [animation * object](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#animation-object) * which holds the current state of the animation. * @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withTiming */ export const withTiming = function ( toValue: AnimatableValue, userConfig?: TimingConfig, callback?: AnimationCallback ): Animation<TimingAnimation> { 'worklet'; if (__DEV__ && userConfig?.easing) { assertEasingIsWorklet(userConfig.easing); } return defineAnimation<TimingAnimation>(toValue, () => { 'worklet'; const config: Required<Omit<TimingConfig, 'reduceMotion'>> = { duration: 300, easing: Easing.inOut(Easing.quad), }; if (userConfig) { Object.keys(userConfig).forEach( (key) => ((config as any)[key] = userConfig[key as keyof typeof userConfig]) ); } function timing(animation: InnerTimingAnimation, now: Timestamp): boolean { // eslint-disable-next-line @typescript-eslint/no-shadow const { toValue, startTime, startValue } = animation; const runtime = now - startTime; if (runtime >= config.duration) { // reset startTime to avoid reusing finished animation config in `start` method animation.startTime = 0; animation.current = toValue; return true; } const progress = animation.easing(runtime / config.duration); animation.current = (startValue as number) + (toValue - (startValue as number)) * progress; return false; } function onStart( animation: TimingAnimation, value: number, now: Timestamp, previousAnimation: Animation<TimingAnimation> ): void { if ( previousAnimation && (previousAnimation as TimingAnimation).type === 'timing' && (previousAnimation as TimingAnimation).toValue === toValue && (previousAnimation as TimingAnimation).startTime ) { // to maintain continuity of timing animations we check if we are starting // new timing over the old one with the same parameters. If so, we want // to copy animation timeline properties animation.startTime = (previousAnimation as TimingAnimation).startTime; animation.startValue = ( previousAnimation as TimingAnimation ).startValue; } else { animation.startTime = now; animation.startValue = value; } animation.current = value; if (typeof config.easing === 'object') { animation.easing = config.easing.factory(); } else { animation.easing = config.easing; } } return { type: 'timing', onFrame: timing, onStart: onStart as (animation: TimingAnimation, now: number) => boolean, progress: 0, toValue, startValue: 0, startTime: 0, easing: () => 0, current: toValue, callback, reduceMotion: getReduceMotionForAnimation(userConfig?.reduceMotion), } as TimingAnimation; }); } as withTimingType;