CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-native-redash

Utility library for React Native Reanimated and Gesture Handler providing mathematical functions, animations, transformations, and helper utilities for building complex gesture-driven animations.

Pending
Overview
Eval results
Files

transitions.mddocs/

Transition Hooks

React hooks for creating smooth transitions with spring and timing animations, providing convenient wrappers around React Native Reanimated's animation functions.

import type { WithSpringConfig, WithTimingConfig } from "react-native-reanimated";

Capabilities

Spring Transitions

Create spring-based transitions that respond to state changes with natural, physics-based motion.

/**
 * Create spring transition based on state
 * @param state - Boolean or numeric state to animate
 * @param config - Optional spring configuration
 * @returns Animated value that springs to target
 */
function useSpring(
  state: boolean | number,
  config?: WithSpringConfig
): Animated.DerivedValue<number>;

Usage Example:

import { useSpring } from "react-native-redash";
import { useAnimatedStyle } from "react-native-reanimated";
import { useState } from "react";

export const SpringButton = () => {
  const [isPressed, setIsPressed] = useState(false);
  
  // Spring animation for scale
  const scale = useSpring(isPressed, {
    damping: 15,
    stiffness: 200,
    mass: 1
  });
  
  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }]
  }));
  
  return (
    <Animated.View
      style={[{ width: 100, height: 100, backgroundColor: 'blue' }, animatedStyle]}
      onTouchStart={() => setIsPressed(true)}
      onTouchEnd={() => setIsPressed(false)}
    />
  );
};

Timing Transitions

Create timing-based transitions with customizable duration and easing curves.

/**
 * Create timing transition based on state
 * @param state - Boolean or numeric state to animate
 * @param config - Optional timing configuration
 * @returns Animated value that times to target
 */
function useTiming(
  state: boolean | number,
  config?: WithTimingConfig
): Animated.DerivedValue<number>;

Usage Example:

import { useTiming } from "react-native-redash";
import { useAnimatedStyle } from "react-native-reanimated";
import { Easing } from "react-native-reanimated";
import { useState } from "react";

export const TimingTransition = () => {
  const [isVisible, setIsVisible] = useState(false);
  
  // Timing animation for opacity
  const opacity = useTiming(isVisible, {
    duration: 300,
    easing: Easing.inOut(Easing.ease)
  });
  
  // Timing animation for position
  const translateY = useTiming(isVisible ? 0 : 100, {
    duration: 500,
    easing: Easing.out(Easing.cubic)
  });
  
  const animatedStyle = useAnimatedStyle(() => ({
    opacity: opacity.value,
    transform: [{ translateY: translateY.value }]
  }));
  
  return (
    <>
      <TouchableOpacity onPress={() => setIsVisible(!isVisible)}>
        <Text>Toggle</Text>
      </TouchableOpacity>
      
      <Animated.View
        style={[
          { width: 100, height: 100, backgroundColor: 'red' },
          animatedStyle
        ]}
      />
    </>
  );
};

Numeric State Transitions

Both hooks work with numeric values, not just booleans.

import { useSpring, useTiming } from "react-native-redash";
import { useState } from "react";

export const NumericTransitions = () => {
  const [value, setValue] = useState(0);
  
  // Spring to numeric value
  const springValue = useSpring(value * 100, {
    damping: 20,
    stiffness: 100
  });
  
  // Timing to numeric value
  const timingValue = useTiming(value * 200, {
    duration: 1000
  });
  
  const animatedStyle = useAnimatedStyle(() => ({
    transform: [
      { translateX: springValue.value },
      { translateY: timingValue.value }
    ]
  }));
  
  return (
    <View>
      <Slider
        value={value}
        onValueChange={setValue}
        minimumValue={0}
        maximumValue={1}
      />
      <Animated.View style={[styles.box, animatedStyle]} />
    </View>
  );
};

Complex State-Based Animations

Combine multiple transition hooks for complex animations.

import { useSpring, useTiming } from "react-native-redash";
import { useState, useEffect } from "react";

export const ComplexTransition = () => {
  const [currentTab, setCurrentTab] = useState(0);
  const [isLoading, setIsLoading] = useState(false);
  
  // Spring animation for tab indicator
  const tabPosition = useSpring(currentTab * 100, {
    damping: 25,
    stiffness: 300
  });
  
  // Timing animation for loading state
  const loadingOpacity = useTiming(isLoading, {
    duration: 200
  });
  
  // Spring animation for content scale
  const contentScale = useSpring(isLoading ? 0.95 : 1, {
    damping: 20,
    stiffness: 200
  });
  
  const tabIndicatorStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: tabPosition.value }]
  }));
  
  const contentStyle = useAnimatedStyle(() => ({
    opacity: 1 - loadingOpacity.value * 0.5,
    transform: [{ scale: contentScale.value }]
  }));
  
  const loadingStyle = useAnimatedStyle(() => ({
    opacity: loadingOpacity.value
  }));
  
  return (
    <View>
      {/* Tab Indicator */}
      <Animated.View style={[styles.tabIndicator, tabIndicatorStyle]} />
      
      {/* Content */}
      <Animated.View style={[styles.content, contentStyle]}>
        {/* Content here */}
      </Animated.View>
      
      {/* Loading Overlay */}
      <Animated.View style={[styles.loadingOverlay, loadingStyle]}>
        <ActivityIndicator />
      </Animated.View>
    </View>
  );
};

Performance Considerations

Both hooks automatically handle state changes and create smooth transitions:

import { useSpring } from "react-native-redash";
import { useState, useCallback } from "react";

export const PerformantComponent = () => {
  const [count, setCount] = useState(0);
  
  // Automatically animates when count changes
  const animatedCount = useSpring(count, {
    damping: 15,
    stiffness: 100
  });
  
  // Memoize handlers to prevent unnecessary re-renders
  const increment = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);
  
  const animatedStyle = useAnimatedStyle(() => ({
    transform: [
      { scale: 1 + animatedCount.value * 0.1 },
      { rotate: `${animatedCount.value * 10}deg` }
    ]
  }));
  
  return (
    <TouchableOpacity onPress={increment}>
      <Animated.View style={[styles.counter, animatedStyle]}>
        <Text>{count}</Text>
      </Animated.View>
    </TouchableOpacity>
  );
};

Configuration Options:

The hooks accept the same configuration options as React Native Reanimated's withSpring and withTiming. These types are imported from react-native-reanimated:

WithSpringConfig:

  • damping?: number - Damping coefficient (default: 10)
  • mass?: number - Mass of the object (default: 1)
  • stiffness?: number - Spring stiffness (default: 100)
  • overshootClamping?: boolean - Prevent overshooting (default: false)
  • restDisplacementThreshold?: number - Threshold for rest detection (default: 0.01)
  • restSpeedThreshold?: number - Speed threshold for rest detection (default: 2)

WithTimingConfig:

  • duration?: number - Animation duration in milliseconds (default: 300)
  • easing?: EasingFunction - Easing function from react-native-reanimated (default: Easing.inOut(Easing.quad))

Best Practices:

  1. Use useSpring for interactive elements (buttons, toggles, gestures)
  2. Use useTiming for sequential animations or precise timing control
  3. Combine both for rich, layered animations
  4. Keep configuration objects stable to avoid unnecessary re-animations
  5. Use numeric states for smooth value transitions
  6. Boolean states automatically convert to 0/1 for animations

Install with Tessl CLI

npx tessl i tessl/npm-react-native-redash

docs

animations.md

colors.md

components.md

coordinates.md

index.md

math.md

matrices.md

paths.md

transforms.md

transitions.md

utilities.md

vectors.md

tile.json