react-native-ease

repository·main·Indexed 21 days ago

https://github.com/appandflow/react-native-ease

A lightweight library providing declarative animations for React Native using native platform APIs (Core Animation on iOS and Animator on Android). It offers a CSS-transition-like API with zero JS overhead for state-driven transitions, supporting timing, spring, and 'none' animation types. Designed for the Fabric (New Architecture), it includes the EaseView component and supports integration with NativeWind and Uniwind.

Tokens
17.1K
Snippets
51
Records
76
Agent score
70%

What's inside react-native-ease

  1. How the react-native-ease refactor migration works

    main

    The react-native-ease-refactor is a migration assistant designed to convert existing animation code from react-native-reanimated or React Native's built-in Animated API into react-native-ease EaseView components.

    The migration process follows a strict 6-phase workflow, starting with a Discovery phase where the assistant scans the project to identify animation patterns, detect dependencies like NativeWind, and determine the version of react-native-reanimated in use. This information is used to ensure correct mapping of default values and unit conversions in later phases.

  2. Optimize Android animations with Hardware Layers

    main

    On Android, setting the useHardwareLayer prop on <EaseView> rasterizes the view into a GPU texture for the duration of the animation. This allows animated properties like opacity, scale, and rotation to be composited on the RenderThread without redrawing the entire view hierarchy.

    When to use:

    • For complex views with many children to improve performance.

    Trade-offs:

    • Memory: Uses additional GPU memory proportional to the view size.
    • Clipping: Children that overflow the view's layout bounds will be clipped by the texture. This can cause visual artifacts when animating translateX or translateY on views with overflowing content.

    Note: This prop is a no-op on iOS as Core Animation already composites off the main thread.

    <EaseView animate={{ opacity: isVisible ? 1 : 0 }} useHardwareLayer />
  3. How EaseView animations work

    main

    The EaseView component is a native Fabric component designed for high-performance animations without JS thread involvement.

    When you update animate or transition props, the following lifecycle occurs:

    1. Diffing: The native side compares previous and new values to identify changes.
    2. Reading: It reads the current in-flight value to allow for smooth interruptions (e.g., if a new animation starts before the previous one finishes).
    3. Creation: It creates a platform-native animation starting from the current value to the new target.
    4. Setting: It sets the final value immediately on the model layer.

    Platform Implementations:

    • iOS: Uses CABasicAnimation and CASpringAnimation on CALayer key paths.
    • Android: Uses ObjectAnimator and SpringAnimation on View properties.
  4. When to use Ease vs Reanimated

    main

    Choose react-native-ease for simple, state-driven transitions. Choose react-native-reanimated for complex, gesture-driven, or layout-based animations.

    Use caseEaseReanimated
    Fade/slide/scale on state change
    Enter/exit animations
    Gesture-driven animations (pan, pinch)
    Layout animations (width, height)
    Complex interpolations & chaining
  5. Compare Ease vs Reanimated

    main

    Choose between react-native-ease and react-native-reanimated based on your animation requirements:

    Use caseEaseReanimated
    Fade/slide/scale on state change
    Enter/exit animations
    Gesture-driven animations (pan, pinch)
    Layout animations (width, height)
    Complex interpolations & chaining

    Note: react-native-ease is designed for simple, declarative transitions. It does not support layout animations, shared element transitions, or complex gesture-driven worklets. It requires the Fabric (New Architecture).

  6. EaseView Key Props and Constraints

    main

    Key Props

    • animate: Target values for animated properties.
    • initialAnimate: Starting values (animates to animate on mount).
    • transition: Animation config (SingleTransition or TransitionMap).
    • onTransitionEnd: Callback function receiving { finished: boolean }.
    • transformOrigin: Pivot point as { x: 0-1, y: 0-1 }, default is center.
    • useHardwareLayer: Boolean to enable Android GPU optimization (default false).
    • className: Supports NativeWind / Tailwind CSS class strings.

    Important Constraints

    • No animation sequencing: There is no equivalent to Reanimated's withSequence. Use delay in the transition config for simple delays.
    • No gesture/scroll-driven animations: EaseView is strictly state-driven.
    • Style/Animate Conflict: If a property is defined in both style and animate, the value in animate takes precedence.
  7. Understand performance benchmarks for Ease

    main

    The react-native-ease example app includes a benchmark to measure per-frame animation overhead. The benchmark runs a translateX loop (linear, 2s duration) across a configurable number of views (10, 100, or 500) to compare performance against other animation libraries like Reanimated and RN Animated.

    Android Metrics

    On Android, performance is measured by UI thread time per frame (comprising anim + layout + draw in milliseconds). A lower value indicates better performance.

    iOS Metrics

    On iOS, performance is measured by Display link callback time per frame (in milliseconds). A lower value indicates better performance.

    Why Ease is faster

    Ease maintains near-zero overhead because animations are executed entirely via platform APIs:

    • iOS: Uses Core Animation, which runs on a separate render server process off the main thread.
    • Android: Uses ObjectAnimator, which runs on the UI thread but is significantly lighter than the alternative approaches tested.
  8. Use Spring Animations

    main

    Spring animations use a physics-based model for natural motion. Use type: 'spring' in the transition prop.

    Parameters:

    • damping (number): Friction. Higher values reduce oscillation (default: 15).
    • stiffness (number): Spring constant. Higher values mean faster animation (default: 120).
    • mass (number): Mass of the object. Higher values mean more momentum (default: 1).
    • delay (number): Delay in milliseconds (default: 0).

    Note: Spring animations do not support looping.

    <EaseView
      animate={{ translateX: isOpen ? 200 : 0 }}
      transition={{ type: 'spring', damping: 15, stiffness: 120, mass: 1 }}
    />
  9. Classify components for migration to EaseView

    main

    When refactoring from Reanimated or the standard React Native Animated API to EaseView, use the following decision tree to determine if a component is migratable. If a component matches any of the first 10 criteria, it is not migratable.

    Non-Migratable Criteria

    1. Gesture-driven animation: Uses Gesture.Pan, Gesture.Pinch, Gesture.Rotation, or useAnimatedGestureHandler.
    2. Scroll-driven animation: Uses useAnimatedScrollHandler or onScroll with Animated.event.
    3. Shared element transition: Uses sharedTransitionTag.
    4. Requires worklet runtime: Uses runOnUI or worklet directives.
    5. Animation sequencing not supported: Uses withSequence (Note: withDelay wrapping a single animation is migratable).
    6. Complex delay/sequencing not supported: Uses withDelay wrapping withSequence or nested withDelay.
    7. Complex interpolation: Uses interpolate() with more than 2 input/output values.
    8. Layout animation: Uses the layout={...} prop.
    9. Animates unsupported property: Animating anything other than: opacity, translateX, translateY, scale, scaleX, scaleY, rotate, rotateX, rotateY, borderRadius, backgroundColor, borderWidth, borderColor, shadowOpacity, shadowRadius, shadowColor, shadowOffset, or elevation.
    10. Not state-driven: Animation is triggered by gesture/scroll values rather than React state.

    Migratable Criteria

    • If the component uses different transition configs per property (e.g., different timing for opacity vs scale), it is migratable using a TransitionMap.
    • Otherwise, if it doesn't meet the above, it is migratable.
  10. Animate Border Radius, Background Color, and Borders

    main

    Border Radius

    Uses hardware-accelerated APIs (ViewOutlineProvider on Android, layer.cornerRadius on iOS). When borderRadius is in animate, any borderRadius in style is automatically stripped to avoid conflicts.

    Background Color

    Supports any React Native color value.

    • iOS: Supports both timing and spring transitions.
    • Android: Only supports timing transitions (via ValueAnimator.ofArgb()). Spring transitions will fall back to timing.
    • Conflict: backgroundColor in style is stripped if present in animate.

    Border

    Animates borderWidth and borderColor. On iOS, these use Core Animation; on Android, they use BackgroundStyleApplicator.

    // Border example
    <EaseView
      animate={{
        borderWidth: selected ? 3 : 0,
        borderColor: selected ? '#3B82F6' : '#E5E7EB',
      }}
      transition={{ border: { type: 'spring', damping: 15, stiffness: 120 } }}
      style={styles.card}
    />
  11. Create enter and looping animations

    main

    Enter Animations

    To create an animation that runs when a component mounts, use the initialAnimate prop to define the starting state and animate for the target state.

    Looping Animations

    To loop an animation, use the loop property ('repeat' or 'reverse') in the transition prop. Requirement: Looping requires initialAnimate to be defined so the library knows the starting value. Spring animations do not support looping.

    <EaseView
      initialAnimate={{ opacity: 0, translateY: 20 }}
      animate={{ opacity: 1, translateY: 0 }}
      transition={{ type: 'spring', damping: 15, stiffness: 120, mass: 1 }}
    />