|
1 | | -# react-native-ease |
| 1 | +# 🍃 react-native-ease |
2 | 2 |
|
3 | | -Lightweight declarative animations powered by platform APIs |
| 3 | +Lightweight declarative animations powered by platform APIs. Uses Core Animation on iOS and Animator on Android — zero JS thread overhead. |
4 | 4 |
|
5 | | -## Installation |
| 5 | +## Goals |
| 6 | + |
| 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. |
| 18 | +- **Fine-grained control** — No per-property transition config, no completion callbacks, no animation chaining. If you need that level of control, Reanimated is the right tool. |
6 | 19 |
|
| 20 | +## When to use this vs Reanimated |
7 | 21 |
|
8 | | -```sh |
| 22 | +| Use react-native-ease | Use Reanimated | |
| 23 | +|---|---| |
| 24 | +| Fade in a view | Gesture-driven animations | |
| 25 | +| Slide/translate on state change | Complex interpolations | |
| 26 | +| Scale/rotate on press | Shared values across components | |
| 27 | +| Simple enter animations | Layout animations | |
| 28 | +| You want zero config | You need animation worklets | |
| 29 | + |
| 30 | +## Installation |
| 31 | + |
| 32 | +```bash |
9 | 33 | npm install react-native-ease |
| 34 | +# or |
| 35 | +yarn add react-native-ease |
| 36 | +``` |
| 37 | + |
| 38 | +On iOS, run pod install: |
| 39 | + |
| 40 | +```bash |
| 41 | +cd ios && pod install |
| 42 | +``` |
| 43 | + |
| 44 | +## Quick Start |
| 45 | + |
| 46 | +```tsx |
| 47 | +import { EaseView } from 'react-native-ease'; |
| 48 | + |
| 49 | +function FadeCard({ visible, children }) { |
| 50 | + return ( |
| 51 | + <EaseView |
| 52 | + animate={{ opacity: visible ? 1 : 0 }} |
| 53 | + transition={{ type: 'timing', duration: 300 }} |
| 54 | + style={styles.card} |
| 55 | + > |
| 56 | + {children} |
| 57 | + </EaseView> |
| 58 | + ); |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +`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. |
| 63 | + |
| 64 | +## Guide |
| 65 | + |
| 66 | +### Timing Animations |
| 67 | + |
| 68 | +Timing animations transition from one value to another over a fixed duration with an easing curve. |
| 69 | + |
| 70 | +```tsx |
| 71 | +<EaseView |
| 72 | + animate={{ opacity: isVisible ? 1 : 0 }} |
| 73 | + transition={{ type: 'timing', duration: 300, easing: 'easeOut' }} |
| 74 | +/> |
| 75 | +``` |
| 76 | + |
| 77 | +| Parameter | Type | Default | Description | |
| 78 | +|---|---|---|---| |
| 79 | +| `duration` | `number` | `300` | Duration in milliseconds | |
| 80 | +| `easing` | `string` | `'easeInOut'` | Easing curve | |
| 81 | + |
| 82 | +Available easing curves: |
| 83 | + |
| 84 | +- `'linear'` — constant speed |
| 85 | +- `'easeIn'` — starts slow, accelerates |
| 86 | +- `'easeOut'` — starts fast, decelerates |
| 87 | +- `'easeInOut'` — slow start and end, fast middle |
| 88 | + |
| 89 | +### Spring Animations |
| 90 | + |
| 91 | +Spring animations use a physics-based model for natural-feeling motion. Great for interactive elements. |
| 92 | + |
| 93 | +```tsx |
| 94 | +<EaseView |
| 95 | + animate={{ translateX: isOpen ? 200 : 0 }} |
| 96 | + transition={{ type: 'spring', damping: 15, stiffness: 120, mass: 1 }} |
| 97 | +/> |
| 98 | +``` |
| 99 | + |
| 100 | +| Parameter | Type | Default | Description | |
| 101 | +|---|---|---|---| |
| 102 | +| `damping` | `number` | `15` | Friction — higher values reduce oscillation | |
| 103 | +| `stiffness` | `number` | `120` | Spring constant — higher values mean faster animation | |
| 104 | +| `mass` | `number` | `1` | Mass of the object — higher values mean slower, more momentum | |
| 105 | + |
| 106 | +Spring presets for common feels: |
| 107 | + |
| 108 | +```tsx |
| 109 | +// Snappy (no bounce) |
| 110 | +{ type: 'spring', damping: 20, stiffness: 300, mass: 1 } |
| 111 | + |
| 112 | +// Gentle bounce |
| 113 | +{ type: 'spring', damping: 12, stiffness: 120, mass: 1 } |
| 114 | + |
| 115 | +// Bouncy |
| 116 | +{ type: 'spring', damping: 8, stiffness: 200, mass: 1 } |
| 117 | + |
| 118 | +// Slow and heavy |
| 119 | +{ type: 'spring', damping: 20, stiffness: 60, mass: 2 } |
| 120 | +``` |
| 121 | + |
| 122 | +### Animatable Properties |
| 123 | + |
| 124 | +All properties are set in the `animate` prop as flat values (no transform array). |
| 125 | + |
| 126 | +```tsx |
| 127 | +<EaseView |
| 128 | + animate={{ |
| 129 | + opacity: 1, // 0 to 1 |
| 130 | + translateX: 0, // pixels |
| 131 | + translateY: 0, // pixels |
| 132 | + scale: 1, // 1 = normal size |
| 133 | + rotate: 0, // degrees |
| 134 | + }} |
| 135 | +/> |
| 136 | +``` |
| 137 | + |
| 138 | +You can animate any combination of properties simultaneously. All properties share the same transition config. |
| 139 | + |
| 140 | +### Enter Animations |
| 141 | + |
| 142 | +Use `initialAnimate` to set starting values. On mount, the view starts at `initialAnimate` values and animates to `animate` values. |
| 143 | + |
| 144 | +```tsx |
| 145 | +// Fade in and slide up on mount |
| 146 | +<EaseView |
| 147 | + initialAnimate={{ opacity: 0, translateY: 20 }} |
| 148 | + animate={{ opacity: 1, translateY: 0 }} |
| 149 | + transition={{ type: 'spring', damping: 15, stiffness: 120, mass: 1 }} |
| 150 | +/> |
| 151 | +``` |
| 152 | + |
| 153 | +Without `initialAnimate`, the view renders at the `animate` values immediately with no animation on mount. |
| 154 | + |
| 155 | +### Interruption |
| 156 | + |
| 157 | +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. |
| 158 | + |
| 159 | +```tsx |
| 160 | +// Rapidly toggling this is fine — each toggle smoothly |
| 161 | +// redirects the animation from its current position |
| 162 | +<EaseView |
| 163 | + animate={{ translateX: isLeft ? 0 : 200 }} |
| 164 | + transition={{ type: 'spring', damping: 15, stiffness: 120, mass: 1 }} |
| 165 | +/> |
10 | 166 | ``` |
11 | 167 |
|
| 168 | +### Style Handling |
12 | 169 |
|
13 | | -## Usage |
| 170 | +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. |
14 | 171 |
|
| 172 | +```tsx |
| 173 | +// ✅ Correct |
| 174 | +<EaseView |
| 175 | + animate={{ opacity: 1, translateY: 0 }} |
| 176 | + style={{ backgroundColor: 'white', borderRadius: 12, padding: 16 }} |
| 177 | +/> |
15 | 178 |
|
16 | | -```js |
17 | | -import { EaseView } from "react-native-ease"; |
| 179 | +// ⚠️ opacity in style will be ignored with a warning |
| 180 | +<EaseView |
| 181 | + animate={{ translateY: 0 }} |
| 182 | + style={{ opacity: 0.5, backgroundColor: 'white' }} |
| 183 | +/> |
| 184 | +``` |
| 185 | + |
| 186 | +## API Reference |
| 187 | + |
| 188 | +### `<EaseView>` |
| 189 | + |
| 190 | +A `View` that animates property changes using native platform APIs. |
| 191 | + |
| 192 | +| Prop | Type | Description | |
| 193 | +|---|---|---| |
| 194 | +| `animate` | `AnimateProps` | Target values for animated properties | |
| 195 | +| `initialAnimate` | `AnimateProps` | Starting values for enter animations (animates to `animate` on mount) | |
| 196 | +| `transition` | `Transition` | Animation configuration (timing or spring) | |
| 197 | +| `style` | `ViewStyle` | Non-animated styles (layout, colors, borders, etc.) | |
| 198 | +| `children` | `ReactNode` | Child elements | |
| 199 | +| ...rest | `ViewProps` | All other standard View props | |
| 200 | + |
| 201 | +### `AnimateProps` |
| 202 | + |
| 203 | +| Property | Type | Default | Description | |
| 204 | +|---|---|---|---| |
| 205 | +| `opacity` | `number` | `1` | View opacity (0–1) | |
| 206 | +| `translateX` | `number` | `0` | Horizontal translation in pixels | |
| 207 | +| `translateY` | `number` | `0` | Vertical translation in pixels | |
| 208 | +| `scale` | `number` | `1` | Uniform scale factor | |
| 209 | +| `rotate` | `number` | `0` | Rotation in degrees | |
| 210 | + |
| 211 | +Properties not specified in `animate` default to their identity values. |
18 | 212 |
|
19 | | -// ... |
| 213 | +### `TimingTransition` |
20 | 214 |
|
21 | | -<EaseView color="tomato" /> |
| 215 | +```tsx |
| 216 | +{ |
| 217 | + type: 'timing'; |
| 218 | + duration?: number; // default: 300 (ms) |
| 219 | + easing?: 'linear' | 'easeIn' | 'easeOut' | 'easeInOut'; // default: 'easeInOut' |
| 220 | +} |
22 | 221 | ``` |
23 | 222 |
|
| 223 | +### `SpringTransition` |
| 224 | + |
| 225 | +```tsx |
| 226 | +{ |
| 227 | + type: 'spring'; |
| 228 | + damping?: number; // default: 15 |
| 229 | + stiffness?: number; // default: 120 |
| 230 | + mass?: number; // default: 1 |
| 231 | +} |
| 232 | +``` |
| 233 | + |
| 234 | +## How It Works |
| 235 | + |
| 236 | +`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: |
| 237 | + |
| 238 | +1. **Diffs** previous vs new values to find what changed |
| 239 | +2. **Reads** the current in-flight value (for smooth interruption) |
| 240 | +3. **Creates** a platform-native animation from the current value to the new target |
| 241 | +4. **Sets** the final value immediately on the model layer |
| 242 | + |
| 243 | +On iOS, this uses `CABasicAnimation`/`CASpringAnimation` on `CALayer` key paths. On Android, this uses `ObjectAnimator`/`SpringAnimation` on `View` properties. No JS thread involvement during the animation. |
| 244 | + |
| 245 | +## Requirements |
| 246 | + |
| 247 | +- React Native 0.76+ (new architecture / Fabric) |
| 248 | +- iOS 15.1+ |
| 249 | +- Android minSdk 24+ |
24 | 250 |
|
25 | 251 | ## Contributing |
26 | 252 |
|
|
0 commit comments