@tamagui/animations-reanimated 2.4.5 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/createAnimations.cjs +361 -124
- package/dist/cjs/createAnimations.native.js +519 -191
- package/dist/cjs/createAnimations.native.js.map +1 -1
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/esm/createAnimations.mjs +361 -124
- package/dist/esm/createAnimations.mjs.map +1 -1
- package/dist/esm/createAnimations.native.js +519 -191
- package/dist/esm/createAnimations.native.js.map +1 -1
- package/package.json +12 -6
- package/src/createAnimations.tsx +596 -165
- package/types/createAnimations.d.ts.map +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getEffectiveAnimation, normalizeTransition } from "@tamagui/animation-helpers";
|
|
2
2
|
import { getSplitStyles, hooks, isWeb, Text, useComposedRefs, useEvent, useIsomorphicLayoutEffect, useThemeWithState, View } from "@tamagui/core";
|
|
3
3
|
import { ResetPresence, usePresence } from "@tamagui/use-presence";
|
|
4
|
+
import normalizeColor from "@react-native/normalize-colors";
|
|
4
5
|
import React, { forwardRef, useMemo, useRef } from "react";
|
|
5
6
|
import Animated_, { cancelAnimation, runOnJS, runOnUI, useAnimatedReaction, useAnimatedStyle, useDerivedValue, useSharedValue, withDelay, withSpring, withTiming } from "react-native-reanimated";
|
|
6
7
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -42,9 +43,162 @@ const cloneAnimationValue = value => {
|
|
|
42
43
|
}
|
|
43
44
|
return value;
|
|
44
45
|
};
|
|
46
|
+
const cloneStyleRecord = style => {
|
|
47
|
+
const next = {};
|
|
48
|
+
for (const key in style) {
|
|
49
|
+
next[key] = cloneAnimationValue(style[key]);
|
|
50
|
+
}
|
|
51
|
+
return next;
|
|
52
|
+
};
|
|
45
53
|
const cloneTransitionConfig = config => {
|
|
46
54
|
return cloneAnimationValue(config);
|
|
47
55
|
};
|
|
56
|
+
const getAnimatedTransforms = value => {
|
|
57
|
+
if (!Array.isArray(value)) return [];
|
|
58
|
+
return value.filter(item => item !== null && typeof item === "object" && !Array.isArray(item));
|
|
59
|
+
};
|
|
60
|
+
const getRemovedAnimatedKeys = (current, previous) => {
|
|
61
|
+
const removedKeys = {};
|
|
62
|
+
for (const key of previous) {
|
|
63
|
+
if (!current.has(key)) removedKeys[key] = true;
|
|
64
|
+
}
|
|
65
|
+
return removedKeys;
|
|
66
|
+
};
|
|
67
|
+
const getImplicitDefault = (key, targetValue) => {
|
|
68
|
+
"worklet";
|
|
69
|
+
|
|
70
|
+
const value = key === "opacity" || key === "scale" || key === "scaleX" || key === "scaleY" ? 1 : 0;
|
|
71
|
+
if (typeof targetValue !== "string") return value;
|
|
72
|
+
let suffixIndex = 0;
|
|
73
|
+
while (suffixIndex < targetValue.length) {
|
|
74
|
+
const character = targetValue[suffixIndex];
|
|
75
|
+
const code = character.charCodeAt(0);
|
|
76
|
+
if (code >= 48 && code <= 57 || character === "." || character === "-" || character === "+") {
|
|
77
|
+
suffixIndex++;
|
|
78
|
+
} else {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return suffixIndex > 0 ? `${value}${targetValue.slice(suffixIndex)}` : value;
|
|
83
|
+
};
|
|
84
|
+
const COLOR_STYLE_KEYS = {
|
|
85
|
+
backgroundColor: true,
|
|
86
|
+
borderColor: true,
|
|
87
|
+
borderLeftColor: true,
|
|
88
|
+
borderRightColor: true,
|
|
89
|
+
borderTopColor: true,
|
|
90
|
+
borderBottomColor: true,
|
|
91
|
+
color: true
|
|
92
|
+
};
|
|
93
|
+
const toReanimatedColor = value => {
|
|
94
|
+
"worklet";
|
|
95
|
+
|
|
96
|
+
if (typeof value === "number") {
|
|
97
|
+
const color = value >>> 0;
|
|
98
|
+
const alpha = (color >>> 24 & 255) / 255;
|
|
99
|
+
const red = color >>> 16 & 255;
|
|
100
|
+
const green = color >>> 8 & 255;
|
|
101
|
+
const blue = color & 255;
|
|
102
|
+
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
|
103
|
+
}
|
|
104
|
+
if (typeof value !== "string") return value;
|
|
105
|
+
const trimmed = value.trim();
|
|
106
|
+
if (trimmed === "transparent") return "rgba(0, 0, 0, 0)";
|
|
107
|
+
return trimmed;
|
|
108
|
+
};
|
|
109
|
+
const isReanimatedColorHistory = value => {
|
|
110
|
+
"worklet";
|
|
111
|
+
|
|
112
|
+
const normalized = toReanimatedColor(value);
|
|
113
|
+
return typeof normalized === "string" && normalized.startsWith("rgba(");
|
|
114
|
+
};
|
|
115
|
+
const normalizeAnimationColor = value => {
|
|
116
|
+
if (typeof value === "number") {
|
|
117
|
+
return toReanimatedColor(value);
|
|
118
|
+
}
|
|
119
|
+
if (typeof value !== "string") return;
|
|
120
|
+
const color = normalizeColor(value);
|
|
121
|
+
if (color == null) return;
|
|
122
|
+
const red = Math.round((color & 4278190080) >>> 24);
|
|
123
|
+
const green = Math.round((color & 16711680) >>> 16);
|
|
124
|
+
const blue = Math.round((color & 65280) >>> 8);
|
|
125
|
+
const alpha = (color & 255) / 255;
|
|
126
|
+
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
|
127
|
+
};
|
|
128
|
+
const buildSnapshot = (animated, statics, transforms, previousKeys, lastPainted) => {
|
|
129
|
+
const snapshotAnimated = {};
|
|
130
|
+
const snapshotStatics = cloneStyleRecord(statics);
|
|
131
|
+
for (const key in snapshotStatics) {
|
|
132
|
+
if (!COLOR_STYLE_KEYS[key]) continue;
|
|
133
|
+
const normalized = normalizeAnimationColor(snapshotStatics[key]);
|
|
134
|
+
if (normalized !== void 0) snapshotStatics[key] = normalized;
|
|
135
|
+
}
|
|
136
|
+
for (const key in animated) {
|
|
137
|
+
if (key === "transform") continue;
|
|
138
|
+
const value = animated[key];
|
|
139
|
+
if (COLOR_STYLE_KEYS[key]) {
|
|
140
|
+
const normalized = normalizeAnimationColor(value);
|
|
141
|
+
if (normalized === void 0) {
|
|
142
|
+
snapshotStatics[key] = cloneAnimationValue(value);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
snapshotAnimated[key] = normalized;
|
|
146
|
+
} else {
|
|
147
|
+
snapshotAnimated[key] = cloneAnimationValue(value);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const snapshotTransforms = transforms.map(cloneStyleRecord);
|
|
151
|
+
const keys = new Set(Object.keys(snapshotAnimated));
|
|
152
|
+
for (const transform of snapshotTransforms) {
|
|
153
|
+
const key = Object.keys(transform)[0];
|
|
154
|
+
const value = key ? transform[key] : void 0;
|
|
155
|
+
if (key && (typeof value === "number" || typeof value === "string")) {
|
|
156
|
+
keys.add(`transform:${key}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const seeds = {};
|
|
160
|
+
for (const key of keys) {
|
|
161
|
+
let value = lastPainted[key];
|
|
162
|
+
if (typeof value !== "number" && typeof value !== "string") continue;
|
|
163
|
+
if (value === "auto" || typeof value === "string" && value.startsWith("calc")) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (COLOR_STYLE_KEYS[key]) value = normalizeAnimationColor(value);
|
|
167
|
+
if (value !== void 0) seeds[key] = value;
|
|
168
|
+
}
|
|
169
|
+
const gatedKeys = {};
|
|
170
|
+
for (const key of keys) gatedKeys[key] = true;
|
|
171
|
+
const removedKeys = getRemovedAnimatedKeys(keys, previousKeys);
|
|
172
|
+
const painted = {
|
|
173
|
+
...snapshotStatics,
|
|
174
|
+
...snapshotAnimated
|
|
175
|
+
};
|
|
176
|
+
const staticTransforms = getAnimatedTransforms(snapshotStatics.transform);
|
|
177
|
+
delete painted.transform;
|
|
178
|
+
for (const transform of staticTransforms) {
|
|
179
|
+
const key = Object.keys(transform)[0];
|
|
180
|
+
if (key) painted[`transform:${key}`] = transform[key];
|
|
181
|
+
}
|
|
182
|
+
for (const transform of snapshotTransforms) {
|
|
183
|
+
const key = Object.keys(transform)[0];
|
|
184
|
+
if (key) painted[`transform:${key}`] = transform[key];
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
value: {
|
|
188
|
+
animated: snapshotAnimated,
|
|
189
|
+
statics: snapshotStatics,
|
|
190
|
+
transforms: snapshotTransforms,
|
|
191
|
+
gatedKeys,
|
|
192
|
+
seeds,
|
|
193
|
+
removedKeys,
|
|
194
|
+
removeTransform: Object.keys(removedKeys).some(key => key.startsWith("transform:")),
|
|
195
|
+
clearValue: isWeb ? "" : null
|
|
196
|
+
},
|
|
197
|
+
painted,
|
|
198
|
+
keys
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
const getGatedKeys = snapshot => Object.keys(snapshot.gatedKeys);
|
|
48
202
|
const createReanimatedConfig = config => {
|
|
49
203
|
"worklet";
|
|
50
204
|
|
|
@@ -59,7 +213,7 @@ const createReanimatedConfig = config => {
|
|
|
59
213
|
}
|
|
60
214
|
return next;
|
|
61
215
|
};
|
|
62
|
-
const applyAnimation = (targetValue, config, callback) => {
|
|
216
|
+
const applyAnimation = (targetValue, config, callback, seedValue, validateStartAsColor = false) => {
|
|
63
217
|
"worklet";
|
|
64
218
|
|
|
65
219
|
const delay = config.delay;
|
|
@@ -70,11 +224,43 @@ const applyAnimation = (targetValue, config, callback) => {
|
|
|
70
224
|
} else {
|
|
71
225
|
animatedValue = withSpring(targetValue, reanimatedConfig, callback);
|
|
72
226
|
}
|
|
227
|
+
if (seedValue !== void 0 || validateStartAsColor) {
|
|
228
|
+
const innerOnStart = animatedValue.onStart;
|
|
229
|
+
animatedValue.onStart = (animation, value, timestamp, previousAnimation) => {
|
|
230
|
+
"worklet";
|
|
231
|
+
|
|
232
|
+
const startValue = validateStartAsColor ? isReanimatedColorHistory(value) ? toReanimatedColor(value) : seedValue ?? targetValue : seedValue;
|
|
233
|
+
innerOnStart(animation, startValue, timestamp, previousAnimation);
|
|
234
|
+
};
|
|
235
|
+
}
|
|
73
236
|
if (delay && delay > 0) {
|
|
74
237
|
animatedValue = withDelay(delay, animatedValue);
|
|
75
238
|
}
|
|
76
239
|
return animatedValue;
|
|
77
240
|
};
|
|
241
|
+
const animateSnapshotValue = (key, implicitKey, targetValue, config, previouslyEmitted, seedValue, gated, currentlyExiting, exitCycleId, currentlyCompletingAnimation, didAnimateCycleId, markExitKeyDone, markDidAnimateKeyDone, validateStartAsColor = false) => {
|
|
242
|
+
"worklet";
|
|
243
|
+
|
|
244
|
+
const cycleGated = gated && (currentlyExiting || currentlyCompletingAnimation);
|
|
245
|
+
if (!previouslyEmitted && seedValue === void 0 && !cycleGated) {
|
|
246
|
+
return targetValue;
|
|
247
|
+
}
|
|
248
|
+
let callback;
|
|
249
|
+
if (gated && currentlyExiting) {
|
|
250
|
+
callback = finished => {
|
|
251
|
+
"worklet";
|
|
252
|
+
|
|
253
|
+
runOnJS(markExitKeyDone)(key, exitCycleId, finished ?? false);
|
|
254
|
+
};
|
|
255
|
+
} else if (gated && currentlyCompletingAnimation) {
|
|
256
|
+
callback = finished => {
|
|
257
|
+
"worklet";
|
|
258
|
+
|
|
259
|
+
runOnJS(markDidAnimateKeyDone)(key, didAnimateCycleId, finished ?? false);
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return applyAnimation(targetValue, config, callback, previouslyEmitted ? void 0 : seedValue ?? getImplicitDefault(implicitKey, targetValue), validateStartAsColor);
|
|
263
|
+
};
|
|
78
264
|
const ANIMATABLE_PROPERTIES = {
|
|
79
265
|
// Transform
|
|
80
266
|
transform: true,
|
|
@@ -149,6 +335,23 @@ const canAnimateProperty = (key, value, animateOnly) => {
|
|
|
149
335
|
if (animateOnly && !animateOnly.includes(key)) return false;
|
|
150
336
|
return true;
|
|
151
337
|
};
|
|
338
|
+
const splitAnimationStyles = (style, isDark, disableAnimation, animateOnly) => {
|
|
339
|
+
const animated = {};
|
|
340
|
+
const statics = {};
|
|
341
|
+
for (const key in style) {
|
|
342
|
+
const value = resolveDynamicValue(style[key], isDark);
|
|
343
|
+
if (value === void 0) continue;
|
|
344
|
+
if (!disableAnimation && canAnimateProperty(key, value, animateOnly)) {
|
|
345
|
+
animated[key] = cloneAnimationValue(value);
|
|
346
|
+
} else {
|
|
347
|
+
statics[key] = cloneAnimationValue(value);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
animated,
|
|
352
|
+
statics
|
|
353
|
+
};
|
|
354
|
+
};
|
|
152
355
|
function createWebAnimatedComponent(defaultTag) {
|
|
153
356
|
const isText = defaultTag === "span";
|
|
154
357
|
const Component = Animated.createAnimatedComponent(forwardRef((propsIn, ref) => {
|
|
@@ -369,7 +572,8 @@ function createAnimations(animationsConfig) {
|
|
|
369
572
|
useStyleEmitter,
|
|
370
573
|
themeName,
|
|
371
574
|
stateRef,
|
|
372
|
-
styleState
|
|
575
|
+
styleState,
|
|
576
|
+
onDidAnimate
|
|
373
577
|
} = animationProps;
|
|
374
578
|
const isHydrating = componentState.unmounted === true;
|
|
375
579
|
const isMounting = componentState.unmounted === "should-enter";
|
|
@@ -387,6 +591,25 @@ function createAnimations(animationsConfig) {
|
|
|
387
591
|
const disableAnimation = isHydrating || !animationKey;
|
|
388
592
|
const isDark = themeName?.startsWith("dark") || false;
|
|
389
593
|
const sendExitComplete = presence?.[1];
|
|
594
|
+
const onDidAnimateRef = useRef(onDidAnimate);
|
|
595
|
+
useIsomorphicLayoutEffect(() => {
|
|
596
|
+
onDidAnimateRef.current = onDidAnimate;
|
|
597
|
+
}, [onDidAnimate]);
|
|
598
|
+
const didAnimateCycleIdRef = useRef(0);
|
|
599
|
+
const pendingDidAnimateKeysRef = useRef(/* @__PURE__ */new Set());
|
|
600
|
+
const didAnimateCompletedRef = useRef(false);
|
|
601
|
+
const isCompletingAnimationRef = useSharedValue(false);
|
|
602
|
+
const didAnimateCycleIdShared = useSharedValue(0);
|
|
603
|
+
const markDidAnimateKeyDone = useEvent((key, cycleId, finished) => {
|
|
604
|
+
if (!finished || cycleId !== didAnimateCycleIdRef.current) return;
|
|
605
|
+
if (didAnimateCompletedRef.current) return;
|
|
606
|
+
pendingDidAnimateKeysRef.current.delete(key);
|
|
607
|
+
if (pendingDidAnimateKeysRef.current.size === 0) {
|
|
608
|
+
didAnimateCompletedRef.current = true;
|
|
609
|
+
isCompletingAnimationRef.value = false;
|
|
610
|
+
onDidAnimateRef.current?.();
|
|
611
|
+
}
|
|
612
|
+
});
|
|
390
613
|
const exitCycleIdRef = useRef(0);
|
|
391
614
|
const pendingExitKeysRef = useRef(/* @__PURE__ */new Set());
|
|
392
615
|
const exitCompletedRef = useRef(false);
|
|
@@ -408,6 +631,9 @@ function createAnimations(animationsConfig) {
|
|
|
408
631
|
exitCycleIdRef.current++;
|
|
409
632
|
exitCompletedRef.current = false;
|
|
410
633
|
pendingExitKeysRef.current.clear();
|
|
634
|
+
didAnimateCycleIdRef.current++;
|
|
635
|
+
pendingDidAnimateKeysRef.current.clear();
|
|
636
|
+
didAnimateCompletedRef.current = true;
|
|
411
637
|
}
|
|
412
638
|
if (justStoppedExiting) {
|
|
413
639
|
exitCycleIdRef.current++;
|
|
@@ -416,52 +642,83 @@ function createAnimations(animationsConfig) {
|
|
|
416
642
|
useIsomorphicLayoutEffect(() => {
|
|
417
643
|
isExitingRef.value = isExiting;
|
|
418
644
|
exitCycleIdShared.value = exitCycleIdRef.current;
|
|
419
|
-
|
|
645
|
+
if (justStartedExiting) {
|
|
646
|
+
isCompletingAnimationRef.value = false;
|
|
647
|
+
didAnimateCycleIdShared.value = didAnimateCycleIdRef.current;
|
|
648
|
+
}
|
|
649
|
+
}, [isExiting, exitCycleIdRef.current, justStartedExiting]);
|
|
420
650
|
React.useEffect(() => {
|
|
421
651
|
wasExitingRef.current = isExiting;
|
|
422
652
|
});
|
|
423
|
-
const
|
|
424
|
-
const
|
|
425
|
-
const
|
|
653
|
+
const committedRenderKeysRef = useRef(/* @__PURE__ */new Set());
|
|
654
|
+
const lastPaintedRef = useRef({});
|
|
655
|
+
const carryRef = useRef({});
|
|
426
656
|
const {
|
|
427
657
|
animatedStyles,
|
|
428
|
-
staticStyles
|
|
658
|
+
staticStyles,
|
|
659
|
+
nextCarry
|
|
429
660
|
} = useMemo(() => {
|
|
430
|
-
const animated = {};
|
|
431
|
-
const staticStyles2 = {};
|
|
432
661
|
const animateOnly = props.animateOnly;
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
if (canAnimateProperty(key, value, animateOnly)) {
|
|
442
|
-
animated[key] = cloneAnimationValue(value);
|
|
443
|
-
} else {
|
|
444
|
-
staticStyles2[key] = cloneAnimationValue(value);
|
|
445
|
-
}
|
|
662
|
+
const {
|
|
663
|
+
animated,
|
|
664
|
+
statics
|
|
665
|
+
} = splitAnimationStyles(style, isDark, disableAnimation, animateOnly);
|
|
666
|
+
const nextCarry2 = cloneStyleRecord(carryRef.current);
|
|
667
|
+
for (const key in nextCarry2) {
|
|
668
|
+
if (!(key in animated)) delete nextCarry2[key];
|
|
446
669
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
670
|
+
for (const key in animated) {
|
|
671
|
+
if (!(key in nextCarry2)) {
|
|
672
|
+
nextCarry2[key] = cloneAnimationValue(animated[key]);
|
|
450
673
|
}
|
|
674
|
+
statics[key] = nextCarry2[key];
|
|
451
675
|
}
|
|
452
676
|
return {
|
|
453
677
|
animatedStyles: animated,
|
|
454
|
-
staticStyles:
|
|
678
|
+
staticStyles: statics,
|
|
679
|
+
nextCarry: nextCarry2
|
|
455
680
|
};
|
|
456
|
-
}, [disableAnimation, style, isDark,
|
|
681
|
+
}, [disableAnimation, style, isDark, props.animateOnly]);
|
|
682
|
+
const renderSnapshot = useMemo(() => buildSnapshot(animatedStyles, staticStyles, getAnimatedTransforms(animatedStyles.transform), committedRenderKeysRef.current, lastPaintedRef.current), [animatedStyles, staticStyles]);
|
|
683
|
+
const renderSnapshotRef = useSharedValue({
|
|
684
|
+
animated: {},
|
|
685
|
+
statics: {},
|
|
686
|
+
transforms: [],
|
|
687
|
+
gatedKeys: {},
|
|
688
|
+
seeds: {},
|
|
689
|
+
removedKeys: {},
|
|
690
|
+
removeTransform: false,
|
|
691
|
+
clearValue: isWeb ? "" : null
|
|
692
|
+
});
|
|
693
|
+
const emitterSnapshotRef = useSharedValue(null);
|
|
694
|
+
const emitterKeysRef = useRef(null);
|
|
695
|
+
useIsomorphicLayoutEffect(() => {
|
|
696
|
+
carryRef.current = nextCarry;
|
|
697
|
+
committedRenderKeysRef.current = renderSnapshot.keys;
|
|
698
|
+
lastPaintedRef.current = renderSnapshot.painted;
|
|
699
|
+
}, [nextCarry, renderSnapshot]);
|
|
457
700
|
const pseudoActiveRef = useRef(false);
|
|
458
701
|
const isExitingJSRef = useRef(false);
|
|
459
702
|
isExitingJSRef.current = isExiting;
|
|
703
|
+
const publishedSnapshotRef = useRef(null);
|
|
460
704
|
useIsomorphicLayoutEffect(() => {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
705
|
+
const droppingLatch = (isExiting || !pseudoActiveRef.current) && emitterSnapshotRef.value !== null;
|
|
706
|
+
const emitterKeys = droppingLatch ? emitterKeysRef.current : null;
|
|
707
|
+
if (droppingLatch || publishedSnapshotRef.current !== renderSnapshot.value) {
|
|
708
|
+
const removedKeys = emitterKeys ? {
|
|
709
|
+
...renderSnapshot.value.removedKeys,
|
|
710
|
+
...getRemovedAnimatedKeys(renderSnapshot.keys, emitterKeys)
|
|
711
|
+
} : renderSnapshot.value.removedKeys;
|
|
712
|
+
renderSnapshotRef.value = {
|
|
713
|
+
...renderSnapshot.value,
|
|
714
|
+
removedKeys,
|
|
715
|
+
removeTransform: Object.keys(removedKeys).some(key => key.startsWith("transform:"))
|
|
716
|
+
};
|
|
717
|
+
publishedSnapshotRef.current = renderSnapshot.value;
|
|
718
|
+
}
|
|
719
|
+
if (droppingLatch) {
|
|
720
|
+
emitterSnapshotRef.value = null;
|
|
721
|
+
emitterKeysRef.current = null;
|
|
465
722
|
}
|
|
466
723
|
});
|
|
467
724
|
const {
|
|
@@ -497,9 +754,6 @@ function createAnimations(animationsConfig) {
|
|
|
497
754
|
if (isExitingJSRef.current) return;
|
|
498
755
|
pseudoActiveRef.current = pseudoActive === true;
|
|
499
756
|
const animateOnly = props.animateOnly;
|
|
500
|
-
const animated = {};
|
|
501
|
-
const statics = {};
|
|
502
|
-
const transforms = [];
|
|
503
757
|
const transitionToUse = effectiveTransition2 ?? props.transition;
|
|
504
758
|
const {
|
|
505
759
|
baseConfig: newBase,
|
|
@@ -511,69 +765,52 @@ function createAnimations(animationsConfig) {
|
|
|
511
765
|
disableAnimation: configRef.value.disableAnimation,
|
|
512
766
|
isHydrating: configRef.value.isHydrating
|
|
513
767
|
};
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
for (const t of value) {
|
|
524
|
-
if (t && typeof t === "object") {
|
|
525
|
-
const tKey = Object.keys(t)[0];
|
|
526
|
-
const tVal = t[tKey];
|
|
527
|
-
if (typeof tVal === "number" || typeof tVal === "string") {
|
|
528
|
-
transforms.push(cloneAnimationValue(t));
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
continue;
|
|
533
|
-
}
|
|
534
|
-
if (canAnimateProperty(key, value, animateOnly)) {
|
|
535
|
-
animated[key] = cloneAnimationValue(value);
|
|
536
|
-
} else {
|
|
537
|
-
statics[key] = cloneAnimationValue(value);
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
animatedTargetsRef.value = animated;
|
|
541
|
-
staticTargetsRef.value = statics;
|
|
542
|
-
transformTargetsRef.value = transforms;
|
|
768
|
+
const previousKeys = emitterKeysRef.current ?? committedRenderKeysRef.current;
|
|
769
|
+
const {
|
|
770
|
+
animated,
|
|
771
|
+
statics
|
|
772
|
+
} = splitAnimationStyles(nextStyle, isDark, configRef.value.disableAnimation, animateOnly);
|
|
773
|
+
const snapshot = buildSnapshot(animated, statics, getAnimatedTransforms(animated.transform), previousKeys, lastPaintedRef.current);
|
|
774
|
+
emitterSnapshotRef.value = snapshot.value;
|
|
775
|
+
emitterKeysRef.current = snapshot.keys;
|
|
776
|
+
lastPaintedRef.current = snapshot.painted;
|
|
543
777
|
if (process.env.NODE_ENV === "development" && props.debug && props.debug !== "profile") {
|
|
544
778
|
console.info("[animations-reanimated] useStyleEmitter update", {
|
|
545
779
|
animated,
|
|
546
780
|
statics,
|
|
547
|
-
transforms
|
|
781
|
+
transforms: snapshot.value.transforms
|
|
548
782
|
});
|
|
549
783
|
}
|
|
550
784
|
});
|
|
551
785
|
const exitKeysRegistered = useRef(false);
|
|
552
786
|
if (justStartedExiting && sendExitComplete) {
|
|
553
|
-
const exitKeys =
|
|
554
|
-
const animateOnly = props.animateOnly;
|
|
555
|
-
for (const key in animatedStyles) {
|
|
556
|
-
if (key === "transform") continue;
|
|
557
|
-
if (canAnimateProperty(key, animatedStyles[key], animateOnly)) {
|
|
558
|
-
exitKeys.push(key);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
const transforms = animatedStyles.transform;
|
|
562
|
-
if (transforms && Array.isArray(transforms)) {
|
|
563
|
-
for (const t of transforms) {
|
|
564
|
-
if (!t) continue;
|
|
565
|
-
const tKey = Object.keys(t)[0];
|
|
566
|
-
if (tKey) {
|
|
567
|
-
if (animateOnly && !animateOnly.includes(tKey)) {
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
exitKeys.push(`transform:${tKey}`);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
787
|
+
const exitKeys = getGatedKeys(renderSnapshot.value);
|
|
574
788
|
pendingExitKeysRef.current = new Set(exitKeys);
|
|
575
789
|
exitKeysRegistered.current = exitKeys.length > 0;
|
|
576
790
|
}
|
|
791
|
+
const previousDidAnimateStyleRef = useRef(null);
|
|
792
|
+
const didAnimateStyle = onDidAnimate ? JSON.stringify(style) : null;
|
|
793
|
+
const previousDidAnimateStyle = previousDidAnimateStyleRef.current;
|
|
794
|
+
const shouldRegisterDidAnimate = didAnimateStyle !== null && previousDidAnimateStyle !== null && previousDidAnimateStyle !== didAnimateStyle && !isExiting && !isEntering && !justFinishedEntering;
|
|
795
|
+
const didAnimateKeys = shouldRegisterDidAnimate ? getGatedKeys(renderSnapshot.value) : [];
|
|
796
|
+
useIsomorphicLayoutEffect(() => {
|
|
797
|
+
if (didAnimateStyle === null) {
|
|
798
|
+
previousDidAnimateStyleRef.current = null;
|
|
799
|
+
isCompletingAnimationRef.value = false;
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
const previousStyleStillCurrent = previousDidAnimateStyleRef.current === previousDidAnimateStyle;
|
|
803
|
+
previousDidAnimateStyleRef.current = didAnimateStyle;
|
|
804
|
+
if (!shouldRegisterDidAnimate || !previousStyleStillCurrent) return;
|
|
805
|
+
const cycleId = ++didAnimateCycleIdRef.current;
|
|
806
|
+
pendingDidAnimateKeysRef.current = new Set(didAnimateKeys);
|
|
807
|
+
didAnimateCompletedRef.current = didAnimateKeys.length === 0;
|
|
808
|
+
isCompletingAnimationRef.value = didAnimateKeys.length > 0;
|
|
809
|
+
didAnimateCycleIdShared.value = cycleId;
|
|
810
|
+
if (didAnimateKeys.length === 0) {
|
|
811
|
+
onDidAnimateRef.current?.();
|
|
812
|
+
}
|
|
813
|
+
}, [didAnimateStyle, shouldRegisterDidAnimate]);
|
|
577
814
|
React.useEffect(() => {
|
|
578
815
|
if (!justStartedExiting || !sendExitComplete) return;
|
|
579
816
|
if (!exitKeysRegistered.current && pendingExitKeysRef.current.size === 0) {
|
|
@@ -583,76 +820,76 @@ function createAnimations(animationsConfig) {
|
|
|
583
820
|
}
|
|
584
821
|
}
|
|
585
822
|
}, [justStartedExiting, sendExitComplete]);
|
|
823
|
+
const mapperStateRef = useSharedValue({
|
|
824
|
+
emitted: {}
|
|
825
|
+
});
|
|
586
826
|
const animatedStyle = useAnimatedStyle(() => {
|
|
587
827
|
"worklet";
|
|
588
828
|
|
|
589
|
-
|
|
829
|
+
const mapperState = mapperStateRef.value;
|
|
830
|
+
const config = configRef.value;
|
|
831
|
+
if (config.disableAnimation || config.isHydrating) {
|
|
832
|
+
mapperState.emitted = {};
|
|
590
833
|
return {};
|
|
591
834
|
}
|
|
835
|
+
const previouslyEmitted = mapperState.emitted;
|
|
836
|
+
const emitted = {};
|
|
592
837
|
const result = {};
|
|
593
|
-
const
|
|
594
|
-
const
|
|
595
|
-
const
|
|
596
|
-
const
|
|
597
|
-
const hasEmitterUpdates = emitterAnimated !== null;
|
|
598
|
-
const animatedValues = hasEmitterUpdates ? emitterAnimated : animatedStyles;
|
|
599
|
-
const staticValues = hasEmitterUpdates ? emitterStatic : staticStyles;
|
|
838
|
+
const emitterSnapshot = emitterSnapshotRef.value;
|
|
839
|
+
const snapshot = emitterSnapshot ?? renderSnapshotRef.value;
|
|
840
|
+
const animatedValues = snapshot.animated;
|
|
841
|
+
const staticValues = snapshot.statics;
|
|
600
842
|
const currentlyExiting = isExitingRef.value;
|
|
601
843
|
const currentCycleId = exitCycleIdShared.value;
|
|
844
|
+
const currentlyCompletingAnimation = isCompletingAnimationRef.value;
|
|
845
|
+
const currentDidAnimateCycleId = didAnimateCycleIdShared.value;
|
|
602
846
|
for (const key in staticValues) {
|
|
603
847
|
result[key] = staticValues[key];
|
|
604
848
|
}
|
|
849
|
+
for (const key in snapshot.removedKeys) {
|
|
850
|
+
if (!key.startsWith("transform:") && !(key in staticValues)) {
|
|
851
|
+
result[key] = snapshot.clearValue;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
605
854
|
for (const key in animatedValues) {
|
|
606
855
|
if (key === "transform") continue;
|
|
607
856
|
const targetValue = animatedValues[key];
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
const capturedCycleId = currentCycleId;
|
|
613
|
-
callback = finished => {
|
|
614
|
-
"worklet";
|
|
615
|
-
|
|
616
|
-
runOnJS(markExitKeyDone)(capturedKey, capturedCycleId, finished ?? false);
|
|
617
|
-
};
|
|
857
|
+
emitted[key] = true;
|
|
858
|
+
if (typeof targetValue !== "number" && typeof targetValue !== "string") {
|
|
859
|
+
result[key] = targetValue;
|
|
860
|
+
continue;
|
|
618
861
|
}
|
|
619
|
-
result[key] =
|
|
862
|
+
result[key] = animateSnapshotValue(key, key, targetValue, config.propertyConfigs[key] ?? config.baseConfig, !!previouslyEmitted[key], snapshot.seeds[key], !!snapshot.gatedKeys[key], currentlyExiting, currentCycleId, currentlyCompletingAnimation, currentDidAnimateCycleId, markExitKeyDone, markDidAnimateKeyDone, !!COLOR_STYLE_KEYS[key]);
|
|
620
863
|
}
|
|
621
|
-
const transforms =
|
|
622
|
-
if (transforms
|
|
864
|
+
const transforms = snapshot.transforms;
|
|
865
|
+
if (transforms.length > 0 || snapshot.removeTransform) {
|
|
623
866
|
const validTransforms = [];
|
|
624
867
|
for (const t of transforms) {
|
|
625
868
|
if (!t) continue;
|
|
626
869
|
const keys = Object.keys(t);
|
|
627
870
|
if (keys.length === 0) continue;
|
|
628
|
-
const
|
|
871
|
+
const transformKey = keys[0];
|
|
872
|
+
const value = t[transformKey];
|
|
629
873
|
if (typeof value === "number" || typeof value === "string") {
|
|
630
|
-
const transformKey = Object.keys(t)[0];
|
|
631
|
-
const targetValue = t[transformKey];
|
|
632
874
|
const propConfig = config.propertyConfigs[transformKey] ?? config.baseConfig;
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
const capturedKey = `transform:${transformKey}`;
|
|
636
|
-
const capturedCycleId = currentCycleId;
|
|
637
|
-
callback = finished => {
|
|
638
|
-
"worklet";
|
|
639
|
-
|
|
640
|
-
runOnJS(markExitKeyDone)(capturedKey, capturedCycleId, finished ?? false);
|
|
641
|
-
};
|
|
642
|
-
}
|
|
875
|
+
const subKey = `transform:${transformKey}`;
|
|
876
|
+
emitted[subKey] = true;
|
|
643
877
|
validTransforms.push({
|
|
644
|
-
[transformKey]:
|
|
878
|
+
[transformKey]: animateSnapshotValue(subKey, transformKey, value, propConfig, !!previouslyEmitted[subKey], snapshot.seeds[subKey], !!snapshot.gatedKeys[subKey], currentlyExiting, currentCycleId, currentlyCompletingAnimation, currentDidAnimateCycleId, markExitKeyDone, markDidAnimateKeyDone)
|
|
645
879
|
});
|
|
880
|
+
} else {
|
|
881
|
+
validTransforms.push(t);
|
|
646
882
|
}
|
|
647
883
|
}
|
|
648
|
-
if (validTransforms.length > 0) {
|
|
884
|
+
if (validTransforms.length > 0 || !("transform" in staticValues)) {
|
|
649
885
|
result.transform = validTransforms;
|
|
650
886
|
}
|
|
651
887
|
}
|
|
888
|
+
mapperState.emitted = emitted;
|
|
652
889
|
return result;
|
|
653
|
-
}, isWeb ? [
|
|
654
|
-
// pass SharedValues so the mapper watches them on web (no babel plugin)
|
|
655
|
-
|
|
890
|
+
}, isWeb ? [
|
|
891
|
+
// pass SharedValues so the stable mapper watches them on web (no babel plugin)
|
|
892
|
+
renderSnapshotRef, emitterSnapshotRef, configRef, isExitingRef, exitCycleIdShared, markExitKeyDone, isCompletingAnimationRef, didAnimateCycleIdShared, markDidAnimateKeyDone] : void 0);
|
|
656
893
|
silenceAnimatedComponentDevCheck(animatedStyle);
|
|
657
894
|
if (process.env.NODE_ENV === "development" && props.debug && props.debug !== "profile") {
|
|
658
895
|
console.info("[animations-reanimated] useAnimations", {
|