motion-on-native 1.2.7 → 1.3.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/README.md CHANGED
@@ -23,25 +23,19 @@ This package requires:
23
23
  ```tsx
24
24
  import { NativeMotion } from 'motion-on-native';
25
25
 
26
- // Basic animation
26
+ // Controlled animation
27
27
  <NativeMotion.View
28
28
  initial={{ opacity: 0, x: -100 }}
29
29
  animate={{ opacity: 1, x: 0 }}
30
+ exit={{ opacity: 0, scale: 0 }}
31
+ shouldAnimate={shouldShow}
32
+ shouldDeAnimate={shouldHide}
33
+ shouldExit={shouldExit}
30
34
  transition={{ type: 'spring', damping: 15 }}
35
+ onExitComplete={() => console.log('Exit animation complete')}
31
36
  >
32
37
  <Text>Animated content</Text>
33
38
  </NativeMotion.View>
34
-
35
- // Exit animation
36
- <NativeMotion.View
37
- initial={{ opacity: 0, scale: 0.5 }}
38
- animate={{ opacity: 1, scale: 1 }}
39
- exit={{ opacity: 0, scale: 0 }}
40
- shouldExit={shouldHide}
41
- onExitComplete={() => console.log('Animation complete')}
42
- >
43
- <Text>Content with exit animation</Text>
44
- </NativeMotion.View>
45
39
  ```
46
40
 
47
41
  ## Components
@@ -52,6 +46,13 @@ import { NativeMotion } from 'motion-on-native';
52
46
  - `NativeMotion.TextInput`
53
47
  - `NativeMotion.TouchableOpacity`
54
48
 
49
+ ## Animation Control Props
50
+
51
+ - `shouldAnimate`: boolean - Triggers animation from `initial` to `animate`
52
+ - `shouldDeAnimate`: boolean - Triggers animation from `animate` back to `initial`
53
+ - `shouldExit`: boolean - Triggers animation from current state to `exit` (element stays mounted)
54
+ - `onExitComplete`: callback - Called when exit animation completes
55
+
55
56
  ## Animation Properties
56
57
 
57
58
  - `opacity`: 0-1
package/lib/index.d.ts CHANGED
@@ -78,6 +78,8 @@ export interface MotionComponentProps {
78
78
  animate?: AnimationProps;
79
79
  exit?: AnimationProps;
80
80
  transition?: TransitionProps;
81
+ shouldAnimate?: boolean;
82
+ shouldDeAnimate?: boolean;
81
83
  shouldExit?: boolean;
82
84
  onExitComplete?: () => void;
83
85
  whileHover?: AnimationProps;
@@ -89,9 +91,14 @@ export interface MotionComponentProps {
89
91
  children?: React.ReactNode;
90
92
  }
91
93
  export declare const NativeMotion: {
92
- View: (props: MotionComponentProps & import("react-native").ViewProps) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>> | null;
93
- Text: (props: MotionComponentProps & import("react-native").TextProps) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>> | null;
94
- Image: (props: MotionComponentProps & import("react-native").ImageProps) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>> | null;
95
- TextInput: (props: MotionComponentProps & import("react-native").TextInputProps) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>> | null;
96
- TouchableOpacity: (props: MotionComponentProps & import("react-native").TouchableOpacityProps & React.RefAttributes<View>) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>> | null;
94
+ View: (props: MotionComponentProps & import("react-native").ViewProps) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>>;
95
+ Text: (props: MotionComponentProps & import("react-native").TextProps) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>>;
96
+ Image: (props: MotionComponentProps & import("react-native").ImageProps) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>>;
97
+ ImageBackground: (props: MotionComponentProps & import("react-native").ImageBackgroundProps) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>>;
98
+ TextInput: (props: MotionComponentProps & import("react-native").TextInputProps) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>>;
99
+ TouchableOpacity: (props: MotionComponentProps & import("react-native").TouchableOpacityProps & React.RefAttributes<View>) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>>;
100
+ ScrollView: (props: MotionComponentProps & import("react-native").ScrollViewProps) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>>;
101
+ FlatList: (props: MotionComponentProps & import("react-native").FlatListProps<unknown>) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>>;
102
+ SectionList: (props: MotionComponentProps & import("react-native").SectionListProps<unknown, unknown>) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>>;
103
+ Pressable: (props: MotionComponentProps & import("react-native").PressableProps & React.RefAttributes<View>) => React.ReactElement<import("react-native-reanimated").AnimateProps<any>, string | React.JSXElementConstructor<any>>;
97
104
  };
package/lib/index.js CHANGED
@@ -58,10 +58,7 @@ const DEFAULT_TRANSITION = {
58
58
  };
59
59
  function createMotionComponent(Component) {
60
60
  return function MotionComponent(props) {
61
- const { initial = {}, animate = {}, exit = {}, transition = DEFAULT_TRANSITION, shouldExit = false, onExitComplete, whileHover, whileTap, whileFocus, layout, layoutId, style = {}, children } = props, rest = __rest(props, ["initial", "animate", "exit", "transition", "shouldExit", "onExitComplete", "whileHover", "whileTap", "whileFocus", "layout", "layoutId", "style", "children"]);
62
- const [isPresent, setIsPresent] = (0, react_1.useState)(true);
63
- const [hasAnimated, setHasAnimated] = (0, react_1.useState)(false);
64
- const isExitingRef = (0, react_1.useRef)(false);
61
+ const { initial = {}, animate = {}, exit = {}, transition = DEFAULT_TRANSITION, shouldAnimate = false, shouldDeAnimate = false, shouldExit = false, onExitComplete, whileHover, whileTap, whileFocus, layout, layoutId, style = {}, children } = props, rest = __rest(props, ["initial", "animate", "exit", "transition", "shouldAnimate", "shouldDeAnimate", "shouldExit", "onExitComplete", "whileHover", "whileTap", "whileFocus", "layout", "layoutId", "style", "children"]);
65
62
  // Create shared values
66
63
  const opacity = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('opacity', initial));
67
64
  const x = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('x', initial));
@@ -111,42 +108,96 @@ function createMotionComponent(Component) {
111
108
  const borderBottomWidth = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('borderBottomWidth', initial));
112
109
  const borderLeftWidth = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('borderLeftWidth', initial));
113
110
  const borderRightWidth = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('borderRightWidth', initial));
114
- const borderColor = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('borderColor', initial));
115
111
  const borderTopColor = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('borderTopColor', initial));
116
112
  const borderBottomColor = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('borderBottomColor', initial));
117
113
  const borderLeftColor = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('borderLeftColor', initial));
118
114
  const borderRightColor = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('borderRightColor', initial));
119
- // Color properties
120
- const backgroundColor = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('backgroundColor', initial));
121
- const color = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('color', initial));
115
+ // Color properties - use progress values for interpolation
116
+ const backgroundColorProgress = (0, react_native_reanimated_1.useSharedValue)(0);
117
+ const colorProgress = (0, react_native_reanimated_1.useSharedValue)(0);
118
+ const borderColorProgress = (0, react_native_reanimated_1.useSharedValue)(0);
119
+ const shadowColorProgress = (0, react_native_reanimated_1.useSharedValue)(0);
120
+ // Store color values for interpolation
121
+ const backgroundColorFrom = (0, react_1.useRef)(getInitialValue('backgroundColor', initial));
122
+ const backgroundColorTo = (0, react_1.useRef)(getInitialValue('backgroundColor', initial));
123
+ const colorFrom = (0, react_1.useRef)(getInitialValue('color', initial));
124
+ const colorTo = (0, react_1.useRef)(getInitialValue('color', initial));
125
+ const borderColorFrom = (0, react_1.useRef)(getInitialValue('borderColor', initial));
126
+ const borderColorTo = (0, react_1.useRef)(getInitialValue('borderColor', initial));
127
+ const shadowColorFrom = (0, react_1.useRef)(getInitialValue('shadowColor', initial));
128
+ const shadowColorTo = (0, react_1.useRef)(getInitialValue('shadowColor', initial));
122
129
  // Position properties
123
130
  const top = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('top', initial));
124
131
  const bottom = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('bottom', initial));
125
132
  const left = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('left', initial));
126
133
  const right = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('right', initial));
127
134
  // Shadow properties
128
- const shadowColor = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('shadowColor', initial));
129
135
  const shadowOpacity = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('shadowOpacity', initial));
130
136
  const shadowRadius = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('shadowRadius', initial));
131
137
  const elevation = (0, react_native_reanimated_1.useSharedValue)(getInitialValue('elevation', initial));
132
138
  // Animation helper
133
139
  const animateToValues = (targetValues, transitionConfig = transition) => {
134
140
  Object.entries(targetValues).forEach(([key, value]) => {
135
- var _a, _b, _c, _d, _e, _f, _g, _h;
141
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
136
142
  if (value !== undefined) {
143
+ // Handle color properties with interpolation
144
+ if (key === 'backgroundColor') {
145
+ backgroundColorTo.current = value;
146
+ const config = transitionConfig.type === 'spring'
147
+ ? (0, react_native_reanimated_1.withSpring)(1, {
148
+ damping: (_a = transitionConfig.damping) !== null && _a !== void 0 ? _a : DEFAULT_TRANSITION.damping,
149
+ stiffness: (_b = transitionConfig.stiffness) !== null && _b !== void 0 ? _b : DEFAULT_TRANSITION.stiffness,
150
+ })
151
+ : (0, react_native_reanimated_1.withTiming)(1, { duration: (_c = transitionConfig.duration) !== null && _c !== void 0 ? _c : DEFAULT_TRANSITION.duration });
152
+ backgroundColorProgress.value = config;
153
+ return;
154
+ }
155
+ if (key === 'color') {
156
+ colorTo.current = value;
157
+ const config = transitionConfig.type === 'spring'
158
+ ? (0, react_native_reanimated_1.withSpring)(1, {
159
+ damping: (_d = transitionConfig.damping) !== null && _d !== void 0 ? _d : DEFAULT_TRANSITION.damping,
160
+ stiffness: (_e = transitionConfig.stiffness) !== null && _e !== void 0 ? _e : DEFAULT_TRANSITION.stiffness,
161
+ })
162
+ : (0, react_native_reanimated_1.withTiming)(1, { duration: (_f = transitionConfig.duration) !== null && _f !== void 0 ? _f : DEFAULT_TRANSITION.duration });
163
+ colorProgress.value = config;
164
+ return;
165
+ }
166
+ if (key === 'borderColor') {
167
+ borderColorTo.current = value;
168
+ const config = transitionConfig.type === 'spring'
169
+ ? (0, react_native_reanimated_1.withSpring)(1, {
170
+ damping: (_g = transitionConfig.damping) !== null && _g !== void 0 ? _g : DEFAULT_TRANSITION.damping,
171
+ stiffness: (_h = transitionConfig.stiffness) !== null && _h !== void 0 ? _h : DEFAULT_TRANSITION.stiffness,
172
+ })
173
+ : (0, react_native_reanimated_1.withTiming)(1, { duration: (_j = transitionConfig.duration) !== null && _j !== void 0 ? _j : DEFAULT_TRANSITION.duration });
174
+ borderColorProgress.value = config;
175
+ return;
176
+ }
177
+ if (key === 'shadowColor') {
178
+ shadowColorTo.current = value;
179
+ const config = transitionConfig.type === 'spring'
180
+ ? (0, react_native_reanimated_1.withSpring)(1, {
181
+ damping: (_k = transitionConfig.damping) !== null && _k !== void 0 ? _k : DEFAULT_TRANSITION.damping,
182
+ stiffness: (_l = transitionConfig.stiffness) !== null && _l !== void 0 ? _l : DEFAULT_TRANSITION.stiffness,
183
+ })
184
+ : (0, react_native_reanimated_1.withTiming)(1, { duration: (_m = transitionConfig.duration) !== null && _m !== void 0 ? _m : DEFAULT_TRANSITION.duration });
185
+ shadowColorProgress.value = config;
186
+ return;
187
+ }
188
+ // Handle non-color properties
137
189
  const sharedValue = getSharedValue(key);
138
190
  if (sharedValue) {
139
191
  let config;
140
192
  if (transitionConfig.repeat && (transitionConfig.repeat > 0 || transitionConfig.repeat === 'infinity')) {
141
- // Use withRepeat for repeated animations
142
193
  const baseAnimation = transitionConfig.type === 'spring'
143
194
  ? (0, react_native_reanimated_1.withSpring)(value, {
144
- damping: (_a = transitionConfig.damping) !== null && _a !== void 0 ? _a : DEFAULT_TRANSITION.damping,
145
- stiffness: (_b = transitionConfig.stiffness) !== null && _b !== void 0 ? _b : DEFAULT_TRANSITION.stiffness,
146
- mass: (_c = transitionConfig.mass) !== null && _c !== void 0 ? _c : 1,
195
+ damping: (_o = transitionConfig.damping) !== null && _o !== void 0 ? _o : DEFAULT_TRANSITION.damping,
196
+ stiffness: (_p = transitionConfig.stiffness) !== null && _p !== void 0 ? _p : DEFAULT_TRANSITION.stiffness,
197
+ mass: (_q = transitionConfig.mass) !== null && _q !== void 0 ? _q : 1,
147
198
  })
148
199
  : (0, react_native_reanimated_1.withTiming)(value, {
149
- duration: (_d = transitionConfig.duration) !== null && _d !== void 0 ? _d : DEFAULT_TRANSITION.duration,
200
+ duration: (_r = transitionConfig.duration) !== null && _r !== void 0 ? _r : DEFAULT_TRANSITION.duration,
150
201
  easing: react_native_reanimated_1.Easing.linear,
151
202
  });
152
203
  const repeatCount = transitionConfig.repeat === 'infinity' ? -1 : transitionConfig.repeat;
@@ -154,15 +205,14 @@ function createMotionComponent(Component) {
154
205
  config = (0, react_native_reanimated_1.withRepeat)(baseAnimation, repeatCount, reverse);
155
206
  }
156
207
  else {
157
- // Single animation
158
208
  config = transitionConfig.type === 'spring'
159
209
  ? (0, react_native_reanimated_1.withSpring)(value, {
160
- damping: (_e = transitionConfig.damping) !== null && _e !== void 0 ? _e : DEFAULT_TRANSITION.damping,
161
- stiffness: (_f = transitionConfig.stiffness) !== null && _f !== void 0 ? _f : DEFAULT_TRANSITION.stiffness,
162
- mass: (_g = transitionConfig.mass) !== null && _g !== void 0 ? _g : 1,
210
+ damping: (_s = transitionConfig.damping) !== null && _s !== void 0 ? _s : DEFAULT_TRANSITION.damping,
211
+ stiffness: (_t = transitionConfig.stiffness) !== null && _t !== void 0 ? _t : DEFAULT_TRANSITION.stiffness,
212
+ mass: (_u = transitionConfig.mass) !== null && _u !== void 0 ? _u : 1,
163
213
  })
164
214
  : (0, react_native_reanimated_1.withTiming)(value, {
165
- duration: (_h = transitionConfig.duration) !== null && _h !== void 0 ? _h : DEFAULT_TRANSITION.duration,
215
+ duration: (_v = transitionConfig.duration) !== null && _v !== void 0 ? _v : DEFAULT_TRANSITION.duration,
166
216
  });
167
217
  }
168
218
  if (transitionConfig.delay) {
@@ -229,30 +279,22 @@ function createMotionComponent(Component) {
229
279
  case 'borderBottomWidth': return borderBottomWidth;
230
280
  case 'borderLeftWidth': return borderLeftWidth;
231
281
  case 'borderRightWidth': return borderRightWidth;
232
- case 'borderColor': return borderColor;
233
282
  case 'borderTopColor': return borderTopColor;
234
283
  case 'borderBottomColor': return borderBottomColor;
235
284
  case 'borderLeftColor': return borderLeftColor;
236
285
  case 'borderRightColor': return borderRightColor;
237
- // Color properties
238
- case 'backgroundColor': return backgroundColor;
239
- case 'color': return color;
240
286
  // Position properties
241
287
  case 'top': return top;
242
288
  case 'bottom': return bottom;
243
289
  case 'left': return left;
244
290
  case 'right': return right;
245
291
  // Shadow properties
246
- case 'shadowColor': return shadowColor;
247
292
  case 'shadowOpacity': return shadowOpacity;
248
293
  case 'shadowRadius': return shadowRadius;
249
294
  case 'elevation': return elevation;
250
295
  default: return null;
251
296
  }
252
297
  };
253
- // Memoize animate prop to prevent unnecessary re-animations
254
- const animateString = JSON.stringify(animate);
255
- const memoizedAnimate = (0, react_1.useMemo)(() => animate, [animateString]);
256
298
  // Set initial values on mount
257
299
  (0, react_1.useEffect)(() => {
258
300
  if (initial !== false) {
@@ -264,78 +306,90 @@ function createMotionComponent(Component) {
264
306
  });
265
307
  }
266
308
  }, []);
267
- // Mount animation: initial -> animate
309
+ // Handle shouldAnimate: initial -> animate
268
310
  (0, react_1.useEffect)(() => {
269
- if (!hasAnimated && !isExitingRef.current) {
270
- // Animate to target values
271
- const timer = setTimeout(() => {
272
- if (!isExitingRef.current) {
273
- animateToValues(memoizedAnimate);
274
- setHasAnimated(true);
275
- }
276
- }, 16);
277
- return () => clearTimeout(timer);
311
+ if (shouldAnimate) {
312
+ // Reset color progress and update from/to values
313
+ if (animate.backgroundColor) {
314
+ backgroundColorFrom.current = backgroundColorTo.current;
315
+ backgroundColorProgress.value = 0;
316
+ }
317
+ if (animate.color) {
318
+ colorFrom.current = colorTo.current;
319
+ colorProgress.value = 0;
320
+ }
321
+ if (animate.borderColor) {
322
+ borderColorFrom.current = borderColorTo.current;
323
+ borderColorProgress.value = 0;
324
+ }
325
+ if (animate.shadowColor) {
326
+ shadowColorFrom.current = shadowColorTo.current;
327
+ shadowColorProgress.value = 0;
328
+ }
329
+ animateToValues(animate);
278
330
  }
279
- return undefined;
280
- }, [memoizedAnimate]);
281
- // Handle animate prop changes (only animate if values actually changed)
282
- const prevAnimateRef = (0, react_1.useRef)(memoizedAnimate);
331
+ }, [shouldAnimate]);
332
+ // Handle shouldDeAnimate: animate -> initial
283
333
  (0, react_1.useEffect)(() => {
284
- if (hasAnimated && !isExitingRef.current) {
285
- // Only animate if animate prop actually changed
286
- const hasChanged = JSON.stringify(prevAnimateRef.current) !== JSON.stringify(memoizedAnimate);
287
- if (hasChanged) {
288
- animateToValues(memoizedAnimate);
289
- prevAnimateRef.current = memoizedAnimate;
334
+ if (shouldDeAnimate && initial !== false) {
335
+ const initialProps = initial;
336
+ // Reset color progress and update from/to values
337
+ if (initialProps.backgroundColor) {
338
+ backgroundColorFrom.current = backgroundColorTo.current;
339
+ backgroundColorTo.current = initialProps.backgroundColor;
340
+ backgroundColorProgress.value = 0;
341
+ }
342
+ if (initialProps.color) {
343
+ colorFrom.current = colorTo.current;
344
+ colorTo.current = initialProps.color;
345
+ colorProgress.value = 0;
290
346
  }
347
+ if (initialProps.borderColor) {
348
+ borderColorFrom.current = borderColorTo.current;
349
+ borderColorTo.current = initialProps.borderColor;
350
+ borderColorProgress.value = 0;
351
+ }
352
+ if (initialProps.shadowColor) {
353
+ shadowColorFrom.current = shadowColorTo.current;
354
+ shadowColorTo.current = initialProps.shadowColor;
355
+ shadowColorProgress.value = 0;
356
+ }
357
+ animateToValues(initialProps);
291
358
  }
292
- }, [memoizedAnimate]);
293
- // Handle shouldExit
359
+ }, [shouldDeAnimate]);
360
+ // Handle shouldExit: animate -> exit (no unmount)
294
361
  (0, react_1.useEffect)(() => {
295
362
  var _a;
296
- if (shouldExit && !isExitingRef.current) {
297
- isExitingRef.current = true;
298
- if (exit && Object.keys(exit).length > 0) {
299
- animateToValues(exit, transition);
363
+ if (shouldExit && exit && Object.keys(exit).length > 0) {
364
+ // Reset color progress and update from/to values
365
+ if (exit.backgroundColor) {
366
+ backgroundColorFrom.current = backgroundColorTo.current;
367
+ backgroundColorTo.current = exit.backgroundColor;
368
+ backgroundColorProgress.value = 0;
369
+ }
370
+ if (exit.color) {
371
+ colorFrom.current = colorTo.current;
372
+ colorTo.current = exit.color;
373
+ colorProgress.value = 0;
374
+ }
375
+ if (exit.borderColor) {
376
+ borderColorFrom.current = borderColorTo.current;
377
+ borderColorTo.current = exit.borderColor;
378
+ borderColorProgress.value = 0;
379
+ }
380
+ if (exit.shadowColor) {
381
+ shadowColorFrom.current = shadowColorTo.current;
382
+ shadowColorTo.current = exit.shadowColor;
383
+ shadowColorProgress.value = 0;
384
+ }
385
+ animateToValues(exit, transition);
386
+ if (onExitComplete) {
300
387
  const exitDuration = (_a = transition.duration) !== null && _a !== void 0 ? _a : 300;
301
388
  setTimeout(() => {
302
- setIsPresent(false);
303
- if (onExitComplete) {
304
- onExitComplete();
305
- }
306
- }, exitDuration);
307
- }
308
- else {
309
- setIsPresent(false);
310
- if (onExitComplete) {
311
389
  onExitComplete();
312
- }
390
+ }, exitDuration);
313
391
  }
314
392
  }
315
- else if (!shouldExit && isExitingRef.current) {
316
- // Re-entering: reset everything
317
- isExitingRef.current = false;
318
- setIsPresent(true);
319
- setHasAnimated(false);
320
- // Reset to initial values and animate
321
- setTimeout(() => {
322
- if (initial !== false) {
323
- Object.entries(initial).forEach(([key, value]) => {
324
- const sharedValue = getSharedValue(key);
325
- if (sharedValue && value !== undefined) {
326
- sharedValue.value = value;
327
- }
328
- });
329
- }
330
- // Animate to target after a frame
331
- setTimeout(() => {
332
- if (!isExitingRef.current) {
333
- animateToValues(memoizedAnimate);
334
- setHasAnimated(true);
335
- }
336
- }, 16);
337
- }, 0);
338
- }
339
393
  }, [shouldExit]);
340
394
  // Animated style
341
395
  const animatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
@@ -436,21 +490,18 @@ function createMotionComponent(Component) {
436
490
  style.borderLeftWidth = borderLeftWidth.value;
437
491
  if (borderRightWidth.value !== 0)
438
492
  style.borderRightWidth = borderRightWidth.value;
439
- if (borderColor.value !== 0)
440
- style.borderColor = borderColor.value;
441
- if (borderTopColor.value !== 0)
493
+ style.borderColor = (0, react_native_reanimated_1.interpolateColor)(borderColorProgress.value, [0, 1], [borderColorFrom.current, borderColorTo.current]);
494
+ if (borderTopColor.value !== 'transparent')
442
495
  style.borderTopColor = borderTopColor.value;
443
- if (borderBottomColor.value !== 0)
496
+ if (borderBottomColor.value !== 'transparent')
444
497
  style.borderBottomColor = borderBottomColor.value;
445
- if (borderLeftColor.value !== 0)
498
+ if (borderLeftColor.value !== 'transparent')
446
499
  style.borderLeftColor = borderLeftColor.value;
447
- if (borderRightColor.value !== 0)
500
+ if (borderRightColor.value !== 'transparent')
448
501
  style.borderRightColor = borderRightColor.value;
449
- // Color properties
450
- if (backgroundColor.value !== 0)
451
- style.backgroundColor = backgroundColor.value;
452
- if (color.value !== 0)
453
- style.color = color.value;
502
+ // Color properties with interpolation
503
+ style.backgroundColor = (0, react_native_reanimated_1.interpolateColor)(backgroundColorProgress.value, [0, 1], [backgroundColorFrom.current, backgroundColorTo.current]);
504
+ style.color = (0, react_native_reanimated_1.interpolateColor)(colorProgress.value, [0, 1], [colorFrom.current, colorTo.current]);
454
505
  // Position properties
455
506
  if (top.value !== 0)
456
507
  style.top = top.value;
@@ -461,8 +512,7 @@ function createMotionComponent(Component) {
461
512
  if (right.value !== 0)
462
513
  style.right = right.value;
463
514
  // Shadow properties
464
- if (shadowColor.value !== 0)
465
- style.shadowColor = shadowColor.value;
515
+ style.shadowColor = (0, react_native_reanimated_1.interpolateColor)(shadowColorProgress.value, [0, 1], [shadowColorFrom.current, shadowColorTo.current]);
466
516
  if (shadowOpacity.value !== 0)
467
517
  style.shadowOpacity = shadowOpacity.value;
468
518
  if (shadowRadius.value !== 0)
@@ -472,8 +522,6 @@ function createMotionComponent(Component) {
472
522
  return style;
473
523
  });
474
524
  const AnimatedComponent = react_native_reanimated_1.default.createAnimatedComponent(Component);
475
- if (!isPresent)
476
- return null;
477
525
  return react_1.default.createElement(AnimatedComponent, Object.assign({ style: [style, animatedStyle] }, rest), children);
478
526
  };
479
527
  }
@@ -504,6 +552,15 @@ function getDefaultValue(key) {
504
552
  case 'skewX':
505
553
  case 'skewY':
506
554
  return '0deg';
555
+ case 'backgroundColor':
556
+ case 'color':
557
+ case 'borderColor':
558
+ case 'borderTopColor':
559
+ case 'borderBottomColor':
560
+ case 'borderLeftColor':
561
+ case 'borderRightColor':
562
+ case 'shadowColor':
563
+ return 'transparent';
507
564
  default:
508
565
  return 0;
509
566
  }
@@ -512,6 +569,11 @@ exports.NativeMotion = {
512
569
  View: createMotionComponent(react_native_1.View),
513
570
  Text: createMotionComponent(react_native_1.Text),
514
571
  Image: createMotionComponent(react_native_1.Image),
572
+ ImageBackground: createMotionComponent(react_native_1.ImageBackground),
515
573
  TextInput: createMotionComponent(react_native_1.TextInput),
516
574
  TouchableOpacity: createMotionComponent(react_native_1.TouchableOpacity),
575
+ ScrollView: createMotionComponent(react_native_1.ScrollView),
576
+ FlatList: createMotionComponent(react_native_1.FlatList),
577
+ SectionList: createMotionComponent(react_native_1.SectionList),
578
+ Pressable: createMotionComponent(react_native_1.Pressable),
517
579
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "motion-on-native",
3
- "version": "1.2.7",
3
+ "version": "1.3.0",
4
4
  "description": "Framer Motion-inspired animation library for React Native with Reanimated. Easy spring animations, gestures, and transitions for mobile apps.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",