@hanzogui/animations-reanimated 3.0.5 → 4.3.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 +484 -334
- package/dist/cjs/createAnimations.native.js +568 -409
- package/dist/cjs/createAnimations.native.js.map +1 -1
- package/dist/cjs/index.cjs +7 -5
- package/dist/cjs/index.native.js +7 -5
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/esm/createAnimations.mjs +456 -308
- package/dist/esm/createAnimations.mjs.map +1 -1
- package/dist/esm/createAnimations.native.js +540 -383
- package/dist/esm/createAnimations.native.js.map +1 -1
- package/package.json +5 -5
- package/src/createAnimations.tsx +172 -124
- package/types/createAnimations.d.ts.map +2 -2
|
@@ -1,158 +1,189 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getEffectiveAnimation, normalizeTransition } from "@hanzogui/animation-helpers";
|
|
2
2
|
import { getSplitStyles, hooks, isWeb, Text, useComposedRefs, useEvent, useIsomorphicLayoutEffect, useThemeWithState, View } from "@hanzogui/core";
|
|
3
3
|
import { ResetPresence, usePresence } from "@hanzogui/use-presence";
|
|
4
4
|
import React, { forwardRef, useMemo, useRef } from "react";
|
|
5
|
-
import Animated_, { cancelAnimation, runOnJS, useAnimatedReaction, useAnimatedStyle,
|
|
5
|
+
import Animated_, { cancelAnimation, runOnJS, runOnUI, useAnimatedReaction, useAnimatedStyle, useSharedValue, withDelay, withSpring, withTiming } from "react-native-reanimated";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
const getDefaultExport = module => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
8
|
+
const mod = module;
|
|
9
|
+
if (mod.__esModule || mod[Symbol.toStringTag] === "Module") {
|
|
10
|
+
return mod.default || mod;
|
|
11
|
+
}
|
|
12
|
+
return mod;
|
|
13
|
+
};
|
|
14
|
+
const Animated = getDefaultExport(Animated_);
|
|
15
|
+
const resolveDynamicValue = (value, isDark) => {
|
|
16
|
+
if (value !== null && typeof value === "object" && "dynamic" in value && typeof value.dynamic === "object") {
|
|
17
|
+
const dynamic = value.dynamic;
|
|
18
|
+
return isDark ? dynamic.dark : dynamic.light;
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
};
|
|
22
|
+
const applyAnimation = (targetValue, config, callback) => {
|
|
23
|
+
"worklet";
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
25
|
+
const delay = config.delay;
|
|
26
|
+
let animatedValue;
|
|
27
|
+
if (config.type === "timing") {
|
|
28
|
+
animatedValue = withTiming(targetValue, config, callback);
|
|
29
|
+
} else {
|
|
30
|
+
animatedValue = withSpring(targetValue, config, callback);
|
|
31
|
+
}
|
|
32
|
+
if (delay && delay > 0) {
|
|
33
|
+
animatedValue = withDelay(delay, animatedValue);
|
|
34
|
+
}
|
|
35
|
+
return animatedValue;
|
|
36
|
+
};
|
|
37
|
+
const ANIMATABLE_PROPERTIES = {
|
|
38
|
+
// Transform
|
|
39
|
+
transform: true,
|
|
40
|
+
// Opacity
|
|
41
|
+
opacity: true,
|
|
42
|
+
// Dimensions
|
|
43
|
+
height: true,
|
|
44
|
+
width: true,
|
|
45
|
+
minWidth: true,
|
|
46
|
+
minHeight: true,
|
|
47
|
+
maxWidth: true,
|
|
48
|
+
maxHeight: true,
|
|
49
|
+
// Background
|
|
50
|
+
backgroundColor: true,
|
|
51
|
+
// Border colors
|
|
52
|
+
borderColor: true,
|
|
53
|
+
borderLeftColor: true,
|
|
54
|
+
borderRightColor: true,
|
|
55
|
+
borderTopColor: true,
|
|
56
|
+
borderBottomColor: true,
|
|
57
|
+
// Border radius
|
|
58
|
+
borderRadius: true,
|
|
59
|
+
borderTopLeftRadius: true,
|
|
60
|
+
borderTopRightRadius: true,
|
|
61
|
+
borderBottomLeftRadius: true,
|
|
62
|
+
borderBottomRightRadius: true,
|
|
63
|
+
// Border width
|
|
64
|
+
borderWidth: true,
|
|
65
|
+
borderLeftWidth: true,
|
|
66
|
+
borderRightWidth: true,
|
|
67
|
+
borderTopWidth: true,
|
|
68
|
+
borderBottomWidth: true,
|
|
69
|
+
// Text
|
|
70
|
+
color: true,
|
|
71
|
+
fontSize: true,
|
|
72
|
+
fontWeight: true,
|
|
73
|
+
lineHeight: true,
|
|
74
|
+
letterSpacing: true,
|
|
75
|
+
// Position
|
|
76
|
+
left: true,
|
|
77
|
+
right: true,
|
|
78
|
+
top: true,
|
|
79
|
+
bottom: true,
|
|
80
|
+
// Margin
|
|
81
|
+
margin: true,
|
|
82
|
+
marginTop: true,
|
|
83
|
+
marginBottom: true,
|
|
84
|
+
marginLeft: true,
|
|
85
|
+
marginRight: true,
|
|
86
|
+
marginHorizontal: true,
|
|
87
|
+
marginVertical: true,
|
|
88
|
+
// Padding
|
|
89
|
+
padding: true,
|
|
90
|
+
paddingTop: true,
|
|
91
|
+
paddingBottom: true,
|
|
92
|
+
paddingLeft: true,
|
|
93
|
+
paddingRight: true,
|
|
94
|
+
paddingHorizontal: true,
|
|
95
|
+
paddingVertical: true,
|
|
96
|
+
// Flex/Gap
|
|
97
|
+
gap: true,
|
|
98
|
+
rowGap: true,
|
|
99
|
+
columnGap: true,
|
|
100
|
+
flex: true,
|
|
101
|
+
flexGrow: true,
|
|
102
|
+
flexShrink: true
|
|
103
|
+
};
|
|
104
|
+
const canAnimateProperty = (key, value, animateOnly) => {
|
|
105
|
+
if (!ANIMATABLE_PROPERTIES[key]) return false;
|
|
106
|
+
if (value === "auto") return false;
|
|
107
|
+
if (typeof value === "string" && value.startsWith("calc")) return false;
|
|
108
|
+
if (animateOnly && !animateOnly.includes(key)) return false;
|
|
109
|
+
return true;
|
|
110
|
+
};
|
|
94
111
|
function createWebAnimatedComponent(defaultTag) {
|
|
95
|
-
const isText = defaultTag === "span"
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
})
|
|
123
|
-
|
|
112
|
+
const isText = defaultTag === "span";
|
|
113
|
+
const Component = Animated.createAnimatedComponent(forwardRef((propsIn, ref) => {
|
|
114
|
+
const {
|
|
115
|
+
forwardedRef,
|
|
116
|
+
render = defaultTag,
|
|
117
|
+
...rest
|
|
118
|
+
} = propsIn;
|
|
119
|
+
const hostRef = useRef(null);
|
|
120
|
+
const composedRefs = useComposedRefs(forwardedRef, ref, hostRef);
|
|
121
|
+
const stateRef = useRef({
|
|
122
|
+
get host() {
|
|
123
|
+
return hostRef.current;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
const [, themeState] = useThemeWithState({});
|
|
127
|
+
const result = getSplitStyles(rest, isText ? Text.staticConfig : View.staticConfig, themeState?.theme ?? {}, themeState?.name ?? "", {
|
|
128
|
+
unmounted: false
|
|
129
|
+
}, {
|
|
130
|
+
isAnimated: false,
|
|
131
|
+
noClass: true
|
|
132
|
+
});
|
|
133
|
+
const viewProps = result?.viewProps ?? {};
|
|
134
|
+
const Element = render;
|
|
135
|
+
const transformedProps = hooks.usePropsTransform?.(render, viewProps, stateRef, false);
|
|
136
|
+
return /* @__PURE__ */jsx(Element, {
|
|
137
|
+
...transformedProps,
|
|
138
|
+
ref: composedRefs
|
|
139
|
+
});
|
|
140
|
+
}));
|
|
141
|
+
Component.acceptRenderProp = true;
|
|
142
|
+
return Component;
|
|
124
143
|
}
|
|
125
|
-
const AnimatedView = createWebAnimatedComponent("div")
|
|
126
|
-
|
|
144
|
+
const AnimatedView = createWebAnimatedComponent("div");
|
|
145
|
+
const AnimatedText = createWebAnimatedComponent("span");
|
|
127
146
|
function buildTransitionConfig(transition, animations, animationState, styleKeys) {
|
|
128
|
-
const normalized = normalizeTransition(transition)
|
|
129
|
-
|
|
147
|
+
const normalized = normalizeTransition(transition);
|
|
148
|
+
const effectiveKey = getEffectiveAnimation(normalized, animationState);
|
|
130
149
|
let base = effectiveKey ? animations[effectiveKey] ?? {
|
|
131
150
|
type: "spring"
|
|
132
151
|
} : {
|
|
133
152
|
type: "spring"
|
|
134
153
|
};
|
|
135
|
-
normalized.delay
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
154
|
+
if (normalized.delay) {
|
|
155
|
+
base = {
|
|
156
|
+
...base,
|
|
157
|
+
delay: normalized.delay
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (normalized.config) {
|
|
161
|
+
base = {
|
|
162
|
+
...base,
|
|
163
|
+
...normalized.config
|
|
164
|
+
};
|
|
165
|
+
if (base.type !== "timing" && normalized.config.duration !== void 0 && normalized.config.damping === void 0 && normalized.config.stiffness === void 0 && normalized.config.mass === void 0) {
|
|
166
|
+
base = {
|
|
167
|
+
...base,
|
|
168
|
+
type: "timing"
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
145
172
|
const propertyConfigs = {};
|
|
146
173
|
for (const key of styleKeys) {
|
|
147
174
|
const propAnimation = normalized.properties[key];
|
|
148
|
-
if (typeof propAnimation
|
|
149
|
-
|
|
150
|
-
|
|
175
|
+
if (typeof propAnimation === "string") {
|
|
176
|
+
propertyConfigs[key] = animations[propAnimation] ?? base;
|
|
177
|
+
} else if (propAnimation && typeof propAnimation === "object") {
|
|
178
|
+
const configType = propAnimation.type;
|
|
179
|
+
const baseForProp = configType ? animations[configType] ?? base : base;
|
|
151
180
|
propertyConfigs[key] = {
|
|
152
181
|
...baseForProp,
|
|
153
182
|
...propAnimation
|
|
154
183
|
};
|
|
155
|
-
} else
|
|
184
|
+
} else {
|
|
185
|
+
propertyConfigs[key] = base;
|
|
186
|
+
}
|
|
156
187
|
}
|
|
157
188
|
return {
|
|
158
189
|
baseConfig: base,
|
|
@@ -161,23 +192,31 @@ function buildTransitionConfig(transition, animations, animationState, styleKeys
|
|
|
161
192
|
}
|
|
162
193
|
function getStyleKeys(style) {
|
|
163
194
|
const keys = new Set(Object.keys(style));
|
|
164
|
-
if (style.transform && Array.isArray(style.transform))
|
|
195
|
+
if (style.transform && Array.isArray(style.transform)) {
|
|
196
|
+
for (const t of style.transform) {
|
|
197
|
+
if (t && typeof t === "object") {
|
|
198
|
+
keys.add(Object.keys(t)[0]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
165
202
|
return keys;
|
|
166
203
|
}
|
|
167
204
|
function createAnimations(animationsConfig) {
|
|
168
205
|
const animations = {};
|
|
169
|
-
for (const key in animationsConfig)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
206
|
+
for (const key in animationsConfig) {
|
|
207
|
+
animations[key] = {
|
|
208
|
+
type: "spring",
|
|
209
|
+
...animationsConfig[key]
|
|
210
|
+
};
|
|
211
|
+
}
|
|
173
212
|
return {
|
|
174
|
-
needsCustomComponent:
|
|
213
|
+
needsCustomComponent: true,
|
|
175
214
|
View: isWeb ? AnimatedView : Animated.View,
|
|
176
215
|
Text: isWeb ? AnimatedText : Animated.Text,
|
|
177
|
-
isReactNative:
|
|
216
|
+
isReactNative: true,
|
|
178
217
|
inputStyle: "value",
|
|
179
218
|
outputStyle: "inline",
|
|
180
|
-
avoidReRenders:
|
|
219
|
+
avoidReRenders: true,
|
|
181
220
|
animations,
|
|
182
221
|
usePresence,
|
|
183
222
|
ResetPresence,
|
|
@@ -200,18 +239,27 @@ function createAnimations(animationsConfig) {
|
|
|
200
239
|
setValue(next, config = {
|
|
201
240
|
type: "spring"
|
|
202
241
|
}, onFinish) {
|
|
203
|
-
"
|
|
242
|
+
if (config.type === "direct") {
|
|
243
|
+
sharedValue.value = next;
|
|
244
|
+
onFinish?.();
|
|
245
|
+
} else {
|
|
246
|
+
const cb = onFinish ? () => {
|
|
247
|
+
"worklet";
|
|
204
248
|
|
|
205
|
-
|
|
206
|
-
|
|
249
|
+
runOnJS(onFinish)();
|
|
250
|
+
} : void 0;
|
|
251
|
+
if (isWeb) {
|
|
252
|
+
sharedValue.value = config.type === "spring" ? withSpring(next, config, cb) : withTiming(next, config, cb);
|
|
253
|
+
} else {
|
|
254
|
+
runOnUI(() => {
|
|
255
|
+
"worklet";
|
|
207
256
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
257
|
+
sharedValue.value = config.type === "spring" ? withSpring(next, config, cb) : withTiming(next, config, cb);
|
|
258
|
+
})();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
211
261
|
},
|
|
212
262
|
stop() {
|
|
213
|
-
"worklet";
|
|
214
|
-
|
|
215
263
|
cancelAnimation(sharedValue);
|
|
216
264
|
}
|
|
217
265
|
}), [sharedValue]);
|
|
@@ -224,106 +272,168 @@ function createAnimations(animationsConfig) {
|
|
|
224
272
|
}, onValue) {
|
|
225
273
|
const instance = value.getInstance();
|
|
226
274
|
return useAnimatedReaction(() => instance.value, (next, prev) => {
|
|
227
|
-
prev !== next
|
|
275
|
+
if (prev !== next) {
|
|
276
|
+
runOnJS(onValue)(next);
|
|
277
|
+
}
|
|
228
278
|
}, [onValue, instance]);
|
|
229
279
|
},
|
|
230
280
|
// =========================================================================
|
|
231
281
|
// useAnimatedNumberStyle - Create animated styles from values
|
|
232
282
|
// =========================================================================
|
|
233
283
|
useAnimatedNumberStyle(val, getStyle) {
|
|
234
|
-
const instance = val.getInstance()
|
|
235
|
-
|
|
236
|
-
|
|
284
|
+
const instance = val.getInstance();
|
|
285
|
+
if (isWeb) {
|
|
286
|
+
return useAnimatedStyle(() => {
|
|
287
|
+
"worklet";
|
|
288
|
+
|
|
289
|
+
return getStyle(instance.value);
|
|
290
|
+
}, [instance, getStyle]);
|
|
291
|
+
}
|
|
292
|
+
const styleVal = useSharedValue(getStyle(instance.value));
|
|
293
|
+
useAnimatedReaction(() => {
|
|
294
|
+
"worklet";
|
|
295
|
+
|
|
296
|
+
return instance.value;
|
|
297
|
+
}, (current, previous) => {
|
|
298
|
+
"worklet";
|
|
299
|
+
|
|
300
|
+
if (current !== previous) {
|
|
301
|
+
styleVal.value = getStyle(current);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
return useAnimatedStyle(() => {
|
|
305
|
+
"worklet";
|
|
306
|
+
|
|
307
|
+
return styleVal.value;
|
|
308
|
+
});
|
|
309
|
+
},
|
|
310
|
+
useAnimatedNumbersStyle(vals, getStyle) {
|
|
311
|
+
const instances = vals.map(v => v.getInstance());
|
|
312
|
+
return useAnimatedStyle(() => {
|
|
313
|
+
"worklet";
|
|
314
|
+
|
|
315
|
+
const currentValues = instances.map(inst => inst.value);
|
|
316
|
+
return getStyle(...currentValues);
|
|
317
|
+
}, isWeb ? [getStyle, ...instances] : void 0);
|
|
237
318
|
},
|
|
238
319
|
// =========================================================================
|
|
239
320
|
// useAnimations - Main animation hook for components
|
|
240
321
|
// =========================================================================
|
|
241
322
|
useAnimations(animationProps) {
|
|
242
323
|
const {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
324
|
+
props,
|
|
325
|
+
presence,
|
|
326
|
+
style,
|
|
327
|
+
componentState,
|
|
328
|
+
useStyleEmitter,
|
|
329
|
+
themeName,
|
|
330
|
+
stateRef,
|
|
331
|
+
styleState
|
|
332
|
+
} = animationProps;
|
|
333
|
+
const isHydrating = componentState.unmounted === true;
|
|
334
|
+
const isMounting = componentState.unmounted === "should-enter";
|
|
335
|
+
const isEntering = !!componentState.unmounted;
|
|
336
|
+
const isExiting = presence?.[0] === false;
|
|
337
|
+
const wasEnteringRef = useRef(isEntering);
|
|
338
|
+
const justFinishedEntering = wasEnteringRef.current && !isEntering;
|
|
258
339
|
React.useEffect(() => {
|
|
259
340
|
wasEnteringRef.current = isEntering;
|
|
260
341
|
});
|
|
261
|
-
const effectiveTransition = styleState?.effectiveTransition ?? props.transition
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
342
|
+
const effectiveTransition = styleState?.effectiveTransition ?? props.transition;
|
|
343
|
+
const normalized = normalizeTransition(effectiveTransition);
|
|
344
|
+
const animationState = isExiting ? "exit" : isMounting || justFinishedEntering ? "enter" : "default";
|
|
345
|
+
const animationKey = getEffectiveAnimation(normalized, animationState);
|
|
346
|
+
const disableAnimation = isHydrating || !animationKey;
|
|
347
|
+
const isDark = themeName?.startsWith("dark") || false;
|
|
348
|
+
const sendExitComplete = presence?.[1];
|
|
349
|
+
const exitCycleIdRef = useRef(0);
|
|
350
|
+
const pendingExitKeysRef = useRef(/* @__PURE__ */new Set());
|
|
351
|
+
const exitCompletedRef = useRef(false);
|
|
352
|
+
const wasExitingRef = useRef(false);
|
|
353
|
+
const justStartedExiting = isExiting && !wasExitingRef.current;
|
|
354
|
+
const justStoppedExiting = !isExiting && wasExitingRef.current;
|
|
355
|
+
const markExitKeyDone = useEvent((key, cycleId, finished) => {
|
|
356
|
+
if (cycleId !== exitCycleIdRef.current) return;
|
|
357
|
+
if (exitCompletedRef.current) return;
|
|
358
|
+
pendingExitKeysRef.current.delete(key);
|
|
359
|
+
if (pendingExitKeysRef.current.size === 0) {
|
|
360
|
+
exitCompletedRef.current = true;
|
|
361
|
+
sendExitComplete?.();
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
const isExitingRef = useSharedValue(isExiting);
|
|
365
|
+
const exitCycleIdShared = useSharedValue(exitCycleIdRef.current);
|
|
366
|
+
if (justStartedExiting) {
|
|
367
|
+
exitCycleIdRef.current++;
|
|
368
|
+
exitCompletedRef.current = false;
|
|
369
|
+
pendingExitKeysRef.current.clear();
|
|
370
|
+
}
|
|
371
|
+
if (justStoppedExiting) {
|
|
372
|
+
exitCycleIdRef.current++;
|
|
373
|
+
pendingExitKeysRef.current.clear();
|
|
374
|
+
}
|
|
375
|
+
useIsomorphicLayoutEffect(() => {
|
|
376
|
+
isExitingRef.value = isExiting;
|
|
377
|
+
exitCycleIdShared.value = exitCycleIdRef.current;
|
|
378
|
+
}, [isExiting, exitCycleIdRef.current]);
|
|
379
|
+
React.useEffect(() => {
|
|
282
380
|
wasExitingRef.current = isExiting;
|
|
283
381
|
});
|
|
284
|
-
const animatedTargetsRef = useSharedValue(null)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
382
|
+
const animatedTargetsRef = useSharedValue(null);
|
|
383
|
+
const staticTargetsRef = useSharedValue(null);
|
|
384
|
+
const transformTargetsRef = useSharedValue(null);
|
|
385
|
+
const {
|
|
386
|
+
animatedStyles,
|
|
387
|
+
staticStyles
|
|
388
|
+
} = useMemo(() => {
|
|
389
|
+
const animated = {};
|
|
390
|
+
const staticStyles2 = {};
|
|
391
|
+
const animateOnly = props.animateOnly;
|
|
392
|
+
for (const key in style) {
|
|
393
|
+
const rawValue = style[key];
|
|
394
|
+
const value = resolveDynamicValue(rawValue, isDark);
|
|
395
|
+
if (value === void 0) continue;
|
|
396
|
+
if (disableAnimation) {
|
|
397
|
+
staticStyles2[key] = value;
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (canAnimateProperty(key, value, animateOnly)) {
|
|
401
|
+
animated[key] = value;
|
|
402
|
+
} else {
|
|
403
|
+
staticStyles2[key] = value;
|
|
304
404
|
}
|
|
305
|
-
|
|
405
|
+
}
|
|
406
|
+
if (isMounting) {
|
|
407
|
+
for (const key in animated) {
|
|
408
|
+
staticStyles2[key] = animated[key];
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
animatedStyles: animated,
|
|
413
|
+
staticStyles: staticStyles2
|
|
414
|
+
};
|
|
415
|
+
}, [disableAnimation, style, isDark, isMounting, props.animateOnly]);
|
|
416
|
+
const {
|
|
417
|
+
baseConfig,
|
|
418
|
+
propertyConfigs
|
|
419
|
+
} = useMemo(() => {
|
|
420
|
+
if (isHydrating) {
|
|
306
421
|
return {
|
|
307
|
-
|
|
308
|
-
|
|
422
|
+
baseConfig: {
|
|
423
|
+
type: "timing",
|
|
424
|
+
duration: 0
|
|
425
|
+
},
|
|
426
|
+
propertyConfigs: {}
|
|
309
427
|
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
propertyConfigs: {}
|
|
320
|
-
} : buildTransitionConfig(props.transition, animations, animationState, getStyleKeys(animatedStyles)), [isHydrating, props.transition, animatedStyles, animationState]),
|
|
321
|
-
configRef = useSharedValue({
|
|
322
|
-
baseConfig,
|
|
323
|
-
propertyConfigs,
|
|
324
|
-
disableAnimation,
|
|
325
|
-
isHydrating
|
|
326
|
-
});
|
|
428
|
+
}
|
|
429
|
+
return buildTransitionConfig(props.transition, animations, animationState, getStyleKeys(animatedStyles));
|
|
430
|
+
}, [isHydrating, props.transition, animatedStyles, animationState]);
|
|
431
|
+
const configRef = useSharedValue({
|
|
432
|
+
baseConfig,
|
|
433
|
+
propertyConfigs,
|
|
434
|
+
disableAnimation,
|
|
435
|
+
isHydrating
|
|
436
|
+
});
|
|
327
437
|
useIsomorphicLayoutEffect(() => {
|
|
328
438
|
configRef.set({
|
|
329
439
|
baseConfig,
|
|
@@ -331,16 +441,17 @@ function createAnimations(animationsConfig) {
|
|
|
331
441
|
disableAnimation,
|
|
332
442
|
isHydrating
|
|
333
443
|
});
|
|
334
|
-
}, [baseConfig, propertyConfigs, disableAnimation, isHydrating])
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
444
|
+
}, [baseConfig, propertyConfigs, disableAnimation, isHydrating]);
|
|
445
|
+
useStyleEmitter?.((nextStyle, effectiveTransition2) => {
|
|
446
|
+
const animateOnly = props.animateOnly;
|
|
447
|
+
const animated = {};
|
|
448
|
+
const statics = {};
|
|
449
|
+
const transforms = [];
|
|
450
|
+
const transitionToUse = effectiveTransition2 ?? props.transition;
|
|
451
|
+
const {
|
|
452
|
+
baseConfig: newBase,
|
|
453
|
+
propertyConfigs: newPropertyConfigs
|
|
454
|
+
} = buildTransitionConfig(transitionToUse, animations, animationState, getStyleKeys(nextStyle));
|
|
344
455
|
configRef.set({
|
|
345
456
|
baseConfig: newBase,
|
|
346
457
|
propertyConfigs: newPropertyConfigs,
|
|
@@ -348,76 +459,108 @@ function createAnimations(animationsConfig) {
|
|
|
348
459
|
isHydrating: configRef.get().isHydrating
|
|
349
460
|
});
|
|
350
461
|
for (const key in nextStyle) {
|
|
351
|
-
const rawValue = nextStyle[key]
|
|
352
|
-
|
|
353
|
-
if (value
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
462
|
+
const rawValue = nextStyle[key];
|
|
463
|
+
const value = resolveDynamicValue(rawValue, isDark);
|
|
464
|
+
if (value == void 0) continue;
|
|
465
|
+
if (configRef.get().disableAnimation) {
|
|
466
|
+
statics[key] = value;
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
if (key === "transform" && Array.isArray(value)) {
|
|
470
|
+
for (const t of value) {
|
|
471
|
+
if (t && typeof t === "object") {
|
|
472
|
+
const tKey = Object.keys(t)[0];
|
|
473
|
+
const tVal = t[tKey];
|
|
474
|
+
if (typeof tVal === "number" || typeof tVal === "string") {
|
|
475
|
+
transforms.push(t);
|
|
476
|
+
}
|
|
363
477
|
}
|
|
364
|
-
continue;
|
|
365
478
|
}
|
|
366
|
-
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (canAnimateProperty(key, value, animateOnly)) {
|
|
482
|
+
animated[key] = value;
|
|
483
|
+
} else {
|
|
484
|
+
statics[key] = value;
|
|
367
485
|
}
|
|
368
486
|
}
|
|
369
|
-
animatedTargetsRef.set(animated)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
487
|
+
animatedTargetsRef.set(animated);
|
|
488
|
+
staticTargetsRef.set(statics);
|
|
489
|
+
transformTargetsRef.set(transforms);
|
|
490
|
+
if (process.env.NODE_ENV === "development" && props.debug && props.debug !== "profile") {
|
|
491
|
+
console.info("[animations-reanimated] useStyleEmitter update", {
|
|
492
|
+
animated,
|
|
493
|
+
statics,
|
|
494
|
+
transforms
|
|
495
|
+
});
|
|
496
|
+
}
|
|
374
497
|
});
|
|
375
|
-
const exitKeysRegistered = useRef(
|
|
498
|
+
const exitKeysRegistered = useRef(false);
|
|
376
499
|
if (justStartedExiting && sendExitComplete) {
|
|
377
|
-
const exitKeys = []
|
|
378
|
-
|
|
379
|
-
for (const key in animatedStyles)
|
|
500
|
+
const exitKeys = [];
|
|
501
|
+
const animateOnly = props.animateOnly;
|
|
502
|
+
for (const key in animatedStyles) {
|
|
503
|
+
if (key === "transform") continue;
|
|
504
|
+
if (canAnimateProperty(key, animatedStyles[key], animateOnly)) {
|
|
505
|
+
exitKeys.push(key);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
380
508
|
const transforms = animatedStyles.transform;
|
|
381
|
-
if (transforms && Array.isArray(transforms))
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if (
|
|
386
|
-
|
|
509
|
+
if (transforms && Array.isArray(transforms)) {
|
|
510
|
+
for (const t of transforms) {
|
|
511
|
+
if (!t) continue;
|
|
512
|
+
const tKey = Object.keys(t)[0];
|
|
513
|
+
if (tKey) {
|
|
514
|
+
if (animateOnly && !animateOnly.includes(tKey)) {
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
exitKeys.push(`transform:${tKey}`);
|
|
518
|
+
}
|
|
387
519
|
}
|
|
388
520
|
}
|
|
389
|
-
pendingExitKeysRef.current = new Set(exitKeys)
|
|
521
|
+
pendingExitKeysRef.current = new Set(exitKeys);
|
|
522
|
+
exitKeysRegistered.current = exitKeys.length > 0;
|
|
390
523
|
}
|
|
391
524
|
React.useEffect(() => {
|
|
392
|
-
!justStartedExiting || !sendExitComplete
|
|
525
|
+
if (!justStartedExiting || !sendExitComplete) return;
|
|
526
|
+
if (!exitKeysRegistered.current && pendingExitKeysRef.current.size === 0) {
|
|
527
|
+
if (!exitCompletedRef.current) {
|
|
528
|
+
exitCompletedRef.current = true;
|
|
529
|
+
sendExitComplete();
|
|
530
|
+
}
|
|
531
|
+
}
|
|
393
532
|
}, [justStartedExiting, sendExitComplete]);
|
|
394
533
|
const animatedStyle = useAnimatedStyle(() => {
|
|
395
534
|
"worklet";
|
|
396
535
|
|
|
397
|
-
if (disableAnimation || isHydrating)
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
536
|
+
if (disableAnimation || isHydrating) {
|
|
537
|
+
return {};
|
|
538
|
+
}
|
|
539
|
+
const result = {};
|
|
540
|
+
const config = configRef.get();
|
|
541
|
+
const emitterAnimated = animatedTargetsRef.value;
|
|
542
|
+
const emitterStatic = staticTargetsRef.value;
|
|
543
|
+
const emitterTransforms = transformTargetsRef.value;
|
|
544
|
+
const hasEmitterUpdates = emitterAnimated !== null;
|
|
545
|
+
const animatedValues = hasEmitterUpdates ? emitterAnimated : animatedStyles;
|
|
546
|
+
const staticValues = hasEmitterUpdates ? emitterStatic : {};
|
|
547
|
+
const currentlyExiting = isExitingRef.value;
|
|
548
|
+
const currentCycleId = exitCycleIdShared.value;
|
|
549
|
+
for (const key in staticValues) {
|
|
550
|
+
result[key] = staticValues[key];
|
|
551
|
+
}
|
|
409
552
|
for (const key in animatedValues) {
|
|
410
553
|
if (key === "transform") continue;
|
|
411
|
-
const targetValue = animatedValues[key]
|
|
412
|
-
|
|
554
|
+
const targetValue = animatedValues[key];
|
|
555
|
+
const propConfig = config.propertyConfigs[key] ?? config.baseConfig;
|
|
413
556
|
let callback;
|
|
414
557
|
if (currentlyExiting) {
|
|
415
|
-
const capturedKey = key
|
|
416
|
-
|
|
558
|
+
const capturedKey = key;
|
|
559
|
+
const capturedCycleId = currentCycleId;
|
|
417
560
|
callback = finished => {
|
|
418
561
|
"worklet";
|
|
419
562
|
|
|
420
|
-
runOnJS(markExitKeyDone)(capturedKey, capturedCycleId, finished ??
|
|
563
|
+
runOnJS(markExitKeyDone)(capturedKey, capturedCycleId, finished ?? false);
|
|
421
564
|
};
|
|
422
565
|
}
|
|
423
566
|
result[key] = applyAnimation(targetValue, propConfig, callback);
|
|
@@ -430,18 +573,18 @@ function createAnimations(animationsConfig) {
|
|
|
430
573
|
const keys = Object.keys(t);
|
|
431
574
|
if (keys.length === 0) continue;
|
|
432
575
|
const value = t[keys[0]];
|
|
433
|
-
if (typeof value
|
|
434
|
-
const transformKey = Object.keys(t)[0]
|
|
435
|
-
|
|
436
|
-
|
|
576
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
577
|
+
const transformKey = Object.keys(t)[0];
|
|
578
|
+
const targetValue = t[transformKey];
|
|
579
|
+
const propConfig = config.propertyConfigs[transformKey] ?? config.baseConfig;
|
|
437
580
|
let callback;
|
|
438
581
|
if (currentlyExiting) {
|
|
439
|
-
const capturedKey = `transform:${transformKey}
|
|
440
|
-
|
|
582
|
+
const capturedKey = `transform:${transformKey}`;
|
|
583
|
+
const capturedCycleId = currentCycleId;
|
|
441
584
|
callback = finished => {
|
|
442
585
|
"worklet";
|
|
443
586
|
|
|
444
|
-
runOnJS(markExitKeyDone)(capturedKey, capturedCycleId, finished ??
|
|
587
|
+
runOnJS(markExitKeyDone)(capturedKey, capturedCycleId, finished ?? false);
|
|
445
588
|
};
|
|
446
589
|
}
|
|
447
590
|
validTransforms.push({
|
|
@@ -449,21 +592,26 @@ function createAnimations(animationsConfig) {
|
|
|
449
592
|
});
|
|
450
593
|
}
|
|
451
594
|
}
|
|
452
|
-
validTransforms.length > 0
|
|
595
|
+
if (validTransforms.length > 0) {
|
|
596
|
+
result.transform = validTransforms;
|
|
597
|
+
}
|
|
453
598
|
}
|
|
454
599
|
return result;
|
|
455
|
-
}, [animatedStyles, baseConfig, propertyConfigs, disableAnimation, isHydrating,
|
|
456
|
-
//
|
|
457
|
-
animatedTargetsRef, staticTargetsRef, transformTargetsRef, isExitingRef, exitCycleIdShared, markExitKeyDone]);
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
600
|
+
}, isWeb ? [animatedStyles, baseConfig, propertyConfigs, disableAnimation, isHydrating,
|
|
601
|
+
// pass SharedValues so the mapper watches them on web (no babel plugin)
|
|
602
|
+
animatedTargetsRef, staticTargetsRef, transformTargetsRef, isExitingRef, exitCycleIdShared, markExitKeyDone] : void 0);
|
|
603
|
+
if (process.env.NODE_ENV === "development" && props.debug && props.debug !== "profile") {
|
|
604
|
+
console.info("[animations-reanimated] useAnimations", {
|
|
605
|
+
animationKey,
|
|
606
|
+
componentState,
|
|
607
|
+
isExiting,
|
|
608
|
+
animatedStyles,
|
|
609
|
+
staticStyles,
|
|
610
|
+
baseConfig,
|
|
611
|
+
propertyConfigs
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
return {
|
|
467
615
|
style: [staticStyles, animatedStyle]
|
|
468
616
|
};
|
|
469
617
|
}
|