@idealyst/animate 1.2.29
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/package.json +72 -0
- package/src/index.native.ts +66 -0
- package/src/index.ts +64 -0
- package/src/types.ts +208 -0
- package/src/useAnimatedStyle.native.ts +168 -0
- package/src/useAnimatedStyle.ts +165 -0
- package/src/useAnimatedValue.native.ts +169 -0
- package/src/useAnimatedValue.ts +227 -0
- package/src/useGradientBorder.native.tsx +229 -0
- package/src/useGradientBorder.ts +180 -0
- package/src/usePresence.native.ts +157 -0
- package/src/usePresence.ts +151 -0
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@idealyst/animate",
|
|
3
|
+
"version": "1.2.29",
|
|
4
|
+
"description": "Cross-platform animation hooks for React and React Native",
|
|
5
|
+
"readme": "README.md",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"module": "src/index.ts",
|
|
8
|
+
"types": "src/index.ts",
|
|
9
|
+
"react-native": "src/index.native.ts",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/IdealystIO/idealyst-framework.git",
|
|
13
|
+
"directory": "packages/animate"
|
|
14
|
+
},
|
|
15
|
+
"author": "Idealyst <contact@idealyst.io>",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"react-native": "./src/index.native.ts",
|
|
23
|
+
"import": "./src/index.ts",
|
|
24
|
+
"require": "./src/index.ts",
|
|
25
|
+
"types": "./src/index.ts"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"prepublishOnly": "echo 'Publishing TypeScript source directly'",
|
|
30
|
+
"publish:npm": "npm publish"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@idealyst/theme": "^1.2.29",
|
|
34
|
+
"react": ">=16.8.0",
|
|
35
|
+
"react-native": ">=0.60.0",
|
|
36
|
+
"react-native-reanimated": ">=3.0.0",
|
|
37
|
+
"react-native-svg": ">=13.0.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"react-native": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
"react-native-reanimated": {
|
|
44
|
+
"optional": true
|
|
45
|
+
},
|
|
46
|
+
"react-native-svg": {
|
|
47
|
+
"optional": true
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@idealyst/theme": "^1.2.29",
|
|
52
|
+
"@types/react": "^19.1.0",
|
|
53
|
+
"react": "^19.1.0",
|
|
54
|
+
"react-native": "^0.80.1",
|
|
55
|
+
"react-native-reanimated": "^4.1.3",
|
|
56
|
+
"react-native-svg": "^15.14.0",
|
|
57
|
+
"typescript": "^5.0.0"
|
|
58
|
+
},
|
|
59
|
+
"files": [
|
|
60
|
+
"src",
|
|
61
|
+
"README.md"
|
|
62
|
+
],
|
|
63
|
+
"keywords": [
|
|
64
|
+
"react",
|
|
65
|
+
"react-native",
|
|
66
|
+
"animation",
|
|
67
|
+
"hooks",
|
|
68
|
+
"cross-platform",
|
|
69
|
+
"reanimated",
|
|
70
|
+
"css-transitions"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @idealyst/animate - Native exports
|
|
3
|
+
*
|
|
4
|
+
* Cross-platform animation hooks for React and React Native.
|
|
5
|
+
* This module provides a unified API for creating smooth animations
|
|
6
|
+
* that work on both web and native platforms.
|
|
7
|
+
*
|
|
8
|
+
* On native, animations use Reanimated for UI thread performance.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* import Animated from 'react-native-reanimated';
|
|
13
|
+
* import { useAnimatedStyle, usePresence, useGradientBorder } from '@idealyst/animate';
|
|
14
|
+
*
|
|
15
|
+
* // State-driven animations
|
|
16
|
+
* const style = useAnimatedStyle({
|
|
17
|
+
* opacity: isVisible ? 1 : 0,
|
|
18
|
+
* }, { duration: 'normal', easing: 'easeOut' });
|
|
19
|
+
*
|
|
20
|
+
* return <Animated.View style={style}>Content</Animated.View>;
|
|
21
|
+
*
|
|
22
|
+
* // Mount/unmount animations
|
|
23
|
+
* const { isPresent, style } = usePresence(isOpen, {
|
|
24
|
+
* enter: { opacity: 1 },
|
|
25
|
+
* exit: { opacity: 0 },
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Gradient borders
|
|
29
|
+
* const { containerStyle, contentStyle, GradientBorder } = useGradientBorder({
|
|
30
|
+
* colors: ['#3b82f6', '#8b5cf6'],
|
|
31
|
+
* animation: 'spin',
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
// Type exports
|
|
37
|
+
export type {
|
|
38
|
+
AnimatableStyle,
|
|
39
|
+
AnimatableProperties,
|
|
40
|
+
TransformProperty,
|
|
41
|
+
AnimationOptions,
|
|
42
|
+
PlatformOverrides,
|
|
43
|
+
UseAnimatedStyleOptions,
|
|
44
|
+
AnimatedValue,
|
|
45
|
+
InterpolationConfig,
|
|
46
|
+
SequenceStep,
|
|
47
|
+
UseSequenceResult,
|
|
48
|
+
KeyframePercentage,
|
|
49
|
+
KeyframeDefinition,
|
|
50
|
+
UseKeyframesOptions,
|
|
51
|
+
UseKeyframesResult,
|
|
52
|
+
UsePresenceOptions,
|
|
53
|
+
UsePresenceResult,
|
|
54
|
+
GradientAnimation,
|
|
55
|
+
UseGradientBorderOptions,
|
|
56
|
+
UseGradientBorderResult,
|
|
57
|
+
} from './types';
|
|
58
|
+
|
|
59
|
+
// Hook exports (native implementations)
|
|
60
|
+
export { useAnimatedStyle } from './useAnimatedStyle.native';
|
|
61
|
+
export { useAnimatedValue } from './useAnimatedValue.native';
|
|
62
|
+
export { usePresence } from './usePresence.native';
|
|
63
|
+
export { useGradientBorder } from './useGradientBorder.native';
|
|
64
|
+
|
|
65
|
+
// Re-export animation tokens for convenience
|
|
66
|
+
export { durations, easings, presets } from '@idealyst/theme/animation';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @idealyst/animate - Web exports
|
|
3
|
+
*
|
|
4
|
+
* Cross-platform animation hooks for React and React Native.
|
|
5
|
+
* This module provides a unified API for creating smooth animations
|
|
6
|
+
* that work on both web and native platforms.
|
|
7
|
+
*
|
|
8
|
+
* On web, animations use CSS transitions and requestAnimationFrame
|
|
9
|
+
* for optimal performance.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* import { useAnimatedStyle, usePresence, useGradientBorder } from '@idealyst/animate';
|
|
14
|
+
*
|
|
15
|
+
* // State-driven animations
|
|
16
|
+
* const style = useAnimatedStyle({
|
|
17
|
+
* opacity: isVisible ? 1 : 0,
|
|
18
|
+
* }, { duration: 'normal', easing: 'easeOut' });
|
|
19
|
+
*
|
|
20
|
+
* // Mount/unmount animations
|
|
21
|
+
* const { isPresent, style } = usePresence(isOpen, {
|
|
22
|
+
* enter: { opacity: 1 },
|
|
23
|
+
* exit: { opacity: 0 },
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // Gradient borders
|
|
27
|
+
* const { containerStyle, contentStyle } = useGradientBorder({
|
|
28
|
+
* colors: ['#3b82f6', '#8b5cf6'],
|
|
29
|
+
* animation: 'spin',
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
// Type exports
|
|
35
|
+
export type {
|
|
36
|
+
AnimatableStyle,
|
|
37
|
+
AnimatableProperties,
|
|
38
|
+
TransformProperty,
|
|
39
|
+
AnimationOptions,
|
|
40
|
+
PlatformOverrides,
|
|
41
|
+
UseAnimatedStyleOptions,
|
|
42
|
+
AnimatedValue,
|
|
43
|
+
InterpolationConfig,
|
|
44
|
+
SequenceStep,
|
|
45
|
+
UseSequenceResult,
|
|
46
|
+
KeyframePercentage,
|
|
47
|
+
KeyframeDefinition,
|
|
48
|
+
UseKeyframesOptions,
|
|
49
|
+
UseKeyframesResult,
|
|
50
|
+
UsePresenceOptions,
|
|
51
|
+
UsePresenceResult,
|
|
52
|
+
GradientAnimation,
|
|
53
|
+
UseGradientBorderOptions,
|
|
54
|
+
UseGradientBorderResult,
|
|
55
|
+
} from './types';
|
|
56
|
+
|
|
57
|
+
// Hook exports
|
|
58
|
+
export { useAnimatedStyle } from './useAnimatedStyle';
|
|
59
|
+
export { useAnimatedValue } from './useAnimatedValue';
|
|
60
|
+
export { usePresence } from './usePresence';
|
|
61
|
+
export { useGradientBorder, createGradientBorderStyle } from './useGradientBorder';
|
|
62
|
+
|
|
63
|
+
// Re-export animation tokens for convenience
|
|
64
|
+
export { durations, easings, presets } from '@idealyst/theme/animation';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for @idealyst/animate
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ViewStyle, TextStyle, ImageStyle } from 'react-native';
|
|
6
|
+
import type { Duration, EasingKey, SpringType } from '@idealyst/theme/animation';
|
|
7
|
+
|
|
8
|
+
// Style types
|
|
9
|
+
export type AnimatableStyle = ViewStyle | TextStyle | ImageStyle;
|
|
10
|
+
export type StyleValue = string | number | undefined;
|
|
11
|
+
|
|
12
|
+
// Transform types (React Native style)
|
|
13
|
+
export type TransformProperty =
|
|
14
|
+
| { perspective: number }
|
|
15
|
+
| { rotate: string }
|
|
16
|
+
| { rotateX: string }
|
|
17
|
+
| { rotateY: string }
|
|
18
|
+
| { rotateZ: string }
|
|
19
|
+
| { scale: number }
|
|
20
|
+
| { scaleX: number }
|
|
21
|
+
| { scaleY: number }
|
|
22
|
+
| { translateX: number }
|
|
23
|
+
| { translateY: number }
|
|
24
|
+
| { skewX: string }
|
|
25
|
+
| { skewY: string };
|
|
26
|
+
|
|
27
|
+
// Animatable properties (subset that can be animated smoothly)
|
|
28
|
+
export interface AnimatableProperties {
|
|
29
|
+
// Opacity
|
|
30
|
+
opacity?: number;
|
|
31
|
+
|
|
32
|
+
// Background
|
|
33
|
+
backgroundColor?: string;
|
|
34
|
+
|
|
35
|
+
// Border
|
|
36
|
+
borderColor?: string;
|
|
37
|
+
borderWidth?: number;
|
|
38
|
+
borderRadius?: number;
|
|
39
|
+
borderTopLeftRadius?: number;
|
|
40
|
+
borderTopRightRadius?: number;
|
|
41
|
+
borderBottomLeftRadius?: number;
|
|
42
|
+
borderBottomRightRadius?: number;
|
|
43
|
+
|
|
44
|
+
// Dimensions (use sparingly - triggers layout)
|
|
45
|
+
width?: number | string;
|
|
46
|
+
height?: number | string;
|
|
47
|
+
maxHeight?: number | string;
|
|
48
|
+
maxWidth?: number | string;
|
|
49
|
+
minHeight?: number | string;
|
|
50
|
+
minWidth?: number | string;
|
|
51
|
+
|
|
52
|
+
// Positioning (use sparingly - triggers layout)
|
|
53
|
+
top?: number | string;
|
|
54
|
+
right?: number | string;
|
|
55
|
+
bottom?: number | string;
|
|
56
|
+
left?: number | string;
|
|
57
|
+
|
|
58
|
+
// Transform (preferred for performance)
|
|
59
|
+
transform?: TransformProperty[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Animation configuration
|
|
63
|
+
export interface AnimationOptions {
|
|
64
|
+
/** Duration in ms or token key */
|
|
65
|
+
duration?: Duration;
|
|
66
|
+
/** Easing function key */
|
|
67
|
+
easing?: EasingKey;
|
|
68
|
+
/** Delay before animation starts (ms) */
|
|
69
|
+
delay?: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Platform-specific overrides
|
|
73
|
+
export interface PlatformOverrides {
|
|
74
|
+
/** Web-specific options */
|
|
75
|
+
web?: AnimationOptions & {
|
|
76
|
+
/** Custom CSS transition string */
|
|
77
|
+
transition?: string;
|
|
78
|
+
};
|
|
79
|
+
/** Native-specific options */
|
|
80
|
+
native?: AnimationOptions & {
|
|
81
|
+
/** Use spring animation instead of timing */
|
|
82
|
+
useSpring?: boolean;
|
|
83
|
+
/** Spring type if useSpring is true */
|
|
84
|
+
springType?: SpringType;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// useAnimatedStyle options
|
|
89
|
+
export interface UseAnimatedStyleOptions extends AnimationOptions, PlatformOverrides {}
|
|
90
|
+
|
|
91
|
+
// useAnimatedValue return type
|
|
92
|
+
export interface AnimatedValue {
|
|
93
|
+
/** Current value (for reading) */
|
|
94
|
+
readonly value: number;
|
|
95
|
+
/** Set value with animation */
|
|
96
|
+
set: (target: number, options?: AnimationOptions) => void;
|
|
97
|
+
/** Set value immediately without animation */
|
|
98
|
+
setImmediate: (target: number) => void;
|
|
99
|
+
/** Interpolate value to another range */
|
|
100
|
+
interpolate: <T extends string | number>(config: InterpolationConfig<T>) => T;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface InterpolationConfig<T> {
|
|
104
|
+
inputRange: number[];
|
|
105
|
+
outputRange: T[];
|
|
106
|
+
extrapolate?: 'extend' | 'clamp' | 'identity';
|
|
107
|
+
extrapolateLeft?: 'extend' | 'clamp' | 'identity';
|
|
108
|
+
extrapolateRight?: 'extend' | 'clamp' | 'identity';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// useSequence types
|
|
112
|
+
export interface SequenceStep {
|
|
113
|
+
/** Style properties for this step */
|
|
114
|
+
style?: AnimatableProperties;
|
|
115
|
+
/** Duration for this step */
|
|
116
|
+
duration?: Duration;
|
|
117
|
+
/** Easing for this step */
|
|
118
|
+
easing?: EasingKey;
|
|
119
|
+
/** Delay before this step */
|
|
120
|
+
delay?: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface UseSequenceResult {
|
|
124
|
+
/** Current animated style */
|
|
125
|
+
style: AnimatableStyle;
|
|
126
|
+
/** Start/restart the sequence */
|
|
127
|
+
play: () => void;
|
|
128
|
+
/** Reset to initial state */
|
|
129
|
+
reset: () => void;
|
|
130
|
+
/** Pause animation (web only) */
|
|
131
|
+
pause: () => void;
|
|
132
|
+
/** Whether animation is currently playing */
|
|
133
|
+
isPlaying: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// useKeyframes types
|
|
137
|
+
export type KeyframePercentage = `${number}%` | 'from' | 'to';
|
|
138
|
+
export type KeyframeDefinition = Partial<Record<KeyframePercentage, AnimatableProperties>>;
|
|
139
|
+
|
|
140
|
+
export interface UseKeyframesOptions extends AnimationOptions {
|
|
141
|
+
/** Number of iterations (default: 1) */
|
|
142
|
+
iterations?: number | 'infinite';
|
|
143
|
+
/** Animation direction */
|
|
144
|
+
direction?: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';
|
|
145
|
+
/** Fill mode */
|
|
146
|
+
fillMode?: 'none' | 'forwards' | 'backwards' | 'both';
|
|
147
|
+
/** Auto-play on mount */
|
|
148
|
+
autoPlay?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface UseKeyframesResult {
|
|
152
|
+
/** Animated style to apply */
|
|
153
|
+
style: AnimatableStyle;
|
|
154
|
+
/** Start animation */
|
|
155
|
+
play: () => void;
|
|
156
|
+
/** Pause animation */
|
|
157
|
+
pause: () => void;
|
|
158
|
+
/** Reset to initial state */
|
|
159
|
+
reset: () => void;
|
|
160
|
+
/** Whether animation is playing */
|
|
161
|
+
isPlaying: boolean;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// usePresence types
|
|
165
|
+
export interface UsePresenceOptions extends AnimationOptions {
|
|
166
|
+
/** Style when entering/visible */
|
|
167
|
+
enter: AnimatableProperties;
|
|
168
|
+
/** Style when exiting/hidden */
|
|
169
|
+
exit: AnimatableProperties;
|
|
170
|
+
/** Initial style (defaults to exit) */
|
|
171
|
+
initial?: AnimatableProperties;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface UsePresenceResult {
|
|
175
|
+
/** Whether element should be rendered */
|
|
176
|
+
isPresent: boolean;
|
|
177
|
+
/** Animated style to apply */
|
|
178
|
+
style: AnimatableStyle;
|
|
179
|
+
/** Trigger exit animation manually */
|
|
180
|
+
exit: () => void;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// useGradientBorder types
|
|
184
|
+
export type GradientAnimation = 'spin' | 'pulse' | 'wave';
|
|
185
|
+
|
|
186
|
+
export interface UseGradientBorderOptions {
|
|
187
|
+
/** Gradient colors */
|
|
188
|
+
colors: string[];
|
|
189
|
+
/** Border width in pixels */
|
|
190
|
+
borderWidth?: number;
|
|
191
|
+
/** Border radius in pixels */
|
|
192
|
+
borderRadius?: number;
|
|
193
|
+
/** Animation duration in ms or token */
|
|
194
|
+
duration?: Duration;
|
|
195
|
+
/** Animation type */
|
|
196
|
+
animation?: GradientAnimation;
|
|
197
|
+
/** Whether animation is active */
|
|
198
|
+
active?: boolean;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface UseGradientBorderResult {
|
|
202
|
+
/** Style for the outer container */
|
|
203
|
+
containerStyle: AnimatableStyle;
|
|
204
|
+
/** Style for the inner content wrapper */
|
|
205
|
+
contentStyle: AnimatableStyle;
|
|
206
|
+
/** Whether gradient CSS has been injected (web only) */
|
|
207
|
+
isReady: boolean;
|
|
208
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAnimatedStyle - Native implementation
|
|
3
|
+
*
|
|
4
|
+
* Creates an animated style using Reanimated.
|
|
5
|
+
* Uses either timing or spring animations based on configuration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useEffect, useMemo } from 'react';
|
|
9
|
+
import {
|
|
10
|
+
useSharedValue,
|
|
11
|
+
useAnimatedStyle as useReanimatedStyle,
|
|
12
|
+
withTiming,
|
|
13
|
+
withSpring,
|
|
14
|
+
withDelay,
|
|
15
|
+
Easing,
|
|
16
|
+
} from 'react-native-reanimated';
|
|
17
|
+
import { timingConfig, springConfig, isSpringEasing } from '@idealyst/theme/animation';
|
|
18
|
+
import type { AnimatableProperties, UseAnimatedStyleOptions, AnimatableStyle } from './types';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Hook that returns an animated style object.
|
|
22
|
+
* When the style properties change, they animate smoothly using Reanimated.
|
|
23
|
+
*
|
|
24
|
+
* @param style - The target style properties
|
|
25
|
+
* @param options - Animation configuration
|
|
26
|
+
* @returns Animated style object to spread onto an Animated component
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* import Animated from 'react-native-reanimated';
|
|
31
|
+
*
|
|
32
|
+
* const style = useAnimatedStyle({
|
|
33
|
+
* opacity: isVisible ? 1 : 0,
|
|
34
|
+
* transform: [{ translateY: isVisible ? 0 : 20 }],
|
|
35
|
+
* }, {
|
|
36
|
+
* duration: 'normal',
|
|
37
|
+
* easing: 'easeOut',
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* return <Animated.View style={style}>Content</Animated.View>;
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export function useAnimatedStyle(
|
|
44
|
+
style: AnimatableProperties,
|
|
45
|
+
options: UseAnimatedStyleOptions = {}
|
|
46
|
+
): AnimatableStyle {
|
|
47
|
+
const { duration = 'normal', easing = 'easeOut', delay = 0, native } = options;
|
|
48
|
+
|
|
49
|
+
// Use native-specific options if provided
|
|
50
|
+
const finalDuration = native?.duration ?? duration;
|
|
51
|
+
const finalEasing = native?.easing ?? easing;
|
|
52
|
+
const finalDelay = native?.delay ?? delay;
|
|
53
|
+
const useSpring = native?.useSpring ?? isSpringEasing(finalEasing);
|
|
54
|
+
const springType = native?.springType ?? (isSpringEasing(finalEasing) ? finalEasing : 'spring');
|
|
55
|
+
|
|
56
|
+
// Create shared values for each animatable property
|
|
57
|
+
const opacity = useSharedValue(style.opacity ?? 1);
|
|
58
|
+
const backgroundColor = useSharedValue(style.backgroundColor ?? 'transparent');
|
|
59
|
+
const borderColor = useSharedValue(style.borderColor ?? 'transparent');
|
|
60
|
+
const borderWidth = useSharedValue(style.borderWidth ?? 0);
|
|
61
|
+
const borderRadius = useSharedValue(style.borderRadius ?? 0);
|
|
62
|
+
|
|
63
|
+
// Transform values
|
|
64
|
+
const translateX = useSharedValue(0);
|
|
65
|
+
const translateY = useSharedValue(0);
|
|
66
|
+
const scale = useSharedValue(1);
|
|
67
|
+
const scaleX = useSharedValue(1);
|
|
68
|
+
const scaleY = useSharedValue(1);
|
|
69
|
+
const rotate = useSharedValue('0deg');
|
|
70
|
+
|
|
71
|
+
// Extract transform values from style
|
|
72
|
+
const transforms = useMemo(() => {
|
|
73
|
+
const result = {
|
|
74
|
+
translateX: 0,
|
|
75
|
+
translateY: 0,
|
|
76
|
+
scale: 1,
|
|
77
|
+
scaleX: 1,
|
|
78
|
+
scaleY: 1,
|
|
79
|
+
rotate: '0deg',
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (style.transform) {
|
|
83
|
+
style.transform.forEach((t) => {
|
|
84
|
+
const [key, value] = Object.entries(t)[0];
|
|
85
|
+
if (key in result) {
|
|
86
|
+
(result as any)[key] = value;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return result;
|
|
92
|
+
}, [style.transform]);
|
|
93
|
+
|
|
94
|
+
// Animate function
|
|
95
|
+
const animate = (sharedValue: any, targetValue: any, isString = false) => {
|
|
96
|
+
'worklet';
|
|
97
|
+
if (useSpring && !isString) {
|
|
98
|
+
const config = springConfig(springType as any);
|
|
99
|
+
return finalDelay > 0
|
|
100
|
+
? withDelay(finalDelay, withSpring(targetValue, config))
|
|
101
|
+
: withSpring(targetValue, config);
|
|
102
|
+
} else {
|
|
103
|
+
const config = timingConfig(finalDuration, finalEasing);
|
|
104
|
+
const timingOptions = {
|
|
105
|
+
duration: config.duration,
|
|
106
|
+
easing: Easing.bezier(config.easing[0], config.easing[1], config.easing[2], config.easing[3]),
|
|
107
|
+
};
|
|
108
|
+
return finalDelay > 0
|
|
109
|
+
? withDelay(finalDelay, withTiming(targetValue, timingOptions))
|
|
110
|
+
: withTiming(targetValue, timingOptions);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Update shared values when style changes
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (style.opacity !== undefined) {
|
|
117
|
+
opacity.value = animate(opacity, style.opacity);
|
|
118
|
+
}
|
|
119
|
+
if (style.backgroundColor !== undefined) {
|
|
120
|
+
backgroundColor.value = animate(backgroundColor, style.backgroundColor, true);
|
|
121
|
+
}
|
|
122
|
+
if (style.borderColor !== undefined) {
|
|
123
|
+
borderColor.value = animate(borderColor, style.borderColor, true);
|
|
124
|
+
}
|
|
125
|
+
if (style.borderWidth !== undefined) {
|
|
126
|
+
borderWidth.value = animate(borderWidth, style.borderWidth);
|
|
127
|
+
}
|
|
128
|
+
if (style.borderRadius !== undefined) {
|
|
129
|
+
borderRadius.value = animate(borderRadius, style.borderRadius);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Update transform values
|
|
133
|
+
translateX.value = animate(translateX, transforms.translateX);
|
|
134
|
+
translateY.value = animate(translateY, transforms.translateY);
|
|
135
|
+
scale.value = animate(scale, transforms.scale);
|
|
136
|
+
scaleX.value = animate(scaleX, transforms.scaleX);
|
|
137
|
+
scaleY.value = animate(scaleY, transforms.scaleY);
|
|
138
|
+
rotate.value = animate(rotate, transforms.rotate, true);
|
|
139
|
+
}, [
|
|
140
|
+
style.opacity,
|
|
141
|
+
style.backgroundColor,
|
|
142
|
+
style.borderColor,
|
|
143
|
+
style.borderWidth,
|
|
144
|
+
style.borderRadius,
|
|
145
|
+
transforms,
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
// Create animated style
|
|
149
|
+
const animatedStyle = useReanimatedStyle(() => {
|
|
150
|
+
return {
|
|
151
|
+
opacity: opacity.value,
|
|
152
|
+
backgroundColor: backgroundColor.value,
|
|
153
|
+
borderColor: borderColor.value,
|
|
154
|
+
borderWidth: borderWidth.value,
|
|
155
|
+
borderRadius: borderRadius.value,
|
|
156
|
+
transform: [
|
|
157
|
+
{ translateX: translateX.value },
|
|
158
|
+
{ translateY: translateY.value },
|
|
159
|
+
{ scale: scale.value },
|
|
160
|
+
{ scaleX: scaleX.value },
|
|
161
|
+
{ scaleY: scaleY.value },
|
|
162
|
+
{ rotate: rotate.value },
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return animatedStyle as AnimatableStyle;
|
|
168
|
+
}
|