@tamagui/animations-reanimated 2.4.6 → 2.5.1
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/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
|
@@ -39,6 +39,7 @@ module.exports = __toCommonJS(createAnimations_exports);
|
|
|
39
39
|
var import_animation_helpers = require("@tamagui/animation-helpers");
|
|
40
40
|
var import_core = require("@tamagui/core");
|
|
41
41
|
var import_use_presence = require("@tamagui/use-presence");
|
|
42
|
+
var import_normalize_colors = __toESM(require("@react-native/normalize-colors"), 1);
|
|
42
43
|
var import_react = __toESM(require("react"), 1);
|
|
43
44
|
var import_react_native_reanimated = __toESM(require("react-native-reanimated"), 1);
|
|
44
45
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -80,9 +81,162 @@ const cloneAnimationValue = value => {
|
|
|
80
81
|
}
|
|
81
82
|
return value;
|
|
82
83
|
};
|
|
84
|
+
const cloneStyleRecord = style => {
|
|
85
|
+
const next = {};
|
|
86
|
+
for (const key in style) {
|
|
87
|
+
next[key] = cloneAnimationValue(style[key]);
|
|
88
|
+
}
|
|
89
|
+
return next;
|
|
90
|
+
};
|
|
83
91
|
const cloneTransitionConfig = config => {
|
|
84
92
|
return cloneAnimationValue(config);
|
|
85
93
|
};
|
|
94
|
+
const getAnimatedTransforms = value => {
|
|
95
|
+
if (!Array.isArray(value)) return [];
|
|
96
|
+
return value.filter(item => item !== null && typeof item === "object" && !Array.isArray(item));
|
|
97
|
+
};
|
|
98
|
+
const getRemovedAnimatedKeys = (current, previous) => {
|
|
99
|
+
const removedKeys = {};
|
|
100
|
+
for (const key of previous) {
|
|
101
|
+
if (!current.has(key)) removedKeys[key] = true;
|
|
102
|
+
}
|
|
103
|
+
return removedKeys;
|
|
104
|
+
};
|
|
105
|
+
const getImplicitDefault = (key, targetValue) => {
|
|
106
|
+
"worklet";
|
|
107
|
+
|
|
108
|
+
const value = key === "opacity" || key === "scale" || key === "scaleX" || key === "scaleY" ? 1 : 0;
|
|
109
|
+
if (typeof targetValue !== "string") return value;
|
|
110
|
+
let suffixIndex = 0;
|
|
111
|
+
while (suffixIndex < targetValue.length) {
|
|
112
|
+
const character = targetValue[suffixIndex];
|
|
113
|
+
const code = character.charCodeAt(0);
|
|
114
|
+
if (code >= 48 && code <= 57 || character === "." || character === "-" || character === "+") {
|
|
115
|
+
suffixIndex++;
|
|
116
|
+
} else {
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return suffixIndex > 0 ? `${value}${targetValue.slice(suffixIndex)}` : value;
|
|
121
|
+
};
|
|
122
|
+
const COLOR_STYLE_KEYS = {
|
|
123
|
+
backgroundColor: true,
|
|
124
|
+
borderColor: true,
|
|
125
|
+
borderLeftColor: true,
|
|
126
|
+
borderRightColor: true,
|
|
127
|
+
borderTopColor: true,
|
|
128
|
+
borderBottomColor: true,
|
|
129
|
+
color: true
|
|
130
|
+
};
|
|
131
|
+
const toReanimatedColor = value => {
|
|
132
|
+
"worklet";
|
|
133
|
+
|
|
134
|
+
if (typeof value === "number") {
|
|
135
|
+
const color = value >>> 0;
|
|
136
|
+
const alpha = (color >>> 24 & 255) / 255;
|
|
137
|
+
const red = color >>> 16 & 255;
|
|
138
|
+
const green = color >>> 8 & 255;
|
|
139
|
+
const blue = color & 255;
|
|
140
|
+
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
|
141
|
+
}
|
|
142
|
+
if (typeof value !== "string") return value;
|
|
143
|
+
const trimmed = value.trim();
|
|
144
|
+
if (trimmed === "transparent") return "rgba(0, 0, 0, 0)";
|
|
145
|
+
return trimmed;
|
|
146
|
+
};
|
|
147
|
+
const isReanimatedColorHistory = value => {
|
|
148
|
+
"worklet";
|
|
149
|
+
|
|
150
|
+
const normalized = toReanimatedColor(value);
|
|
151
|
+
return typeof normalized === "string" && normalized.startsWith("rgba(");
|
|
152
|
+
};
|
|
153
|
+
const normalizeAnimationColor = value => {
|
|
154
|
+
if (typeof value === "number") {
|
|
155
|
+
return toReanimatedColor(value);
|
|
156
|
+
}
|
|
157
|
+
if (typeof value !== "string") return;
|
|
158
|
+
const color = (0, import_normalize_colors.default)(value);
|
|
159
|
+
if (color == null) return;
|
|
160
|
+
const red = Math.round((color & 4278190080) >>> 24);
|
|
161
|
+
const green = Math.round((color & 16711680) >>> 16);
|
|
162
|
+
const blue = Math.round((color & 65280) >>> 8);
|
|
163
|
+
const alpha = (color & 255) / 255;
|
|
164
|
+
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
|
165
|
+
};
|
|
166
|
+
const buildSnapshot = (animated, statics, transforms, previousKeys, lastPainted) => {
|
|
167
|
+
const snapshotAnimated = {};
|
|
168
|
+
const snapshotStatics = cloneStyleRecord(statics);
|
|
169
|
+
for (const key in snapshotStatics) {
|
|
170
|
+
if (!COLOR_STYLE_KEYS[key]) continue;
|
|
171
|
+
const normalized = normalizeAnimationColor(snapshotStatics[key]);
|
|
172
|
+
if (normalized !== void 0) snapshotStatics[key] = normalized;
|
|
173
|
+
}
|
|
174
|
+
for (const key in animated) {
|
|
175
|
+
if (key === "transform") continue;
|
|
176
|
+
const value = animated[key];
|
|
177
|
+
if (COLOR_STYLE_KEYS[key]) {
|
|
178
|
+
const normalized = normalizeAnimationColor(value);
|
|
179
|
+
if (normalized === void 0) {
|
|
180
|
+
snapshotStatics[key] = cloneAnimationValue(value);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
snapshotAnimated[key] = normalized;
|
|
184
|
+
} else {
|
|
185
|
+
snapshotAnimated[key] = cloneAnimationValue(value);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const snapshotTransforms = transforms.map(cloneStyleRecord);
|
|
189
|
+
const keys = new Set(Object.keys(snapshotAnimated));
|
|
190
|
+
for (const transform of snapshotTransforms) {
|
|
191
|
+
const key = Object.keys(transform)[0];
|
|
192
|
+
const value = key ? transform[key] : void 0;
|
|
193
|
+
if (key && (typeof value === "number" || typeof value === "string")) {
|
|
194
|
+
keys.add(`transform:${key}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const seeds = {};
|
|
198
|
+
for (const key of keys) {
|
|
199
|
+
let value = lastPainted[key];
|
|
200
|
+
if (typeof value !== "number" && typeof value !== "string") continue;
|
|
201
|
+
if (value === "auto" || typeof value === "string" && value.startsWith("calc")) {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (COLOR_STYLE_KEYS[key]) value = normalizeAnimationColor(value);
|
|
205
|
+
if (value !== void 0) seeds[key] = value;
|
|
206
|
+
}
|
|
207
|
+
const gatedKeys = {};
|
|
208
|
+
for (const key of keys) gatedKeys[key] = true;
|
|
209
|
+
const removedKeys = getRemovedAnimatedKeys(keys, previousKeys);
|
|
210
|
+
const painted = {
|
|
211
|
+
...snapshotStatics,
|
|
212
|
+
...snapshotAnimated
|
|
213
|
+
};
|
|
214
|
+
const staticTransforms = getAnimatedTransforms(snapshotStatics.transform);
|
|
215
|
+
delete painted.transform;
|
|
216
|
+
for (const transform of staticTransforms) {
|
|
217
|
+
const key = Object.keys(transform)[0];
|
|
218
|
+
if (key) painted[`transform:${key}`] = transform[key];
|
|
219
|
+
}
|
|
220
|
+
for (const transform of snapshotTransforms) {
|
|
221
|
+
const key = Object.keys(transform)[0];
|
|
222
|
+
if (key) painted[`transform:${key}`] = transform[key];
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
value: {
|
|
226
|
+
animated: snapshotAnimated,
|
|
227
|
+
statics: snapshotStatics,
|
|
228
|
+
transforms: snapshotTransforms,
|
|
229
|
+
gatedKeys,
|
|
230
|
+
seeds,
|
|
231
|
+
removedKeys,
|
|
232
|
+
removeTransform: Object.keys(removedKeys).some(key => key.startsWith("transform:")),
|
|
233
|
+
clearValue: import_core.isWeb ? "" : null
|
|
234
|
+
},
|
|
235
|
+
painted,
|
|
236
|
+
keys
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
const getGatedKeys = snapshot => Object.keys(snapshot.gatedKeys);
|
|
86
240
|
const createReanimatedConfig = config => {
|
|
87
241
|
"worklet";
|
|
88
242
|
|
|
@@ -97,7 +251,7 @@ const createReanimatedConfig = config => {
|
|
|
97
251
|
}
|
|
98
252
|
return next;
|
|
99
253
|
};
|
|
100
|
-
const applyAnimation = (targetValue, config, callback) => {
|
|
254
|
+
const applyAnimation = (targetValue, config, callback, seedValue, validateStartAsColor = false) => {
|
|
101
255
|
"worklet";
|
|
102
256
|
|
|
103
257
|
const delay = config.delay;
|
|
@@ -108,11 +262,43 @@ const applyAnimation = (targetValue, config, callback) => {
|
|
|
108
262
|
} else {
|
|
109
263
|
animatedValue = (0, import_react_native_reanimated.withSpring)(targetValue, reanimatedConfig, callback);
|
|
110
264
|
}
|
|
265
|
+
if (seedValue !== void 0 || validateStartAsColor) {
|
|
266
|
+
const innerOnStart = animatedValue.onStart;
|
|
267
|
+
animatedValue.onStart = (animation, value, timestamp, previousAnimation) => {
|
|
268
|
+
"worklet";
|
|
269
|
+
|
|
270
|
+
const startValue = validateStartAsColor ? isReanimatedColorHistory(value) ? toReanimatedColor(value) : seedValue ?? targetValue : seedValue;
|
|
271
|
+
innerOnStart(animation, startValue, timestamp, previousAnimation);
|
|
272
|
+
};
|
|
273
|
+
}
|
|
111
274
|
if (delay && delay > 0) {
|
|
112
275
|
animatedValue = (0, import_react_native_reanimated.withDelay)(delay, animatedValue);
|
|
113
276
|
}
|
|
114
277
|
return animatedValue;
|
|
115
278
|
};
|
|
279
|
+
const animateSnapshotValue = (key, implicitKey, targetValue, config, previouslyEmitted, seedValue, gated, currentlyExiting, exitCycleId, currentlyCompletingAnimation, didAnimateCycleId, markExitKeyDone, markDidAnimateKeyDone, validateStartAsColor = false) => {
|
|
280
|
+
"worklet";
|
|
281
|
+
|
|
282
|
+
const cycleGated = gated && (currentlyExiting || currentlyCompletingAnimation);
|
|
283
|
+
if (!previouslyEmitted && seedValue === void 0 && !cycleGated) {
|
|
284
|
+
return targetValue;
|
|
285
|
+
}
|
|
286
|
+
let callback;
|
|
287
|
+
if (gated && currentlyExiting) {
|
|
288
|
+
callback = finished => {
|
|
289
|
+
"worklet";
|
|
290
|
+
|
|
291
|
+
(0, import_react_native_reanimated.runOnJS)(markExitKeyDone)(key, exitCycleId, finished ?? false);
|
|
292
|
+
};
|
|
293
|
+
} else if (gated && currentlyCompletingAnimation) {
|
|
294
|
+
callback = finished => {
|
|
295
|
+
"worklet";
|
|
296
|
+
|
|
297
|
+
(0, import_react_native_reanimated.runOnJS)(markDidAnimateKeyDone)(key, didAnimateCycleId, finished ?? false);
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return applyAnimation(targetValue, config, callback, previouslyEmitted ? void 0 : seedValue ?? getImplicitDefault(implicitKey, targetValue), validateStartAsColor);
|
|
301
|
+
};
|
|
116
302
|
const ANIMATABLE_PROPERTIES = {
|
|
117
303
|
// Transform
|
|
118
304
|
transform: true,
|
|
@@ -187,6 +373,23 @@ const canAnimateProperty = (key, value, animateOnly) => {
|
|
|
187
373
|
if (animateOnly && !animateOnly.includes(key)) return false;
|
|
188
374
|
return true;
|
|
189
375
|
};
|
|
376
|
+
const splitAnimationStyles = (style, isDark, disableAnimation, animateOnly) => {
|
|
377
|
+
const animated = {};
|
|
378
|
+
const statics = {};
|
|
379
|
+
for (const key in style) {
|
|
380
|
+
const value = resolveDynamicValue(style[key], isDark);
|
|
381
|
+
if (value === void 0) continue;
|
|
382
|
+
if (!disableAnimation && canAnimateProperty(key, value, animateOnly)) {
|
|
383
|
+
animated[key] = cloneAnimationValue(value);
|
|
384
|
+
} else {
|
|
385
|
+
statics[key] = cloneAnimationValue(value);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
animated,
|
|
390
|
+
statics
|
|
391
|
+
};
|
|
392
|
+
};
|
|
190
393
|
function createWebAnimatedComponent(defaultTag) {
|
|
191
394
|
const isText = defaultTag === "span";
|
|
192
395
|
const Component = Animated.createAnimatedComponent((0, import_react.forwardRef)((propsIn, ref) => {
|
|
@@ -407,7 +610,8 @@ function createAnimations(animationsConfig) {
|
|
|
407
610
|
useStyleEmitter,
|
|
408
611
|
themeName,
|
|
409
612
|
stateRef,
|
|
410
|
-
styleState
|
|
613
|
+
styleState,
|
|
614
|
+
onDidAnimate
|
|
411
615
|
} = animationProps;
|
|
412
616
|
const isHydrating = componentState.unmounted === true;
|
|
413
617
|
const isMounting = componentState.unmounted === "should-enter";
|
|
@@ -425,6 +629,25 @@ function createAnimations(animationsConfig) {
|
|
|
425
629
|
const disableAnimation = isHydrating || !animationKey;
|
|
426
630
|
const isDark = themeName?.startsWith("dark") || false;
|
|
427
631
|
const sendExitComplete = presence?.[1];
|
|
632
|
+
const onDidAnimateRef = (0, import_react.useRef)(onDidAnimate);
|
|
633
|
+
(0, import_core.useIsomorphicLayoutEffect)(() => {
|
|
634
|
+
onDidAnimateRef.current = onDidAnimate;
|
|
635
|
+
}, [onDidAnimate]);
|
|
636
|
+
const didAnimateCycleIdRef = (0, import_react.useRef)(0);
|
|
637
|
+
const pendingDidAnimateKeysRef = (0, import_react.useRef)(/* @__PURE__ */new Set());
|
|
638
|
+
const didAnimateCompletedRef = (0, import_react.useRef)(false);
|
|
639
|
+
const isCompletingAnimationRef = (0, import_react_native_reanimated.useSharedValue)(false);
|
|
640
|
+
const didAnimateCycleIdShared = (0, import_react_native_reanimated.useSharedValue)(0);
|
|
641
|
+
const markDidAnimateKeyDone = (0, import_core.useEvent)((key, cycleId, finished) => {
|
|
642
|
+
if (!finished || cycleId !== didAnimateCycleIdRef.current) return;
|
|
643
|
+
if (didAnimateCompletedRef.current) return;
|
|
644
|
+
pendingDidAnimateKeysRef.current.delete(key);
|
|
645
|
+
if (pendingDidAnimateKeysRef.current.size === 0) {
|
|
646
|
+
didAnimateCompletedRef.current = true;
|
|
647
|
+
isCompletingAnimationRef.value = false;
|
|
648
|
+
onDidAnimateRef.current?.();
|
|
649
|
+
}
|
|
650
|
+
});
|
|
428
651
|
const exitCycleIdRef = (0, import_react.useRef)(0);
|
|
429
652
|
const pendingExitKeysRef = (0, import_react.useRef)(/* @__PURE__ */new Set());
|
|
430
653
|
const exitCompletedRef = (0, import_react.useRef)(false);
|
|
@@ -446,6 +669,9 @@ function createAnimations(animationsConfig) {
|
|
|
446
669
|
exitCycleIdRef.current++;
|
|
447
670
|
exitCompletedRef.current = false;
|
|
448
671
|
pendingExitKeysRef.current.clear();
|
|
672
|
+
didAnimateCycleIdRef.current++;
|
|
673
|
+
pendingDidAnimateKeysRef.current.clear();
|
|
674
|
+
didAnimateCompletedRef.current = true;
|
|
449
675
|
}
|
|
450
676
|
if (justStoppedExiting) {
|
|
451
677
|
exitCycleIdRef.current++;
|
|
@@ -454,52 +680,83 @@ function createAnimations(animationsConfig) {
|
|
|
454
680
|
(0, import_core.useIsomorphicLayoutEffect)(() => {
|
|
455
681
|
isExitingRef.value = isExiting;
|
|
456
682
|
exitCycleIdShared.value = exitCycleIdRef.current;
|
|
457
|
-
|
|
683
|
+
if (justStartedExiting) {
|
|
684
|
+
isCompletingAnimationRef.value = false;
|
|
685
|
+
didAnimateCycleIdShared.value = didAnimateCycleIdRef.current;
|
|
686
|
+
}
|
|
687
|
+
}, [isExiting, exitCycleIdRef.current, justStartedExiting]);
|
|
458
688
|
import_react.default.useEffect(() => {
|
|
459
689
|
wasExitingRef.current = isExiting;
|
|
460
690
|
});
|
|
461
|
-
const
|
|
462
|
-
const
|
|
463
|
-
const
|
|
691
|
+
const committedRenderKeysRef = (0, import_react.useRef)(/* @__PURE__ */new Set());
|
|
692
|
+
const lastPaintedRef = (0, import_react.useRef)({});
|
|
693
|
+
const carryRef = (0, import_react.useRef)({});
|
|
464
694
|
const {
|
|
465
695
|
animatedStyles,
|
|
466
|
-
staticStyles
|
|
696
|
+
staticStyles,
|
|
697
|
+
nextCarry
|
|
467
698
|
} = (0, import_react.useMemo)(() => {
|
|
468
|
-
const animated = {};
|
|
469
|
-
const staticStyles2 = {};
|
|
470
699
|
const animateOnly = props.animateOnly;
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
if (canAnimateProperty(key, value, animateOnly)) {
|
|
480
|
-
animated[key] = cloneAnimationValue(value);
|
|
481
|
-
} else {
|
|
482
|
-
staticStyles2[key] = cloneAnimationValue(value);
|
|
483
|
-
}
|
|
700
|
+
const {
|
|
701
|
+
animated,
|
|
702
|
+
statics
|
|
703
|
+
} = splitAnimationStyles(style, isDark, disableAnimation, animateOnly);
|
|
704
|
+
const nextCarry2 = cloneStyleRecord(carryRef.current);
|
|
705
|
+
for (const key in nextCarry2) {
|
|
706
|
+
if (!(key in animated)) delete nextCarry2[key];
|
|
484
707
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
708
|
+
for (const key in animated) {
|
|
709
|
+
if (!(key in nextCarry2)) {
|
|
710
|
+
nextCarry2[key] = cloneAnimationValue(animated[key]);
|
|
488
711
|
}
|
|
712
|
+
statics[key] = nextCarry2[key];
|
|
489
713
|
}
|
|
490
714
|
return {
|
|
491
715
|
animatedStyles: animated,
|
|
492
|
-
staticStyles:
|
|
716
|
+
staticStyles: statics,
|
|
717
|
+
nextCarry: nextCarry2
|
|
493
718
|
};
|
|
494
|
-
}, [disableAnimation, style, isDark,
|
|
719
|
+
}, [disableAnimation, style, isDark, props.animateOnly]);
|
|
720
|
+
const renderSnapshot = (0, import_react.useMemo)(() => buildSnapshot(animatedStyles, staticStyles, getAnimatedTransforms(animatedStyles.transform), committedRenderKeysRef.current, lastPaintedRef.current), [animatedStyles, staticStyles]);
|
|
721
|
+
const renderSnapshotRef = (0, import_react_native_reanimated.useSharedValue)({
|
|
722
|
+
animated: {},
|
|
723
|
+
statics: {},
|
|
724
|
+
transforms: [],
|
|
725
|
+
gatedKeys: {},
|
|
726
|
+
seeds: {},
|
|
727
|
+
removedKeys: {},
|
|
728
|
+
removeTransform: false,
|
|
729
|
+
clearValue: import_core.isWeb ? "" : null
|
|
730
|
+
});
|
|
731
|
+
const emitterSnapshotRef = (0, import_react_native_reanimated.useSharedValue)(null);
|
|
732
|
+
const emitterKeysRef = (0, import_react.useRef)(null);
|
|
733
|
+
(0, import_core.useIsomorphicLayoutEffect)(() => {
|
|
734
|
+
carryRef.current = nextCarry;
|
|
735
|
+
committedRenderKeysRef.current = renderSnapshot.keys;
|
|
736
|
+
lastPaintedRef.current = renderSnapshot.painted;
|
|
737
|
+
}, [nextCarry, renderSnapshot]);
|
|
495
738
|
const pseudoActiveRef = (0, import_react.useRef)(false);
|
|
496
739
|
const isExitingJSRef = (0, import_react.useRef)(false);
|
|
497
740
|
isExitingJSRef.current = isExiting;
|
|
741
|
+
const publishedSnapshotRef = (0, import_react.useRef)(null);
|
|
498
742
|
(0, import_core.useIsomorphicLayoutEffect)(() => {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
743
|
+
const droppingLatch = (isExiting || !pseudoActiveRef.current) && emitterSnapshotRef.value !== null;
|
|
744
|
+
const emitterKeys = droppingLatch ? emitterKeysRef.current : null;
|
|
745
|
+
if (droppingLatch || publishedSnapshotRef.current !== renderSnapshot.value) {
|
|
746
|
+
const removedKeys = emitterKeys ? {
|
|
747
|
+
...renderSnapshot.value.removedKeys,
|
|
748
|
+
...getRemovedAnimatedKeys(renderSnapshot.keys, emitterKeys)
|
|
749
|
+
} : renderSnapshot.value.removedKeys;
|
|
750
|
+
renderSnapshotRef.value = {
|
|
751
|
+
...renderSnapshot.value,
|
|
752
|
+
removedKeys,
|
|
753
|
+
removeTransform: Object.keys(removedKeys).some(key => key.startsWith("transform:"))
|
|
754
|
+
};
|
|
755
|
+
publishedSnapshotRef.current = renderSnapshot.value;
|
|
756
|
+
}
|
|
757
|
+
if (droppingLatch) {
|
|
758
|
+
emitterSnapshotRef.value = null;
|
|
759
|
+
emitterKeysRef.current = null;
|
|
503
760
|
}
|
|
504
761
|
});
|
|
505
762
|
const {
|
|
@@ -535,9 +792,6 @@ function createAnimations(animationsConfig) {
|
|
|
535
792
|
if (isExitingJSRef.current) return;
|
|
536
793
|
pseudoActiveRef.current = pseudoActive === true;
|
|
537
794
|
const animateOnly = props.animateOnly;
|
|
538
|
-
const animated = {};
|
|
539
|
-
const statics = {};
|
|
540
|
-
const transforms = [];
|
|
541
795
|
const transitionToUse = effectiveTransition2 ?? props.transition;
|
|
542
796
|
const {
|
|
543
797
|
baseConfig: newBase,
|
|
@@ -549,69 +803,52 @@ function createAnimations(animationsConfig) {
|
|
|
549
803
|
disableAnimation: configRef.value.disableAnimation,
|
|
550
804
|
isHydrating: configRef.value.isHydrating
|
|
551
805
|
};
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
for (const t of value) {
|
|
562
|
-
if (t && typeof t === "object") {
|
|
563
|
-
const tKey = Object.keys(t)[0];
|
|
564
|
-
const tVal = t[tKey];
|
|
565
|
-
if (typeof tVal === "number" || typeof tVal === "string") {
|
|
566
|
-
transforms.push(cloneAnimationValue(t));
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
continue;
|
|
571
|
-
}
|
|
572
|
-
if (canAnimateProperty(key, value, animateOnly)) {
|
|
573
|
-
animated[key] = cloneAnimationValue(value);
|
|
574
|
-
} else {
|
|
575
|
-
statics[key] = cloneAnimationValue(value);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
animatedTargetsRef.value = animated;
|
|
579
|
-
staticTargetsRef.value = statics;
|
|
580
|
-
transformTargetsRef.value = transforms;
|
|
806
|
+
const previousKeys = emitterKeysRef.current ?? committedRenderKeysRef.current;
|
|
807
|
+
const {
|
|
808
|
+
animated,
|
|
809
|
+
statics
|
|
810
|
+
} = splitAnimationStyles(nextStyle, isDark, configRef.value.disableAnimation, animateOnly);
|
|
811
|
+
const snapshot = buildSnapshot(animated, statics, getAnimatedTransforms(animated.transform), previousKeys, lastPaintedRef.current);
|
|
812
|
+
emitterSnapshotRef.value = snapshot.value;
|
|
813
|
+
emitterKeysRef.current = snapshot.keys;
|
|
814
|
+
lastPaintedRef.current = snapshot.painted;
|
|
581
815
|
if (process.env.NODE_ENV === "development" && props.debug && props.debug !== "profile") {
|
|
582
816
|
console.info("[animations-reanimated] useStyleEmitter update", {
|
|
583
817
|
animated,
|
|
584
818
|
statics,
|
|
585
|
-
transforms
|
|
819
|
+
transforms: snapshot.value.transforms
|
|
586
820
|
});
|
|
587
821
|
}
|
|
588
822
|
});
|
|
589
823
|
const exitKeysRegistered = (0, import_react.useRef)(false);
|
|
590
824
|
if (justStartedExiting && sendExitComplete) {
|
|
591
|
-
const exitKeys =
|
|
592
|
-
const animateOnly = props.animateOnly;
|
|
593
|
-
for (const key in animatedStyles) {
|
|
594
|
-
if (key === "transform") continue;
|
|
595
|
-
if (canAnimateProperty(key, animatedStyles[key], animateOnly)) {
|
|
596
|
-
exitKeys.push(key);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
const transforms = animatedStyles.transform;
|
|
600
|
-
if (transforms && Array.isArray(transforms)) {
|
|
601
|
-
for (const t of transforms) {
|
|
602
|
-
if (!t) continue;
|
|
603
|
-
const tKey = Object.keys(t)[0];
|
|
604
|
-
if (tKey) {
|
|
605
|
-
if (animateOnly && !animateOnly.includes(tKey)) {
|
|
606
|
-
continue;
|
|
607
|
-
}
|
|
608
|
-
exitKeys.push(`transform:${tKey}`);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
}
|
|
825
|
+
const exitKeys = getGatedKeys(renderSnapshot.value);
|
|
612
826
|
pendingExitKeysRef.current = new Set(exitKeys);
|
|
613
827
|
exitKeysRegistered.current = exitKeys.length > 0;
|
|
614
828
|
}
|
|
829
|
+
const previousDidAnimateStyleRef = (0, import_react.useRef)(null);
|
|
830
|
+
const didAnimateStyle = onDidAnimate ? JSON.stringify(style) : null;
|
|
831
|
+
const previousDidAnimateStyle = previousDidAnimateStyleRef.current;
|
|
832
|
+
const shouldRegisterDidAnimate = didAnimateStyle !== null && previousDidAnimateStyle !== null && previousDidAnimateStyle !== didAnimateStyle && !isExiting && !isEntering && !justFinishedEntering;
|
|
833
|
+
const didAnimateKeys = shouldRegisterDidAnimate ? getGatedKeys(renderSnapshot.value) : [];
|
|
834
|
+
(0, import_core.useIsomorphicLayoutEffect)(() => {
|
|
835
|
+
if (didAnimateStyle === null) {
|
|
836
|
+
previousDidAnimateStyleRef.current = null;
|
|
837
|
+
isCompletingAnimationRef.value = false;
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
const previousStyleStillCurrent = previousDidAnimateStyleRef.current === previousDidAnimateStyle;
|
|
841
|
+
previousDidAnimateStyleRef.current = didAnimateStyle;
|
|
842
|
+
if (!shouldRegisterDidAnimate || !previousStyleStillCurrent) return;
|
|
843
|
+
const cycleId = ++didAnimateCycleIdRef.current;
|
|
844
|
+
pendingDidAnimateKeysRef.current = new Set(didAnimateKeys);
|
|
845
|
+
didAnimateCompletedRef.current = didAnimateKeys.length === 0;
|
|
846
|
+
isCompletingAnimationRef.value = didAnimateKeys.length > 0;
|
|
847
|
+
didAnimateCycleIdShared.value = cycleId;
|
|
848
|
+
if (didAnimateKeys.length === 0) {
|
|
849
|
+
onDidAnimateRef.current?.();
|
|
850
|
+
}
|
|
851
|
+
}, [didAnimateStyle, shouldRegisterDidAnimate]);
|
|
615
852
|
import_react.default.useEffect(() => {
|
|
616
853
|
if (!justStartedExiting || !sendExitComplete) return;
|
|
617
854
|
if (!exitKeysRegistered.current && pendingExitKeysRef.current.size === 0) {
|
|
@@ -621,76 +858,76 @@ function createAnimations(animationsConfig) {
|
|
|
621
858
|
}
|
|
622
859
|
}
|
|
623
860
|
}, [justStartedExiting, sendExitComplete]);
|
|
861
|
+
const mapperStateRef = (0, import_react_native_reanimated.useSharedValue)({
|
|
862
|
+
emitted: {}
|
|
863
|
+
});
|
|
624
864
|
const animatedStyle = (0, import_react_native_reanimated.useAnimatedStyle)(() => {
|
|
625
865
|
"worklet";
|
|
626
866
|
|
|
627
|
-
|
|
867
|
+
const mapperState = mapperStateRef.value;
|
|
868
|
+
const config = configRef.value;
|
|
869
|
+
if (config.disableAnimation || config.isHydrating) {
|
|
870
|
+
mapperState.emitted = {};
|
|
628
871
|
return {};
|
|
629
872
|
}
|
|
873
|
+
const previouslyEmitted = mapperState.emitted;
|
|
874
|
+
const emitted = {};
|
|
630
875
|
const result = {};
|
|
631
|
-
const
|
|
632
|
-
const
|
|
633
|
-
const
|
|
634
|
-
const
|
|
635
|
-
const hasEmitterUpdates = emitterAnimated !== null;
|
|
636
|
-
const animatedValues = hasEmitterUpdates ? emitterAnimated : animatedStyles;
|
|
637
|
-
const staticValues = hasEmitterUpdates ? emitterStatic : staticStyles;
|
|
876
|
+
const emitterSnapshot = emitterSnapshotRef.value;
|
|
877
|
+
const snapshot = emitterSnapshot ?? renderSnapshotRef.value;
|
|
878
|
+
const animatedValues = snapshot.animated;
|
|
879
|
+
const staticValues = snapshot.statics;
|
|
638
880
|
const currentlyExiting = isExitingRef.value;
|
|
639
881
|
const currentCycleId = exitCycleIdShared.value;
|
|
882
|
+
const currentlyCompletingAnimation = isCompletingAnimationRef.value;
|
|
883
|
+
const currentDidAnimateCycleId = didAnimateCycleIdShared.value;
|
|
640
884
|
for (const key in staticValues) {
|
|
641
885
|
result[key] = staticValues[key];
|
|
642
886
|
}
|
|
887
|
+
for (const key in snapshot.removedKeys) {
|
|
888
|
+
if (!key.startsWith("transform:") && !(key in staticValues)) {
|
|
889
|
+
result[key] = snapshot.clearValue;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
643
892
|
for (const key in animatedValues) {
|
|
644
893
|
if (key === "transform") continue;
|
|
645
894
|
const targetValue = animatedValues[key];
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
const capturedCycleId = currentCycleId;
|
|
651
|
-
callback = finished => {
|
|
652
|
-
"worklet";
|
|
653
|
-
|
|
654
|
-
(0, import_react_native_reanimated.runOnJS)(markExitKeyDone)(capturedKey, capturedCycleId, finished ?? false);
|
|
655
|
-
};
|
|
895
|
+
emitted[key] = true;
|
|
896
|
+
if (typeof targetValue !== "number" && typeof targetValue !== "string") {
|
|
897
|
+
result[key] = targetValue;
|
|
898
|
+
continue;
|
|
656
899
|
}
|
|
657
|
-
result[key] =
|
|
900
|
+
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]);
|
|
658
901
|
}
|
|
659
|
-
const transforms =
|
|
660
|
-
if (transforms
|
|
902
|
+
const transforms = snapshot.transforms;
|
|
903
|
+
if (transforms.length > 0 || snapshot.removeTransform) {
|
|
661
904
|
const validTransforms = [];
|
|
662
905
|
for (const t of transforms) {
|
|
663
906
|
if (!t) continue;
|
|
664
907
|
const keys = Object.keys(t);
|
|
665
908
|
if (keys.length === 0) continue;
|
|
666
|
-
const
|
|
909
|
+
const transformKey = keys[0];
|
|
910
|
+
const value = t[transformKey];
|
|
667
911
|
if (typeof value === "number" || typeof value === "string") {
|
|
668
|
-
const transformKey = Object.keys(t)[0];
|
|
669
|
-
const targetValue = t[transformKey];
|
|
670
912
|
const propConfig = config.propertyConfigs[transformKey] ?? config.baseConfig;
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
const capturedKey = `transform:${transformKey}`;
|
|
674
|
-
const capturedCycleId = currentCycleId;
|
|
675
|
-
callback = finished => {
|
|
676
|
-
"worklet";
|
|
677
|
-
|
|
678
|
-
(0, import_react_native_reanimated.runOnJS)(markExitKeyDone)(capturedKey, capturedCycleId, finished ?? false);
|
|
679
|
-
};
|
|
680
|
-
}
|
|
913
|
+
const subKey = `transform:${transformKey}`;
|
|
914
|
+
emitted[subKey] = true;
|
|
681
915
|
validTransforms.push({
|
|
682
|
-
[transformKey]:
|
|
916
|
+
[transformKey]: animateSnapshotValue(subKey, transformKey, value, propConfig, !!previouslyEmitted[subKey], snapshot.seeds[subKey], !!snapshot.gatedKeys[subKey], currentlyExiting, currentCycleId, currentlyCompletingAnimation, currentDidAnimateCycleId, markExitKeyDone, markDidAnimateKeyDone)
|
|
683
917
|
});
|
|
918
|
+
} else {
|
|
919
|
+
validTransforms.push(t);
|
|
684
920
|
}
|
|
685
921
|
}
|
|
686
|
-
if (validTransforms.length > 0) {
|
|
922
|
+
if (validTransforms.length > 0 || !("transform" in staticValues)) {
|
|
687
923
|
result.transform = validTransforms;
|
|
688
924
|
}
|
|
689
925
|
}
|
|
926
|
+
mapperState.emitted = emitted;
|
|
690
927
|
return result;
|
|
691
|
-
}, import_core.isWeb ? [
|
|
692
|
-
// pass SharedValues so the mapper watches them on web (no babel plugin)
|
|
693
|
-
|
|
928
|
+
}, import_core.isWeb ? [
|
|
929
|
+
// pass SharedValues so the stable mapper watches them on web (no babel plugin)
|
|
930
|
+
renderSnapshotRef, emitterSnapshotRef, configRef, isExitingRef, exitCycleIdShared, markExitKeyDone, isCompletingAnimationRef, didAnimateCycleIdShared, markDidAnimateKeyDone] : void 0);
|
|
694
931
|
silenceAnimatedComponentDevCheck(animatedStyle);
|
|
695
932
|
if (process.env.NODE_ENV === "development" && props.debug && props.debug !== "profile") {
|
|
696
933
|
console.info("[animations-reanimated] useAnimations", {
|