UNPKG

5.05 kBPlain TextView Raw
1'use strict';
2import type { EasingFunction, EasingFunctionFactory } from '../Easing';
3import { Easing } from '../Easing';
4import {
5 assertEasingIsWorklet,
6 defineAnimation,
7 getReduceMotionForAnimation,
8} from './util';
9import type {
10 Animation,
11 AnimationCallback,
12 Timestamp,
13 AnimatableValue,
14 ReduceMotion,
15} from '../commonTypes';
16
17/**
18 * The timing animation configuration.
19 *
20 * @param duration - Length of the animation (in milliseconds). Defaults to 300.
21 * @param easing - An easing function which defines the animation curve. Defaults to `Easing.inOut(Easing.quad)`.
22 * @param reduceMotion - Determines how the animation responds to the device's reduced motion accessibility setting. Default to `ReduceMotion.System` - {@link ReduceMotion}.
23 * @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withTiming#config-
24 */
25interface TimingConfig {
26 duration?: number;
27 reduceMotion?: ReduceMotion;
28 easing?: EasingFunction | EasingFunctionFactory;
29}
30
31export type WithTimingConfig = TimingConfig;
32
33export interface TimingAnimation extends Animation<TimingAnimation> {
34 type: string;
35 easing: EasingFunction;
36 startValue: AnimatableValue;
37 startTime: Timestamp;
38 progress: number;
39 toValue: AnimatableValue;
40 current: AnimatableValue;
41}
42
43interface InnerTimingAnimation
44 extends Omit<TimingAnimation, 'toValue' | 'current'> {
45 toValue: number;
46 current: number;
47}
48
49// TODO TYPESCRIPT This is temporary type put in here to get rid of our .d.ts file
50type withTimingType = <T extends AnimatableValue>(
51 toValue: T,
52 userConfig?: TimingConfig,
53 callback?: AnimationCallback
54) => T;
55
56/**
57 * Lets you create an animation based on duration and easing.
58 *
59 * @param toValue - The value on which the animation will come at rest - {@link AnimatableValue}.
60 * @param config - The timing animation configuration - {@link TimingConfig}.
61 * @param callback - A function called on animation complete - {@link AnimationCallback}.
62 * @returns An [animation object](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#animation-object) which holds the current state of the animation.
63 * @see https://docs.swmansion.com/react-native-reanimated/docs/animations/withTiming
64 */
65export const withTiming = function (
66 toValue: AnimatableValue,
67 userConfig?: TimingConfig,
68 callback?: AnimationCallback
69): Animation<TimingAnimation> {
70 'worklet';
71
72 if (__DEV__ && userConfig?.easing) {
73 assertEasingIsWorklet(userConfig.easing);
74 }
75
76 return defineAnimation<TimingAnimation>(toValue, () => {
77 'worklet';
78 const config: Required<Omit<TimingConfig, 'reduceMotion'>> = {
79 duration: 300,
80 easing: Easing.inOut(Easing.quad),
81 };
82 if (userConfig) {
83 Object.keys(userConfig).forEach(
84 (key) =>
85 ((config as any)[key] = userConfig[key as keyof typeof userConfig])
86 );
87 }
88
89 function timing(animation: InnerTimingAnimation, now: Timestamp): boolean {
90 // eslint-disable-next-line @typescript-eslint/no-shadow
91 const { toValue, startTime, startValue } = animation;
92 const runtime = now - startTime;
93
94 if (runtime >= config.duration) {
95 // reset startTime to avoid reusing finished animation config in `start` method
96 animation.startTime = 0;
97 animation.current = toValue;
98 return true;
99 }
100 const progress = animation.easing(runtime / config.duration);
101 animation.current =
102 (startValue as number) + (toValue - (startValue as number)) * progress;
103 return false;
104 }
105
106 function onStart(
107 animation: TimingAnimation,
108 value: number,
109 now: Timestamp,
110 previousAnimation: Animation<TimingAnimation>
111 ): void {
112 if (
113 previousAnimation &&
114 (previousAnimation as TimingAnimation).type === 'timing' &&
115 (previousAnimation as TimingAnimation).toValue === toValue &&
116 (previousAnimation as TimingAnimation).startTime
117 ) {
118 // to maintain continuity of timing animations we check if we are starting
119 // new timing over the old one with the same parameters. If so, we want
120 // to copy animation timeline properties
121 animation.startTime = (previousAnimation as TimingAnimation).startTime;
122 animation.startValue = (
123 previousAnimation as TimingAnimation
124 ).startValue;
125 } else {
126 animation.startTime = now;
127 animation.startValue = value;
128 }
129 animation.current = value;
130 if (typeof config.easing === 'object') {
131 animation.easing = config.easing.factory();
132 } else {
133 animation.easing = config.easing;
134 }
135 }
136
137 return {
138 type: 'timing',
139 onFrame: timing,
140 onStart: onStart as (animation: TimingAnimation, now: number) => boolean,
141 progress: 0,
142 toValue,
143 startValue: 0,
144 startTime: 0,
145 easing: () => 0,
146 current: toValue,
147 callback,
148 reduceMotion: getReduceMotionForAnimation(userConfig?.reduceMotion),
149 } as TimingAnimation;
150 });
151} as withTimingType;
152
\No newline at end of file