UNPKG

3.82 kBPlain TextView Raw
1'use strict';
2import {
3 defineAnimation,
4 getReduceMotionForAnimation,
5 recognizePrefixSuffix,
6} from './util';
7import type {
8 Animation,
9 Timestamp,
10 AnimatableValue,
11 AnimationObject,
12 ReduceMotion,
13} from '../commonTypes';
14import type { ClampAnimation } from './commonTypes';
15
16type withClampType = <T extends number | string>(
17 config: {
18 min?: T;
19 max?: T;
20 },
21 clampedAnimation: T
22) => T;
23
24export const withClamp = function <T extends number | string>(
25 config: { min?: T; max?: T; reduceMotion?: ReduceMotion },
26 _animationToClamp: AnimationObject<T> | (() => AnimationObject<T>)
27): Animation<ClampAnimation> {
28 'worklet';
29 return defineAnimation<ClampAnimation, AnimationObject<T>>(
30 _animationToClamp,
31 (): ClampAnimation => {
32 'worklet';
33 const animationToClamp =
34 typeof _animationToClamp === 'function'
35 ? _animationToClamp()
36 : _animationToClamp;
37
38 const strippedMin =
39 config.min === undefined
40 ? undefined
41 : recognizePrefixSuffix(config.min).strippedValue;
42
43 const strippedMax =
44 config.max === undefined
45 ? undefined
46 : recognizePrefixSuffix(config.max).strippedValue;
47
48 function clampOnFrame(
49 animation: ClampAnimation,
50 now: Timestamp
51 ): boolean {
52 const finished = animationToClamp.onFrame(animationToClamp, now);
53
54 if (animationToClamp.current === undefined) {
55 console.warn(
56 "[Reanimated] Error inside 'withClamp' animation, the inner animation has invalid current value"
57 );
58 return true;
59 } else {
60 const { prefix, strippedValue, suffix } = recognizePrefixSuffix(
61 animationToClamp.current
62 );
63
64 let newValue;
65
66 if (strippedMax !== undefined && strippedMax < strippedValue) {
67 newValue = strippedMax;
68 } else if (strippedMin !== undefined && strippedMin > strippedValue) {
69 newValue = strippedMin;
70 } else {
71 newValue = strippedValue;
72 }
73
74 animation.current =
75 typeof animationToClamp.current === 'number'
76 ? newValue
77 : `${prefix === undefined ? '' : prefix}${newValue}${
78 suffix === undefined ? '' : suffix
79 }`;
80 }
81
82 return finished;
83 }
84
85 function onStart(
86 animation: Animation<any>,
87 value: AnimatableValue,
88 now: Timestamp,
89 previousAnimation: Animation<any> | null
90 ): void {
91 animation.current = value;
92 animation.previousAnimation = animationToClamp;
93 const animationBeforeClamped = previousAnimation?.previousAnimation;
94 if (
95 config.max !== undefined &&
96 config.min !== undefined &&
97 config.max < config.min
98 ) {
99 console.warn(
100 '[Reanimated] Wrong config was provided to withClamp. Min value is bigger than max'
101 );
102 }
103
104 animationToClamp.onStart(
105 animationToClamp,
106 /** provide the current value of the previous animation of the clamped animation
107 so we can animate from the original "un-truncated" value
108 */
109 animationBeforeClamped?.current || value,
110 now,
111 animationBeforeClamped
112 );
113 }
114
115 const callback = (finished?: boolean): void => {
116 if (animationToClamp.callback) {
117 animationToClamp.callback(finished);
118 }
119 };
120
121 return {
122 isHigherOrder: true,
123 onFrame: clampOnFrame,
124 onStart,
125 current: animationToClamp.current!,
126 callback,
127 previousAnimation: null,
128 reduceMotion: getReduceMotionForAnimation(config.reduceMotion),
129 };
130 }
131 );
132} as withClampType;