Claude Code Plugins

Community-maintained marketplace

Feedback

reanimated-patterns

@IvanTorresEdge/molcajete.ai
0
0

React Native Reanimated animation patterns. Use when implementing animations.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name reanimated-patterns
description React Native Reanimated animation patterns. Use when implementing animations.

Reanimated Patterns Skill

This skill covers React Native Reanimated for performant animations.

When to Use

Use this skill when:

  • Implementing smooth animations
  • Gesture-based interactions
  • Complex animation sequences
  • Performance-critical animations

Core Principle

UI THREAD ANIMATIONS - Reanimated runs animations on the UI thread for 60fps.

Installation

npx expo install react-native-reanimated

Update babel.config.js:

module.exports = function(api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: ['react-native-reanimated/plugin'],
  };
};

Basic Concepts

Shared Values

import { useSharedValue } from 'react-native-reanimated';

// Shared values are synchronized between JS and UI threads
const scale = useSharedValue(1);
const opacity = useSharedValue(0);
const translateX = useSharedValue(0);

// Update shared values
scale.value = 2;
opacity.value = 1;

Animated Styles

import Animated, {
  useSharedValue,
  useAnimatedStyle,
} from 'react-native-reanimated';

function AnimatedBox(): React.ReactElement {
  const scale = useSharedValue(1);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  return (
    <Animated.View style={[styles.box, animatedStyle]} />
  );
}

Animation Functions

withSpring

import { withSpring } from 'react-native-reanimated';

// Spring animation (natural bounce)
scale.value = withSpring(2, {
  damping: 10,
  stiffness: 100,
  mass: 1,
});

withTiming

import { withTiming, Easing } from 'react-native-reanimated';

// Timed animation
opacity.value = withTiming(1, {
  duration: 300,
  easing: Easing.bezier(0.25, 0.1, 0.25, 1),
});

withDelay

import { withDelay, withSpring } from 'react-native-reanimated';

// Delayed animation
scale.value = withDelay(500, withSpring(2));

withSequence

import { withSequence, withTiming } from 'react-native-reanimated';

// Sequential animations
scale.value = withSequence(
  withTiming(1.2, { duration: 100 }),
  withTiming(0.9, { duration: 100 }),
  withTiming(1, { duration: 100 })
);

withRepeat

import { withRepeat, withTiming } from 'react-native-reanimated';

// Repeating animation
rotation.value = withRepeat(
  withTiming(360, { duration: 1000 }),
  -1,  // -1 for infinite
  false // reverse
);

Common Animations

Fade In/Out

import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming,
} from 'react-native-reanimated';

function FadeView({ visible }: { visible: boolean }): React.ReactElement {
  const opacity = useSharedValue(0);

  useEffect(() => {
    opacity.value = withTiming(visible ? 1 : 0, { duration: 300 });
  }, [visible]);

  const animatedStyle = useAnimatedStyle(() => ({
    opacity: opacity.value,
  }));

  return (
    <Animated.View style={animatedStyle}>
      {children}
    </Animated.View>
  );
}

Scale on Press

function ScaleButton({ onPress, children }: Props): React.ReactElement {
  const scale = useSharedValue(1);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  const handlePressIn = () => {
    scale.value = withSpring(0.95);
  };

  const handlePressOut = () => {
    scale.value = withSpring(1);
  };

  return (
    <TouchableOpacity
      onPressIn={handlePressIn}
      onPressOut={handlePressOut}
      onPress={onPress}
      activeOpacity={1}
    >
      <Animated.View style={animatedStyle}>
        {children}
      </Animated.View>
    </TouchableOpacity>
  );
}

Slide In

import { Dimensions } from 'react-native';

const { width } = Dimensions.get('window');

function SlideIn({ visible }: { visible: boolean }): React.ReactElement {
  const translateX = useSharedValue(width);

  useEffect(() => {
    translateX.value = withSpring(visible ? 0 : width);
  }, [visible]);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: translateX.value }],
  }));

  return (
    <Animated.View style={[styles.panel, animatedStyle]}>
      {children}
    </Animated.View>
  );
}

Entering/Exiting Animations

import Animated, {
  FadeIn,
  FadeOut,
  SlideInRight,
  SlideOutLeft,
  ZoomIn,
  BounceIn,
} from 'react-native-reanimated';

// Fade
<Animated.View entering={FadeIn.duration(300)} exiting={FadeOut.duration(200)}>

// Slide
<Animated.View entering={SlideInRight} exiting={SlideOutLeft}>

// Zoom
<Animated.View entering={ZoomIn.springify()}>

// Bounce
<Animated.View entering={BounceIn.delay(200)}>

// Custom
<Animated.View
  entering={FadeIn.duration(500).delay(100).springify()}
  exiting={FadeOut.duration(300)}
>

Layout Animations

import Animated, { Layout } from 'react-native-reanimated';

// Animate layout changes
<Animated.View layout={Layout.springify()}>
  {items.map((item) => (
    <Animated.View
      key={item.id}
      entering={FadeIn}
      exiting={FadeOut}
      layout={Layout}
    >
      <Text>{item.name}</Text>
    </Animated.View>
  ))}
</Animated.View>

Gesture Handler Integration

import { GestureDetector, Gesture } from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';

function DraggableBox(): React.ReactElement {
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);

  const gesture = Gesture.Pan()
    .onUpdate((event) => {
      translateX.value = event.translationX;
      translateY.value = event.translationY;
    })
    .onEnd(() => {
      translateX.value = withSpring(0);
      translateY.value = withSpring(0);
    });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [
      { translateX: translateX.value },
      { translateY: translateY.value },
    ],
  }));

  return (
    <GestureDetector gesture={gesture}>
      <Animated.View style={[styles.box, animatedStyle]} />
    </GestureDetector>
  );
}

Interpolation

import { interpolate, Extrapolation } from 'react-native-reanimated';

const animatedStyle = useAnimatedStyle(() => {
  const scale = interpolate(
    scrollY.value,
    [0, 100],
    [1, 0.5],
    Extrapolation.CLAMP
  );

  const opacity = interpolate(
    scrollY.value,
    [0, 50, 100],
    [1, 0.5, 0]
  );

  return {
    transform: [{ scale }],
    opacity,
  };
});

Worklets

import { runOnJS } from 'react-native-reanimated';

// Call JS function from worklet
const updateState = (value: number) => {
  setState(value);
};

const animatedStyle = useAnimatedStyle(() => {
  if (position.value > 100) {
    runOnJS(updateState)(position.value);
  }
  return { transform: [{ translateX: position.value }] };
});

Notes

  • All animation logic runs on UI thread
  • Use worklets ('worklet' directive) for custom functions
  • Use runOnJS to call JS functions from worklets
  • Test animations on real devices
  • Reanimated is compatible with Expo