react-native-ease 0.1.0-alpha.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/Ease.podspec +20 -0
- package/LICENSE +20 -0
- package/README.md +411 -0
- package/android/build.gradle +68 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/ease/EasePackage.kt +17 -0
- package/android/src/main/java/com/ease/EaseView.kt +541 -0
- package/android/src/main/java/com/ease/EaseViewManager.kt +233 -0
- package/ios/EaseView.h +14 -0
- package/ios/EaseView.mm +435 -0
- package/lib/module/EaseView.js +186 -0
- package/lib/module/EaseView.js.map +1 -0
- package/lib/module/EaseViewNativeComponent.ts +68 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/EaseView.d.ts +33 -0
- package/lib/typescript/src/EaseView.d.ts.map +1 -0
- package/lib/typescript/src/EaseViewNativeComponent.d.ts +38 -0
- package/lib/typescript/src/EaseViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +66 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +187 -0
- package/src/EaseView.tsx +256 -0
- package/src/EaseViewNativeComponent.ts +68 -0
- package/src/index.tsx +13 -0
- package/src/types.ts +78 -0
package/src/EaseView.tsx
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { StyleSheet, type ViewProps, type ViewStyle } from 'react-native';
|
|
2
|
+
import NativeEaseView from './EaseViewNativeComponent';
|
|
3
|
+
import type {
|
|
4
|
+
AnimateProps,
|
|
5
|
+
CubicBezier,
|
|
6
|
+
Transition,
|
|
7
|
+
TransitionEndEvent,
|
|
8
|
+
TransformOrigin,
|
|
9
|
+
} from './types';
|
|
10
|
+
|
|
11
|
+
/** Identity values used as defaults for animate/initialAnimate. */
|
|
12
|
+
const IDENTITY = {
|
|
13
|
+
opacity: 1,
|
|
14
|
+
translateX: 0,
|
|
15
|
+
translateY: 0,
|
|
16
|
+
scaleX: 1,
|
|
17
|
+
scaleY: 1,
|
|
18
|
+
rotate: 0,
|
|
19
|
+
rotateX: 0,
|
|
20
|
+
rotateY: 0,
|
|
21
|
+
borderRadius: 0,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** Bitmask flags — must match native constants. */
|
|
25
|
+
/* eslint-disable no-bitwise */
|
|
26
|
+
const MASK_OPACITY = 1 << 0;
|
|
27
|
+
const MASK_TRANSLATE_X = 1 << 1;
|
|
28
|
+
const MASK_TRANSLATE_Y = 1 << 2;
|
|
29
|
+
const MASK_SCALE_X = 1 << 3;
|
|
30
|
+
const MASK_SCALE_Y = 1 << 4;
|
|
31
|
+
const MASK_ROTATE = 1 << 5;
|
|
32
|
+
const MASK_ROTATE_X = 1 << 6;
|
|
33
|
+
const MASK_ROTATE_Y = 1 << 7;
|
|
34
|
+
const MASK_BORDER_RADIUS = 1 << 8;
|
|
35
|
+
/* eslint-enable no-bitwise */
|
|
36
|
+
|
|
37
|
+
/** Maps animate prop keys to style keys that conflict. */
|
|
38
|
+
const ANIMATE_TO_STYLE_KEYS: Record<keyof AnimateProps, string> = {
|
|
39
|
+
opacity: 'opacity',
|
|
40
|
+
translateX: 'transform',
|
|
41
|
+
translateY: 'transform',
|
|
42
|
+
scale: 'transform',
|
|
43
|
+
scaleX: 'transform',
|
|
44
|
+
scaleY: 'transform',
|
|
45
|
+
rotate: 'transform',
|
|
46
|
+
rotateX: 'transform',
|
|
47
|
+
rotateY: 'transform',
|
|
48
|
+
borderRadius: 'borderRadius',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Preset easing curves as cubic bezier control points. */
|
|
52
|
+
const EASING_PRESETS: Record<string, CubicBezier> = {
|
|
53
|
+
linear: [0, 0, 1, 1],
|
|
54
|
+
easeIn: [0.42, 0, 1, 1],
|
|
55
|
+
easeOut: [0, 0, 0.58, 1],
|
|
56
|
+
easeInOut: [0.42, 0, 0.58, 1],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type EaseViewProps = ViewProps & {
|
|
60
|
+
/** Target values for animated properties. */
|
|
61
|
+
animate?: AnimateProps;
|
|
62
|
+
/** Starting values for enter animations. Animates to `animate` on mount. */
|
|
63
|
+
initialAnimate?: AnimateProps;
|
|
64
|
+
/** Animation configuration (timing or spring). */
|
|
65
|
+
transition?: Transition;
|
|
66
|
+
/** Called when all animations complete. Reports whether they finished naturally or were interrupted. */
|
|
67
|
+
onTransitionEnd?: (event: TransitionEndEvent) => void;
|
|
68
|
+
/**
|
|
69
|
+
* Enable Android hardware layer during animations. The view is rasterized to
|
|
70
|
+
* a GPU texture so animated property changes (opacity, scale, rotation) are
|
|
71
|
+
* composited on the RenderThread without redrawing the view hierarchy.
|
|
72
|
+
*
|
|
73
|
+
* **Trade-offs:**
|
|
74
|
+
* - Faster rendering of opacity/scale/rotation animations.
|
|
75
|
+
* - Uses additional GPU memory for the off-screen texture.
|
|
76
|
+
* - Children that overflow the view's layout bounds are clipped by the
|
|
77
|
+
* texture, which can cause visual artifacts with `translateX`/`translateY`.
|
|
78
|
+
*
|
|
79
|
+
* Best suited for views that animate opacity, scale, or rotation without
|
|
80
|
+
* overflowing children. No-op on iOS where Core Animation already composites
|
|
81
|
+
* off the main thread.
|
|
82
|
+
* @default false
|
|
83
|
+
*/
|
|
84
|
+
useHardwareLayer?: boolean;
|
|
85
|
+
/** Pivot point for scale and rotation as 0–1 fractions. @default { x: 0.5, y: 0.5 } (center) */
|
|
86
|
+
transformOrigin?: TransformOrigin;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export function EaseView({
|
|
90
|
+
animate,
|
|
91
|
+
initialAnimate,
|
|
92
|
+
transition,
|
|
93
|
+
onTransitionEnd,
|
|
94
|
+
useHardwareLayer = false,
|
|
95
|
+
transformOrigin,
|
|
96
|
+
style,
|
|
97
|
+
...rest
|
|
98
|
+
}: EaseViewProps) {
|
|
99
|
+
// Compute bitmask of which properties are animated.
|
|
100
|
+
// Native uses this to skip non-animated properties (lets style handle them).
|
|
101
|
+
/* eslint-disable no-bitwise */
|
|
102
|
+
let animatedProperties = 0;
|
|
103
|
+
if (animate?.opacity != null) animatedProperties |= MASK_OPACITY;
|
|
104
|
+
if (animate?.translateX != null) animatedProperties |= MASK_TRANSLATE_X;
|
|
105
|
+
if (animate?.translateY != null) animatedProperties |= MASK_TRANSLATE_Y;
|
|
106
|
+
if (animate?.scaleX != null || animate?.scale != null)
|
|
107
|
+
animatedProperties |= MASK_SCALE_X;
|
|
108
|
+
if (animate?.scaleY != null || animate?.scale != null)
|
|
109
|
+
animatedProperties |= MASK_SCALE_Y;
|
|
110
|
+
if (animate?.rotate != null) animatedProperties |= MASK_ROTATE;
|
|
111
|
+
if (animate?.rotateX != null) animatedProperties |= MASK_ROTATE_X;
|
|
112
|
+
if (animate?.rotateY != null) animatedProperties |= MASK_ROTATE_Y;
|
|
113
|
+
if (animate?.borderRadius != null) animatedProperties |= MASK_BORDER_RADIUS;
|
|
114
|
+
/* eslint-enable no-bitwise */
|
|
115
|
+
|
|
116
|
+
// Resolve animate values (identity defaults for non-animated — safe values).
|
|
117
|
+
const resolved = {
|
|
118
|
+
...IDENTITY,
|
|
119
|
+
...animate,
|
|
120
|
+
scaleX: animate?.scaleX ?? animate?.scale ?? IDENTITY.scaleX,
|
|
121
|
+
scaleY: animate?.scaleY ?? animate?.scale ?? IDENTITY.scaleY,
|
|
122
|
+
rotateX: animate?.rotateX ?? IDENTITY.rotateX,
|
|
123
|
+
rotateY: animate?.rotateY ?? IDENTITY.rotateY,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Resolve initialAnimate:
|
|
127
|
+
// - No initialAnimate: same as resolved (no enter animation)
|
|
128
|
+
// - With initialAnimate: use initial values for animated properties,
|
|
129
|
+
// falling back to identity defaults.
|
|
130
|
+
const initial = initialAnimate ?? animate;
|
|
131
|
+
const resolvedInitial = {
|
|
132
|
+
...IDENTITY,
|
|
133
|
+
...initial,
|
|
134
|
+
scaleX: initial?.scaleX ?? initial?.scale ?? IDENTITY.scaleX,
|
|
135
|
+
scaleY: initial?.scaleY ?? initial?.scale ?? IDENTITY.scaleY,
|
|
136
|
+
rotateX: initial?.rotateX ?? IDENTITY.rotateX,
|
|
137
|
+
rotateY: initial?.rotateY ?? IDENTITY.rotateY,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Strip style keys that conflict with animated properties
|
|
141
|
+
let cleanStyle: ViewProps['style'] = style;
|
|
142
|
+
if (animate && style) {
|
|
143
|
+
const flat = StyleSheet.flatten(style) as Record<string, unknown>;
|
|
144
|
+
if (flat) {
|
|
145
|
+
const conflicting = new Set<string>();
|
|
146
|
+
for (const key of Object.keys(animate) as (keyof AnimateProps)[]) {
|
|
147
|
+
if (animate[key] != null) {
|
|
148
|
+
const styleKey = ANIMATE_TO_STYLE_KEYS[key];
|
|
149
|
+
if (styleKey && styleKey in flat) {
|
|
150
|
+
conflicting.add(styleKey);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (conflicting.size > 0) {
|
|
155
|
+
if (__DEV__) {
|
|
156
|
+
console.warn(
|
|
157
|
+
`react-native-ease: ${[...conflicting].join(
|
|
158
|
+
', ',
|
|
159
|
+
)} found in both style and animate. ` +
|
|
160
|
+
'The animated value takes priority; the style value will be ignored.',
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
const cleaned: Record<string, unknown> = {};
|
|
164
|
+
for (const [k, v] of Object.entries(flat)) {
|
|
165
|
+
if (!conflicting.has(k)) {
|
|
166
|
+
cleaned[k] = v;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
cleanStyle = cleaned as ViewStyle;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Resolve transition config
|
|
175
|
+
const transitionType = transition?.type ?? 'timing';
|
|
176
|
+
const transitionDuration =
|
|
177
|
+
transition?.type === 'timing' ? transition.duration ?? 300 : 300;
|
|
178
|
+
const rawEasing =
|
|
179
|
+
transition?.type === 'timing'
|
|
180
|
+
? transition.easing ?? 'easeInOut'
|
|
181
|
+
: 'easeInOut';
|
|
182
|
+
if (__DEV__) {
|
|
183
|
+
if (Array.isArray(rawEasing)) {
|
|
184
|
+
if ((rawEasing as number[]).length !== 4) {
|
|
185
|
+
console.warn(
|
|
186
|
+
'react-native-ease: Custom easing must be a [x1, y1, x2, y2] tuple (got length ' +
|
|
187
|
+
(rawEasing as number[]).length +
|
|
188
|
+
').',
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
if (
|
|
192
|
+
rawEasing[0] < 0 ||
|
|
193
|
+
rawEasing[0] > 1 ||
|
|
194
|
+
rawEasing[2] < 0 ||
|
|
195
|
+
rawEasing[2] > 1
|
|
196
|
+
) {
|
|
197
|
+
console.warn(
|
|
198
|
+
'react-native-ease: Easing x-values (x1, x2) must be between 0 and 1.',
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const bezier: CubicBezier = Array.isArray(rawEasing)
|
|
204
|
+
? rawEasing
|
|
205
|
+
: EASING_PRESETS[rawEasing]!;
|
|
206
|
+
const transitionDamping =
|
|
207
|
+
transition?.type === 'spring' ? transition.damping ?? 15 : 15;
|
|
208
|
+
const transitionStiffness =
|
|
209
|
+
transition?.type === 'spring' ? transition.stiffness ?? 120 : 120;
|
|
210
|
+
const transitionMass =
|
|
211
|
+
transition?.type === 'spring' ? transition.mass ?? 1 : 1;
|
|
212
|
+
const transitionLoop =
|
|
213
|
+
transition?.type === 'timing' ? transition.loop ?? 'none' : 'none';
|
|
214
|
+
|
|
215
|
+
const handleTransitionEnd = onTransitionEnd
|
|
216
|
+
? (event: { nativeEvent: { finished: boolean } }) =>
|
|
217
|
+
onTransitionEnd(event.nativeEvent)
|
|
218
|
+
: undefined;
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<NativeEaseView
|
|
222
|
+
style={cleanStyle}
|
|
223
|
+
onTransitionEnd={handleTransitionEnd}
|
|
224
|
+
animatedProperties={animatedProperties}
|
|
225
|
+
animateOpacity={resolved.opacity}
|
|
226
|
+
animateTranslateX={resolved.translateX}
|
|
227
|
+
animateTranslateY={resolved.translateY}
|
|
228
|
+
animateScaleX={resolved.scaleX}
|
|
229
|
+
animateScaleY={resolved.scaleY}
|
|
230
|
+
animateRotate={resolved.rotate}
|
|
231
|
+
animateRotateX={resolved.rotateX}
|
|
232
|
+
animateRotateY={resolved.rotateY}
|
|
233
|
+
animateBorderRadius={resolved.borderRadius}
|
|
234
|
+
initialAnimateOpacity={resolvedInitial.opacity}
|
|
235
|
+
initialAnimateTranslateX={resolvedInitial.translateX}
|
|
236
|
+
initialAnimateTranslateY={resolvedInitial.translateY}
|
|
237
|
+
initialAnimateScaleX={resolvedInitial.scaleX}
|
|
238
|
+
initialAnimateScaleY={resolvedInitial.scaleY}
|
|
239
|
+
initialAnimateRotate={resolvedInitial.rotate}
|
|
240
|
+
initialAnimateRotateX={resolvedInitial.rotateX}
|
|
241
|
+
initialAnimateRotateY={resolvedInitial.rotateY}
|
|
242
|
+
initialAnimateBorderRadius={resolvedInitial.borderRadius}
|
|
243
|
+
transitionType={transitionType}
|
|
244
|
+
transitionDuration={transitionDuration}
|
|
245
|
+
transitionEasingBezier={bezier}
|
|
246
|
+
transitionDamping={transitionDamping}
|
|
247
|
+
transitionStiffness={transitionStiffness}
|
|
248
|
+
transitionMass={transitionMass}
|
|
249
|
+
transitionLoop={transitionLoop}
|
|
250
|
+
useHardwareLayer={useHardwareLayer}
|
|
251
|
+
transformOriginX={transformOrigin?.x ?? 0.5}
|
|
252
|
+
transformOriginY={transformOrigin?.y ?? 0.5}
|
|
253
|
+
{...rest}
|
|
254
|
+
/>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
codegenNativeComponent,
|
|
3
|
+
type CodegenTypes,
|
|
4
|
+
type ViewProps,
|
|
5
|
+
type HostComponent,
|
|
6
|
+
} from 'react-native';
|
|
7
|
+
|
|
8
|
+
export interface NativeProps extends ViewProps {
|
|
9
|
+
// Bitmask of which properties are animated (0 = none, let style handle all)
|
|
10
|
+
animatedProperties?: CodegenTypes.WithDefault<CodegenTypes.Int32, 0>;
|
|
11
|
+
|
|
12
|
+
// Animate target values
|
|
13
|
+
animateOpacity?: CodegenTypes.WithDefault<CodegenTypes.Float, 1.0>;
|
|
14
|
+
animateTranslateX?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
15
|
+
animateTranslateY?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
16
|
+
animateScaleX?: CodegenTypes.WithDefault<CodegenTypes.Float, 1.0>;
|
|
17
|
+
animateScaleY?: CodegenTypes.WithDefault<CodegenTypes.Float, 1.0>;
|
|
18
|
+
animateRotate?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
19
|
+
animateRotateX?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
20
|
+
animateRotateY?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
21
|
+
animateBorderRadius?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
22
|
+
|
|
23
|
+
// Initial values for enter animations
|
|
24
|
+
initialAnimateOpacity?: CodegenTypes.WithDefault<CodegenTypes.Float, 1.0>;
|
|
25
|
+
initialAnimateTranslateX?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
26
|
+
initialAnimateTranslateY?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
27
|
+
initialAnimateScaleX?: CodegenTypes.WithDefault<CodegenTypes.Float, 1.0>;
|
|
28
|
+
initialAnimateScaleY?: CodegenTypes.WithDefault<CodegenTypes.Float, 1.0>;
|
|
29
|
+
initialAnimateRotate?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
30
|
+
initialAnimateRotateX?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
31
|
+
initialAnimateRotateY?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.0>;
|
|
32
|
+
initialAnimateBorderRadius?: CodegenTypes.WithDefault<
|
|
33
|
+
CodegenTypes.Float,
|
|
34
|
+
0.0
|
|
35
|
+
>;
|
|
36
|
+
|
|
37
|
+
// Transition config
|
|
38
|
+
transitionType?: CodegenTypes.WithDefault<
|
|
39
|
+
'timing' | 'spring' | 'none',
|
|
40
|
+
'timing'
|
|
41
|
+
>;
|
|
42
|
+
transitionDuration?: CodegenTypes.WithDefault<CodegenTypes.Int32, 300>;
|
|
43
|
+
// Easing cubic bezier control points [x1, y1, x2, y2] (default: easeInOut)
|
|
44
|
+
transitionEasingBezier?: ReadonlyArray<CodegenTypes.Float>;
|
|
45
|
+
transitionDamping?: CodegenTypes.WithDefault<CodegenTypes.Float, 15.0>;
|
|
46
|
+
transitionStiffness?: CodegenTypes.WithDefault<CodegenTypes.Float, 120.0>;
|
|
47
|
+
transitionMass?: CodegenTypes.WithDefault<CodegenTypes.Float, 1.0>;
|
|
48
|
+
transitionLoop?: CodegenTypes.WithDefault<
|
|
49
|
+
'none' | 'repeat' | 'reverse',
|
|
50
|
+
'none'
|
|
51
|
+
>;
|
|
52
|
+
|
|
53
|
+
// Transform origin (0–1 fractions, default center)
|
|
54
|
+
transformOriginX?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.5>;
|
|
55
|
+
transformOriginY?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.5>;
|
|
56
|
+
|
|
57
|
+
// Events
|
|
58
|
+
onTransitionEnd?: CodegenTypes.DirectEventHandler<
|
|
59
|
+
Readonly<{ finished: boolean }>
|
|
60
|
+
>;
|
|
61
|
+
|
|
62
|
+
// Android hardware layer optimization (no-op on iOS)
|
|
63
|
+
useHardwareLayer?: CodegenTypes.WithDefault<boolean, false>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default codegenNativeComponent<NativeProps>(
|
|
67
|
+
'EaseView',
|
|
68
|
+
) as HostComponent<NativeProps>;
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { EaseView } from './EaseView';
|
|
2
|
+
export type { EaseViewProps } from './EaseView';
|
|
3
|
+
export type {
|
|
4
|
+
AnimateProps,
|
|
5
|
+
CubicBezier,
|
|
6
|
+
Transition,
|
|
7
|
+
TimingTransition,
|
|
8
|
+
SpringTransition,
|
|
9
|
+
NoneTransition,
|
|
10
|
+
EasingType,
|
|
11
|
+
TransitionEndEvent,
|
|
12
|
+
TransformOrigin,
|
|
13
|
+
} from './types';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Cubic bezier control points: [x1, y1, x2, y2]. */
|
|
2
|
+
export type CubicBezier = [number, number, number, number];
|
|
3
|
+
|
|
4
|
+
/** Easing curve for timing animations. */
|
|
5
|
+
export type EasingType =
|
|
6
|
+
| 'linear'
|
|
7
|
+
| 'easeIn'
|
|
8
|
+
| 'easeOut'
|
|
9
|
+
| 'easeInOut'
|
|
10
|
+
| CubicBezier;
|
|
11
|
+
|
|
12
|
+
/** Timing-based transition with fixed duration and easing curve. */
|
|
13
|
+
export type TimingTransition = {
|
|
14
|
+
type: 'timing';
|
|
15
|
+
/** Duration in milliseconds. @default 300 */
|
|
16
|
+
duration?: number;
|
|
17
|
+
/** Easing curve. @default 'easeInOut' */
|
|
18
|
+
easing?: EasingType;
|
|
19
|
+
/** Loop mode — 'repeat' restarts from the beginning, 'reverse' alternates direction. */
|
|
20
|
+
loop?: 'repeat' | 'reverse';
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Physics-based spring transition. */
|
|
24
|
+
export type SpringTransition = {
|
|
25
|
+
type: 'spring';
|
|
26
|
+
/** Friction — higher values reduce oscillation. @default 15 */
|
|
27
|
+
damping?: number;
|
|
28
|
+
/** Spring constant — higher values mean faster animation. @default 120 */
|
|
29
|
+
stiffness?: number;
|
|
30
|
+
/** Mass of the object — higher values mean slower, more momentum. @default 1 */
|
|
31
|
+
mass?: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** No transition — values are applied immediately without animation. */
|
|
35
|
+
export type NoneTransition = {
|
|
36
|
+
type: 'none';
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Animation transition configuration. */
|
|
40
|
+
export type Transition = TimingTransition | SpringTransition | NoneTransition;
|
|
41
|
+
|
|
42
|
+
/** Event fired when the animation ends. */
|
|
43
|
+
export type TransitionEndEvent = {
|
|
44
|
+
/** True if the animation completed naturally, false if interrupted. */
|
|
45
|
+
finished: boolean;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Transform origin as 0–1 fractions. Default is center (0.5, 0.5). */
|
|
49
|
+
export type TransformOrigin = {
|
|
50
|
+
/** Horizontal origin. 0 = left, 0.5 = center, 1 = right. @default 0.5 */
|
|
51
|
+
x?: number;
|
|
52
|
+
/** Vertical origin. 0 = top, 0.5 = center, 1 = bottom. @default 0.5 */
|
|
53
|
+
y?: number;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Animatable view properties. Unspecified properties default to their identity values. */
|
|
57
|
+
export type AnimateProps = {
|
|
58
|
+
/** View opacity (0–1). @default 1 */
|
|
59
|
+
opacity?: number;
|
|
60
|
+
/** Horizontal translation in pixels. @default 0 */
|
|
61
|
+
translateX?: number;
|
|
62
|
+
/** Vertical translation in pixels. @default 0 */
|
|
63
|
+
translateY?: number;
|
|
64
|
+
/** Uniform scale factor (shorthand for scaleX + scaleY). @default 1 */
|
|
65
|
+
scale?: number;
|
|
66
|
+
/** Horizontal scale factor. Overrides `scale` for the X axis. @default 1 */
|
|
67
|
+
scaleX?: number;
|
|
68
|
+
/** Vertical scale factor. Overrides `scale` for the Y axis. @default 1 */
|
|
69
|
+
scaleY?: number;
|
|
70
|
+
/** Z-axis rotation in degrees. @default 0 */
|
|
71
|
+
rotate?: number;
|
|
72
|
+
/** X-axis rotation in degrees (3D). @default 0 */
|
|
73
|
+
rotateX?: number;
|
|
74
|
+
/** Y-axis rotation in degrees (3D). @default 0 */
|
|
75
|
+
rotateY?: number;
|
|
76
|
+
/** Border radius in pixels. Uses hardware-accelerated clipping (ViewOutlineProvider on Android, layer.cornerRadius on iOS). @default 0 */
|
|
77
|
+
borderRadius?: number;
|
|
78
|
+
};
|