react-native-ease 0.2.0 → 0.4.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.
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "react-native-ease-plugins",
3
+ "owner": {
4
+ "name": "AppAndFlow",
5
+ "email": "devops@appandflow.com"
6
+ },
7
+ "metadata": {
8
+ "description": "Claude Code skills for react-native-ease — migrate Reanimated/Animated code to react-native-ease"
9
+ },
10
+ "plugins": [
11
+ {
12
+ "name": "react-native-ease",
13
+ "source": "./",
14
+ "description": "Scan for Animated/Reanimated code and migrate to react-native-ease",
15
+ "version": "0.2.0",
16
+ "author": {
17
+ "name": "AppAndFlow"
18
+ }
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "react-native-ease",
3
+ "description": "Declarative native animations for React Native — migration tools",
4
+ "version": "0.2.0"
5
+ }
package/README.md CHANGED
@@ -1,32 +1,17 @@
1
- # 🍃 react-native-ease
1
+ <img width="100%" height="auto" alt="react-native-ease by App & Flow" src="https://github.com/user-attachments/assets/8006ed51-d373-4c97-9e80-9937eb9a569e" />
2
2
 
3
3
  Lightweight declarative animations powered by platform APIs. Uses Core Animation on iOS and Animator on Android — zero JS overhead.
4
4
 
5
- ## Goals
5
+ ## About
6
+ App & Flow is a Montreal-based React Native engineering and consulting studio. We partner with the world’s top companies and are recommended by [Expo](https://expo.dev/consultants). Need a hand? Let’s build together. team@appandflow.com
6
7
 
7
- - **Fast** — Animations run entirely on native platform APIs (CAAnimation, ObjectAnimator/SpringAnimation). No JS animation loop, no worklets, no shared values.
8
- - **Simple** — CSS-transition-like API. Set target values, get smooth animations. One component, a few props.
9
- - **Lightweight** — Minimal native code, no C++ runtime, no custom animation engine. Just a thin declarative wrapper around what the OS already provides.
10
- - **Interruptible** — Changing values mid-animation smoothly redirects to the new target. No jumps.
11
-
12
- ## Non-Goals
13
-
14
- - **Complex gesture-driven animations** — If you need pan/pinch-driven animations, animation worklets, or shared values across components, use [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated).
15
- - **Layout animations** — Animating width/height/layout changes is not supported.
16
- - **Shared element transitions** — Use Reanimated or React Navigation's shared element transitions.
17
- - **Old architecture** — Fabric (new architecture) only.
8
+ ## Demo
18
9
 
19
- ## When to use this vs Reanimated
10
+ ![ease-demo](https://github.com/user-attachments/assets/09658b07-803e-4b7e-a23c-831a6c63df84)
20
11
 
21
- | Use react-native-ease | Use Reanimated |
22
- |---|---|
23
- | Fade in a view | Gesture-driven animations |
24
- | Slide/translate on state change | Complex interpolations |
25
- | Scale/rotate on press | Shared values across components |
26
- | Simple enter animations | Layout animations |
27
- | You want zero config | You need animation worklets |
12
+ ## Getting started
28
13
 
29
- ## Installation
14
+ ### Installation
30
15
 
31
16
  ```bash
32
17
  npm install react-native-ease
@@ -34,7 +19,25 @@ npm install react-native-ease
34
19
  yarn add react-native-ease
35
20
  ```
36
21
 
37
- ## Quick Start
22
+ ### Migration Skill
23
+
24
+ If you're already using `react-native-reanimated` or React Native's `Animated` API, this project includes an [Agent Skill](https://agentskills.io) that scans your codebase for animations that can be replaced with `react-native-ease` and migrates them automatically.
25
+
26
+ ```bash
27
+ npx skills add appandflow/react-native-ease
28
+ ```
29
+
30
+ Then invoke the skill in your agent (e.g., `/react-native-ease-refactor` in Claude Code).
31
+
32
+ The skill will:
33
+
34
+ 1. Scan your project for Reanimated/Animated code
35
+ 2. Classify which animations can be migrated (and which can't, with reasons)
36
+ 3. Show a migration report with before/after details
37
+ 4. Let you select which components to migrate
38
+ 5. Apply the changes, preserving all non-animation logic
39
+
40
+ ### Example
38
41
 
39
42
  ```tsx
40
43
  import { EaseView } from 'react-native-ease';
@@ -54,6 +57,32 @@ function FadeCard({ visible, children }) {
54
57
 
55
58
  `EaseView` works like a regular `View` — it accepts children, styles, and all standard view props. When values in `animate` change, it smoothly transitions to the new values using native platform animations.
56
59
 
60
+ ## Why
61
+
62
+ ### Goals
63
+
64
+ - **Fast** — Animations run entirely on native platform APIs (CAAnimation, ObjectAnimator/SpringAnimation). No JS animation loop, no worklets, no shared values.
65
+ - **Simple** — CSS-transition-like API. Set target values, get smooth animations. One component, a few props.
66
+ - **Lightweight** — Minimal native code, no C++ runtime, no custom animation engine. Just a thin declarative wrapper around what the OS already provides.
67
+ - **Interruptible** — Changing values mid-animation smoothly redirects to the new target. No jumps.
68
+
69
+ ### Non-Goals
70
+
71
+ - **Complex gesture-driven animations** — If you need pan/pinch-driven animations, animation worklets, or shared values across components, use [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated).
72
+ - **Layout animations** — Animating width/height/layout changes is not supported.
73
+ - **Shared element transitions** — Use Reanimated or React Navigation's shared element transitions.
74
+ - **Old architecture** — Fabric (new architecture) only.
75
+
76
+ ### When to use this vs Reanimated
77
+
78
+ | Use case | Ease | Reanimated |
79
+ | -------------------------------------- | ---- | ---------- |
80
+ | Fade/slide/scale on state change | ✅ | |
81
+ | Enter/exit animations | ✅ | |
82
+ | Gesture-driven animations (pan, pinch) | | ✅ |
83
+ | Layout animations (width, height) | | ✅ |
84
+ | Complex interpolations & chaining | | ✅ |
85
+
57
86
  ## Guide
58
87
 
59
88
  ### Timing Animations
@@ -67,11 +96,12 @@ Timing animations transition from one value to another over a fixed duration wit
67
96
  />
68
97
  ```
69
98
 
70
- | Parameter | Type | Default | Description |
71
- |---|---|---|---|
72
- | `duration` | `number` | `300` | Duration in milliseconds |
73
- | `easing` | `EasingType` | `'easeInOut'` | Easing curve (preset name or `[x1, y1, x2, y2]` cubic bezier) |
74
- | `loop` | `string` | — | `'repeat'` restarts from the beginning, `'reverse'` alternates direction |
99
+ | Parameter | Type | Default | Description |
100
+ | ---------- | ------------ | ------------- | ------------------------------------------------------------------------ |
101
+ | `duration` | `number` | `300` | Duration in milliseconds |
102
+ | `easing` | `EasingType` | `'easeInOut'` | Easing curve (preset name or `[x1, y1, x2, y2]` cubic bezier) |
103
+ | `delay` | `number` | `0` | Delay in milliseconds before the animation starts |
104
+ | `loop` | `string` | — | `'repeat'` restarts from the beginning, `'reverse'` alternates direction |
75
105
 
76
106
  Available easing curves:
77
107
 
@@ -112,11 +142,12 @@ Spring animations use a physics-based model for natural-feeling motion. Great fo
112
142
  />
113
143
  ```
114
144
 
115
- | Parameter | Type | Default | Description |
116
- |---|---|---|---|
117
- | `damping` | `number` | `15` | Friction — higher values reduce oscillation |
118
- | `stiffness` | `number` | `120` | Spring constant — higher values mean faster animation |
119
- | `mass` | `number` | `1` | Mass of the object — higher values mean slower, more momentum |
145
+ | Parameter | Type | Default | Description |
146
+ | ----------- | -------- | ------- | ------------------------------------------------------------- |
147
+ | `damping` | `number` | `15` | Friction — higher values reduce oscillation |
148
+ | `stiffness` | `number` | `120` | Spring constant — higher values mean faster animation |
149
+ | `mass` | `number` | `1` | Mass of the object — higher values mean slower, more momentum |
150
+ | `delay` | `number` | `0` | Delay in milliseconds before the animation starts |
120
151
 
121
152
  Spring presets for common feels:
122
153
 
@@ -147,6 +178,46 @@ Use `{ type: 'none' }` to apply values immediately without animation. Useful for
147
178
 
148
179
  `onTransitionEnd` fires immediately with `{ finished: true }`.
149
180
 
181
+ ### Per-Property Transitions
182
+
183
+ Pass a map instead of a single config to use different animation types per property category.
184
+
185
+ ```tsx
186
+ <EaseView
187
+ animate={{ opacity: visible ? 1 : 0, translateY: visible ? 0 : 30 }}
188
+ transition={{
189
+ opacity: { type: 'timing', duration: 150, easing: 'easeOut' },
190
+ transform: { type: 'spring', damping: 12, stiffness: 200 },
191
+ }}
192
+ />
193
+ ```
194
+
195
+ Available category keys:
196
+
197
+ | Key | Properties |
198
+ | ----------------- | ---------------------------------------------------------------- |
199
+ | `default` | Fallback for categories not explicitly listed |
200
+ | `transform` | translateX, translateY, scaleX, scaleY, rotate, rotateX, rotateY |
201
+ | `opacity` | opacity |
202
+ | `borderRadius` | borderRadius |
203
+ | `backgroundColor` | backgroundColor |
204
+
205
+ Use `default` as a fallback for categories not explicitly listed:
206
+
207
+ ```tsx
208
+ <EaseView
209
+ animate={{ opacity: 1, scale: 1.2, translateY: -20 }}
210
+ transition={{
211
+ default: { type: 'spring', damping: 15, stiffness: 120 },
212
+ opacity: { type: 'timing', duration: 200, easing: 'easeOut' },
213
+ }}
214
+ />
215
+ ```
216
+
217
+ When no `default` key is provided, the library default (timing 300ms easeInOut) applies to all categories.
218
+
219
+ > **Android note:** Android animates `backgroundColor` with `ValueAnimator` (timing only). If a per-property map specifies `type: 'spring'` for `backgroundColor`, it silently falls back to timing 300ms.
220
+
150
221
  ### Border Radius
151
222
 
152
223
  `borderRadius` can be animated just like other properties. It uses hardware-accelerated platform APIs — `ViewOutlineProvider` + `clipToOutline` on Android and `layer.cornerRadius` + `layer.masksToBounds` on iOS. Unlike RN's style-based `borderRadius` (which uses a Canvas drawable on Android), this clips children properly and is GPU-accelerated.
@@ -180,6 +251,8 @@ When `borderRadius` is in `animate`, any `borderRadius` in `style` is automatica
180
251
 
181
252
  On Android, background color uses `ValueAnimator.ofArgb()` (timing only — spring is not supported for colors). On iOS, it uses `CAAnimation` on the `backgroundColor` layer key path and supports both timing and spring transitions.
182
253
 
254
+ > **Note:** On Android, background color animation uses `ValueAnimator.ofArgb()` which only supports timing transitions. Spring transitions for `backgroundColor` are not supported on Android and will fall back to timing with the default duration. On iOS, both timing and spring transitions work for background color.
255
+
183
256
  When `backgroundColor` is in `animate`, any `backgroundColor` in `style` is automatically stripped to avoid conflicts.
184
257
 
185
258
  ### Animatable Properties
@@ -189,16 +262,16 @@ All properties are set in the `animate` prop as flat values (no transform array)
189
262
  ```tsx
190
263
  <EaseView
191
264
  animate={{
192
- opacity: 1, // 0 to 1
193
- translateX: 0, // pixels
194
- translateY: 0, // pixels
195
- scale: 1, // 1 = normal size (shorthand for scaleX + scaleY)
196
- scaleX: 1, // horizontal scale
197
- scaleY: 1, // vertical scale
198
- rotate: 0, // Z-axis rotation in degrees
199
- rotateX: 0, // X-axis rotation in degrees (3D)
200
- rotateY: 0, // Y-axis rotation in degrees (3D)
201
- borderRadius: 0, // pixels (hardware-accelerated, clips children)
265
+ opacity: 1, // 0 to 1
266
+ translateX: 0, // pixels
267
+ translateY: 0, // pixels
268
+ scale: 1, // 1 = normal size (shorthand for scaleX + scaleY)
269
+ scaleX: 1, // horizontal scale
270
+ scaleY: 1, // vertical scale
271
+ rotate: 0, // Z-axis rotation in degrees
272
+ rotateX: 0, // X-axis rotation in degrees (3D)
273
+ rotateY: 0, // Y-axis rotation in degrees (3D)
274
+ borderRadius: 0, // pixels (hardware-accelerated, clips children)
202
275
  backgroundColor: 'transparent', // any RN color value
203
276
  }}
204
277
  />
@@ -245,6 +318,26 @@ Use `initialAnimate` to set starting values. On mount, the view starts at `initi
245
318
 
246
319
  Without `initialAnimate`, the view renders at the `animate` values immediately with no animation on mount.
247
320
 
321
+ ### Delay
322
+
323
+ Use `delay` to postpone the start of an animation. This is useful for staggering enter animations across multiple elements.
324
+
325
+ ```tsx
326
+ // Staggered fade-in list
327
+ {items.map((item, i) => (
328
+ <EaseView
329
+ key={item.id}
330
+ initialAnimate={{ opacity: 0, translateY: 20 }}
331
+ animate={{ opacity: 1, translateY: 0 }}
332
+ transition={{ type: 'timing', duration: 300, delay: i * 100 }}
333
+ >
334
+ <Text>{item.label}</Text>
335
+ </EaseView>
336
+ ))}
337
+ ```
338
+
339
+ `delay` works with both timing and spring transitions.
340
+
248
341
  ### Interruption
249
342
 
250
343
  Animations are interruptible by default. If you change `animate` values while an animation is running, it smoothly redirects to the new target from wherever it currently is — no jumping or restarting.
@@ -280,11 +373,11 @@ By default, scale and rotation animate from the view's center. Use `transformOri
280
373
  />
281
374
  ```
282
375
 
283
- | Value | Position |
284
- |---|---|
285
- | `{ x: 0, y: 0 }` | Top-left |
376
+ | Value | Position |
377
+ | -------------------- | ---------------- |
378
+ | `{ x: 0, y: 0 }` | Top-left |
286
379
  | `{ x: 0.5, y: 0.5 }` | Center (default) |
287
- | `{ x: 1, y: 1 }` | Bottom-right |
380
+ | `{ x: 1, y: 1 }` | Bottom-right |
288
381
 
289
382
  ### Style Handling
290
383
 
@@ -318,32 +411,32 @@ By default, scale and rotation animate from the view's center. Use `transformOri
318
411
 
319
412
  A `View` that animates property changes using native platform APIs.
320
413
 
321
- | Prop | Type | Description |
322
- |---|---|---|
323
- | `animate` | `AnimateProps` | Target values for animated properties |
324
- | `initialAnimate` | `AnimateProps` | Starting values for enter animations (animates to `animate` on mount) |
325
- | `transition` | `Transition` | Animation configuration (timing, spring, or none) |
326
- | `onTransitionEnd` | `(event) => void` | Called when all animations complete with `{ finished: boolean }` |
327
- | `transformOrigin` | `{ x?: number; y?: number }` | Pivot point for scale/rotation as 0–1 fractions. Default: `{ x: 0.5, y: 0.5 }` (center) |
328
- | `useHardwareLayer` | `boolean` | Android only — rasterize to GPU texture during animations. See [Hardware Layers](#hardware-layers-android). Default: `false` |
329
- | `style` | `ViewStyle` | Non-animated styles (layout, colors, borders, etc.) |
330
- | `children` | `ReactNode` | Child elements |
331
- | ...rest | `ViewProps` | All other standard View props |
414
+ | Prop | Type | Description |
415
+ | ------------------ | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
416
+ | `animate` | `AnimateProps` | Target values for animated properties |
417
+ | `initialAnimate` | `AnimateProps` | Starting values for enter animations (animates to `animate` on mount) |
418
+ | `transition` | `Transition` | Animation configuration (timing, spring, or none) |
419
+ | `onTransitionEnd` | `(event) => void` | Called when all animations complete with `{ finished: boolean }` |
420
+ | `transformOrigin` | `{ x?: number; y?: number }` | Pivot point for scale/rotation as 0–1 fractions. Default: `{ x: 0.5, y: 0.5 }` (center) |
421
+ | `useHardwareLayer` | `boolean` | Android only — rasterize to GPU texture during animations. See [Hardware Layers](#hardware-layers-android). Default: `false` |
422
+ | `style` | `ViewStyle` | Non-animated styles (layout, colors, borders, etc.) |
423
+ | `children` | `ReactNode` | Child elements |
424
+ | ...rest | `ViewProps` | All other standard View props |
332
425
 
333
426
  ### `AnimateProps`
334
427
 
335
- | Property | Type | Default | Description |
336
- |---|---|---|---|
337
- | `opacity` | `number` | `1` | View opacity (0–1) |
338
- | `translateX` | `number` | `0` | Horizontal translation in pixels |
339
- | `translateY` | `number` | `0` | Vertical translation in pixels |
340
- | `scale` | `number` | `1` | Uniform scale factor (shorthand for `scaleX` + `scaleY`) |
341
- | `scaleX` | `number` | `1` | Horizontal scale factor (overrides `scale` for X axis) |
342
- | `scaleY` | `number` | `1` | Vertical scale factor (overrides `scale` for Y axis) |
343
- | `rotate` | `number` | `0` | Z-axis rotation in degrees |
344
- | `rotateX` | `number` | `0` | X-axis rotation in degrees (3D) |
345
- | `rotateY` | `number` | `0` | Y-axis rotation in degrees (3D) |
346
- | `borderRadius` | `number` | `0` | Border radius in pixels (hardware-accelerated, clips children) |
428
+ | Property | Type | Default | Description |
429
+ | ----------------- | ------------ | --------------- | ------------------------------------------------------------------------------------ |
430
+ | `opacity` | `number` | `1` | View opacity (0–1) |
431
+ | `translateX` | `number` | `0` | Horizontal translation in pixels |
432
+ | `translateY` | `number` | `0` | Vertical translation in pixels |
433
+ | `scale` | `number` | `1` | Uniform scale factor (shorthand for `scaleX` + `scaleY`) |
434
+ | `scaleX` | `number` | `1` | Horizontal scale factor (overrides `scale` for X axis) |
435
+ | `scaleY` | `number` | `1` | Vertical scale factor (overrides `scale` for Y axis) |
436
+ | `rotate` | `number` | `0` | Z-axis rotation in degrees |
437
+ | `rotateX` | `number` | `0` | X-axis rotation in degrees (3D) |
438
+ | `rotateY` | `number` | `0` | Y-axis rotation in degrees (3D) |
439
+ | `borderRadius` | `number` | `0` | Border radius in pixels (hardware-accelerated, clips children) |
347
440
  | `backgroundColor` | `ColorValue` | `'transparent'` | Background color (any RN color value). Timing-only on Android, spring+timing on iOS. |
348
441
 
349
442
  Properties not specified in `animate` default to their identity values.
@@ -355,6 +448,7 @@ Properties not specified in `animate` default to their identity values.
355
448
  type: 'timing';
356
449
  duration?: number; // default: 300 (ms)
357
450
  easing?: EasingType; // default: 'easeInOut' — preset name or [x1, y1, x2, y2]
451
+ delay?: number; // default: 0 (ms)
358
452
  loop?: 'repeat' | 'reverse'; // default: none
359
453
  }
360
454
  ```
@@ -367,6 +461,7 @@ Properties not specified in `animate` default to their identity values.
367
461
  damping?: number; // default: 15
368
462
  stiffness?: number; // default: 120
369
463
  mass?: number; // default: 1
464
+ delay?: number; // default: 0 (ms)
370
465
  }
371
466
  ```
372
467
 
@@ -385,10 +480,7 @@ Applies values instantly with no animation. `onTransitionEnd` fires immediately
385
480
  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.
386
481
 
387
482
  ```tsx
388
- <EaseView
389
- animate={{ opacity: isVisible ? 1 : 0 }}
390
- useHardwareLayer
391
- />
483
+ <EaseView animate={{ opacity: isVisible ? 1 : 0 }} useHardwareLayer />
392
484
  ```
393
485
 
394
486
  **Trade-offs:**
@@ -399,6 +491,58 @@ Setting `useHardwareLayer` rasterizes the view into a GPU texture for the durati
399
491
 
400
492
  No-op on iOS where Core Animation already composites off the main thread.
401
493
 
494
+ ## Benchmarks
495
+
496
+ The example app includes a benchmark that measures per-frame animation overhead across different approaches. All approaches run the same animation (translateX loop, linear, 2s) on a configurable number of views.
497
+
498
+ ### Android (release build, emulator, M4 MacBook Pro)
499
+
500
+ UI thread time per frame: anim + layout + draw (ms). Lower is better.
501
+
502
+ ![Android benchmark](https://github.com/user-attachments/assets/f0e5cf26-76be-4dd3-ae04-e17c6d13b49c)
503
+
504
+ <details>
505
+ <summary>Detailed numbers</summary>
506
+
507
+ | Views | Metric | Ease | Reanimated SV | Reanimated SV (FF) | Reanimated CSS | Reanimated CSS (FF) | RN Animated |
508
+ |-------|--------|------|---------------|---------------------|----------------|----------------------|-------------|
509
+ | 10 | Avg | 0.21 | 1.15 | 0.75 | 0.99 | 0.45 | 0.36 |
510
+ | 10 | P95 | 0.33 | 1.70 | 1.53 | 1.44 | 0.80 | 0.62 |
511
+ | 10 | P99 | 0.48 | 1.94 | 2.26 | 1.62 | 1.35 | 0.98 |
512
+ | 100 | Avg | 0.36 | 2.71 | 1.81 | 2.19 | 1.01 | 0.71 |
513
+ | 100 | P95 | 0.56 | 3.09 | 2.29 | 2.67 | 1.91 | 1.08 |
514
+ | 100 | P99 | 0.71 | 3.20 | 2.63 | 2.97 | 2.25 | 1.36 |
515
+ | 500 | Avg | 0.60 | 8.31 | 5.37 | 5.50 | 2.37 | 1.60 |
516
+ | 500 | P95 | 0.75 | 9.26 | 6.36 | 6.34 | 2.86 | 1.88 |
517
+ | 500 | P99 | 0.87 | 9.59 | 6.89 | 6.88 | 3.22 | 3.84 |
518
+
519
+ </details>
520
+
521
+ ### iOS (release build, simulator, iPhone 16 Pro, M4 MacBook Pro)
522
+
523
+ Display link callback time per frame (ms). Lower is better.
524
+
525
+ ![iOS benchmark](https://github.com/user-attachments/assets/c39a7a71-bf21-4276-b02f-b29983989832)
526
+
527
+ <details>
528
+ <summary>Detailed numbers</summary>
529
+
530
+ | Views | Metric | Ease | Reanimated SV | Reanimated SV (FF) | Reanimated CSS | Reanimated CSS (FF) | RN Animated |
531
+ |-------|--------|------|---------------|---------------------|----------------|----------------------|-------------|
532
+ | 10 | Avg | 0.01 | 1.33 | 1.08 | 1.06 | 0.63 | 0.83 |
533
+ | 10 | P95 | 0.02 | 1.67 | 1.59 | 1.34 | 1.01 | 1.18 |
534
+ | 10 | P99 | 0.03 | 1.90 | 1.68 | 1.50 | 1.08 | 1.31 |
535
+ | 100 | Avg | 0.01 | 3.72 | 3.33 | 2.71 | 2.48 | 3.32 |
536
+ | 100 | P95 | 0.01 | 5.21 | 4.50 | 3.83 | 3.39 | 4.28 |
537
+ | 100 | P99 | 0.02 | 5.68 | 4.75 | 4.91 | 3.79 | 4.55 |
538
+ | 500 | Avg | 0.01 | 6.84 | 6.54 | 4.16 | 3.70 | 4.91 |
539
+ | 500 | P95 | 0.01 | 7.69 | 7.32 | 4.59 | 4.22 | 5.66 |
540
+ | 500 | P99 | 0.02 | 8.10 | 7.45 | 4.71 | 4.33 | 5.89 |
541
+
542
+ </details>
543
+
544
+ Ease stays near zero because animations run entirely on platform APIs. On iOS, Core Animation runs on a separate render server process off the main thread, which is why Ease shows ~0ms. On Android, ObjectAnimator runs on the UI thread but is significantly lighter than other approaches. Reanimated results shown with experimental [feature flags](https://docs.swmansion.com/react-native-reanimated/docs/guides/feature-flags/) OFF (default) and ON (FF). Run the benchmark yourself in the [example app](example/).
545
+
402
546
  ## How It Works
403
547
 
404
548
  `EaseView` is a native Fabric component. The JS side flattens your `animate` and `transition` props into flat native props. When those props change, the native view: