You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Resolve all easing (presets + custom) to a [x1, y1, x2, y2] array in
JS. Replace the transitionEasing string enum with a single
transitionEasingBezier array prop. Native always receives 4 floats and
constructs the timing function directly.
3. Add default to `IDENTITY` in `src/EaseView.tsx` and pass the flat props to `NativeEaseView`
51
51
4.**iOS:** Handle the new property in `updateProps:` — diff old/new, read presentation value, create animation
52
52
5.**Android:** Add `pending<Prop>` field, `@ReactProp` setter in `EaseViewManager.kt`, and handle in `applyAnimateValues()`
53
-
6. Add tests and update README
53
+
6.**Recycle:** Reset the new property to its identity value in `prepareForRecycle` (iOS) and `cleanup()` (Android). Fabric recycles views — any property not reset will leak stale values to the next user of the view.
54
+
7. Add tests and update README
54
55
55
56
## Development Commands
56
57
@@ -99,7 +100,8 @@ Use conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, etc.
99
100
100
101
## Key Gotchas
101
102
102
-
-**`style` must not contain `opacity` or `transform`** — these are controlled by the `animate` prop. The component warns in dev mode if you do this.
103
+
-**Style/animate conflict:**`style` can contain any `ViewStyle` property. If a property appears in both `style` and `animate`, the animated value wins and the style value is stripped. A dev warning is logged. Properties like `opacity` in `style` work fine when not in `animate` — the bitmask tells native which properties are animated vs style-managed.
104
+
-**View recycling:** Fabric recycles native views. `prepareForRecycle` (iOS) and `cleanup()` (Android) must reset ALL mutable view properties (opacity, translation, scale, rotation) to identity. Missing a reset causes stale values to leak across hot reloads or view reuse.
103
105
-**translateX/translateY are in DIPs (density-independent pixels) on the JS side.** Android `EaseViewManager` converts to pixels via `PixelUtil.toPixelFromDIP()`. iOS codegen handles this automatically.
104
106
-**iOS uses `CALayer` key-path animations** (`transform.scale.x`, `transform.translation.x`, etc.), not the `transform` property directly. This means `anchorPoint` controls the pivot for scale/rotation.
105
107
-**iOS `anchorPoint` changes shift visual position.** The `updateLayoutMetrics:oldLayoutMetrics:` override compensates for this — don't change it without understanding the position math.
Pass a `[x1, y1, x2, y2]` tuple for custom cubic bezier curves. The values correspond to the two control points of the bezier curve, matching the CSS `cubic-bezier()` function.
Use `{ type: 'none' }` to apply values immediately without animation. Useful for skipping animations in reduced-motion modes or when you need an instant state change.
140
+
141
+
```tsx
142
+
<EaseView
143
+
animate={{ opacity: isVisible?1:0 }}
144
+
transition={{ type: 'none' }}
145
+
/>
146
+
```
147
+
148
+
`onTransitionEnd` fires immediately with `{ finished: true }`.
149
+
116
150
### Animatable Properties
117
151
118
152
All properties are set in the `animate` prop as flat values (no transform array).
@@ -123,12 +157,18 @@ All properties are set in the `animate` prop as flat values (no transform array)
123
157
opacity: 1, // 0 to 1
124
158
translateX: 0, // pixels
125
159
translateY: 0, // pixels
126
-
scale: 1, // 1 = normal size
127
-
rotate: 0, // degrees
160
+
scale: 1, // 1 = normal size (shorthand for scaleX + scaleY)
161
+
scaleX: 1, // horizontal scale
162
+
scaleY: 1, // vertical scale
163
+
rotate: 0, // Z-axis rotation in degrees
164
+
rotateX: 0, // X-axis rotation in degrees (3D)
165
+
rotateY: 0, // Y-axis rotation in degrees (3D)
128
166
}}
129
167
/>
130
168
```
131
169
170
+
`scale` is a shorthand that sets both `scaleX` and `scaleY`. When `scaleX` or `scaleY` is also specified, it overrides the `scale` value for that axis.
171
+
132
172
You can animate any combination of properties simultaneously. All properties share the same transition config.
133
173
134
174
### Looping Animations
@@ -211,18 +251,26 @@ By default, scale and rotation animate from the view's center. Use `transformOri
211
251
212
252
### Style Handling
213
253
214
-
Use `animate` for animated properties and `style` for everything else. If you accidentally put `opacity` or `transform` in `style`, they will be ignored and you'll get a dev warning.
254
+
`EaseView` accepts all standard `ViewStyle` properties. If a property appears in both `style` and `animate`, the animated value takes priority and the style value is stripped. A dev warning is logged when this happens.
215
255
216
256
```tsx
217
-
//✅ Correct
257
+
//opacity in style works because only translateY is animated
|`transition`|`Transition`| Animation configuration (timing, spring, or none) |
241
289
|`onTransitionEnd`|`(event) => void`| Called when all animations complete with `{ finished: boolean }`|
242
290
|`transformOrigin`|`{ x?: number; y?: number }`| Pivot point for scale/rotation as 0–1 fractions. Default: `{ x: 0.5, y: 0.5 }` (center) |
243
291
|`useHardwareLayer`|`boolean`| Android only — rasterize to GPU texture during animations. See [Hardware Layers](#hardware-layers-android). Default: `false`|
@@ -252,8 +300,12 @@ A `View` that animates property changes using native platform APIs.
252
300
|`opacity`|`number`|`1`| View opacity (0–1) |
253
301
|`translateX`|`number`|`0`| Horizontal translation in pixels |
254
302
|`translateY`|`number`|`0`| Vertical translation in pixels |
255
-
|`scale`|`number`|`1`| Uniform scale factor |
256
-
|`rotate`|`number`|`0`| Rotation in degrees |
303
+
|`scale`|`number`|`1`| Uniform scale factor (shorthand for `scaleX` + `scaleY`) |
304
+
|`scaleX`|`number`|`1`| Horizontal scale factor (overrides `scale` for X axis) |
305
+
|`scaleY`|`number`|`1`| Vertical scale factor (overrides `scale` for Y axis) |
306
+
|`rotate`|`number`|`0`| Z-axis rotation in degrees |
307
+
|`rotateX`|`number`|`0`| X-axis rotation in degrees (3D) |
308
+
|`rotateY`|`number`|`0`| Y-axis rotation in degrees (3D) |
257
309
258
310
Properties not specified in `animate` default to their identity values.
259
311
@@ -263,7 +315,7 @@ Properties not specified in `animate` default to their identity values.
easing?:EasingType; // default: 'easeInOut' — preset name or [x1, y1, x2, y2]
267
319
loop?:'repeat'|'reverse'; // default: none
268
320
}
269
321
```
@@ -279,6 +331,16 @@ Properties not specified in `animate` default to their identity values.
279
331
}
280
332
```
281
333
334
+
### `NoneTransition`
335
+
336
+
```tsx
337
+
{
338
+
type: 'none';
339
+
}
340
+
```
341
+
342
+
Applies values instantly with no animation. `onTransitionEnd` fires immediately with `{ finished: true }`.
343
+
282
344
## Hardware Layers (Android)
283
345
284
346
Setting `useHardwareLayer` rasterizes the view into a GPU texture for the duration of the animation. This means animated property changes (opacity, scale, rotation) are composited on the RenderThread without redrawing the view hierarchy — useful for complex views with many children.
0 commit comments