Animation Functions
Custom animation functions for controlling how draggable items animate during and after drag operations.
Overview
Animation functions allow you to customize how draggable items animate when they return to their original position or snap to a drop zone. The library uses React Native Reanimated for smooth, performant animations that run on the UI thread.
AnimationFunction Type
type AnimationFunction = (toValue: number) => number;
Animation functions are worklets that take a target value and return an animated value using Reanimated's animation functions.
Parameters
toValue
: The target value to animate to (typically 0 for returning to original position)
Returns
An animated value created using Reanimated animation functions like withSpring
, withTiming
, etc.
Built-in Animation Patterns
Spring Animations
Spring animations provide natural, bouncy motion that feels responsive and organic.
Basic Spring
const springAnimation = (toValue) => {
'worklet';
return withSpring(toValue);
};
<Draggable
data={data}
animationFunction={springAnimation}
>
<Text>Spring animation</Text>
</Draggable>
Custom Spring Configuration
const customSpring = (toValue) => {
'worklet';
return withSpring(toValue, {
damping: 15, // Controls oscillation (higher = less bouncy)
stiffness: 150, // Controls speed (higher = faster)
mass: 1, // Controls inertia (higher = slower)
overshootClamping: false, // Allow overshooting
restDisplacementThreshold: 0.01,
restSpeedThreshold: 2,
});
};
Bouncy Spring
const bouncySpring = (toValue) => {
'worklet';
return withSpring(toValue, {
damping: 8, // Low damping for more bounce
stiffness: 100,
mass: 0.8,
});
};
Gentle Spring
const gentleSpring = (toValue) => {
'worklet';
return withSpring(toValue, {
damping: 20, // High damping for smooth motion
stiffness: 120,
mass: 1.2,
});
};
Timing Animations
Timing animations provide precise control over duration and easing curves.
Basic Timing
const timingAnimation = (toValue) => {
'worklet';
return withTiming(toValue, {
duration: 300,
easing: Easing.out(Easing.cubic),
});
};
Elastic Timing
import { Easing } from 'react-native-reanimated';
const elasticTiming = (toValue) => {
'worklet';
return withTiming(toValue, {
duration: 600,
easing: Easing.elastic(2), // Elastic effect
});
};
Bounce Timing
const bounceTiming = (toValue) => {
'worklet';
return withTiming(toValue, {
duration: 800,
easing: Easing.bounce,
});
};
Smooth Timing
const smoothTiming = (toValue) => {
'worklet';
return withTiming(toValue, {
duration: 250,
easing: Easing.bezier(0.25, 0.1, 0.25, 1), // Material Design curve
});
};
Advanced Animation Patterns
Sequential Animations
const sequentialAnimation = (toValue) => {
'worklet';
return withSequence(
withTiming(toValue * 1.1, { duration: 100 }), // Slight overshoot
withSpring(toValue, { damping: 15 }) // Spring back
);
};
Delayed Animations
const delayedAnimation = (toValue) => {
'worklet';
return withDelay(
200, // 200ms delay
withSpring(toValue)
);
};
Repeated Animations
const pulseAnimation = (toValue) => {
'worklet';
return withRepeat(
withSequence(
withTiming(toValue * 0.95, { duration: 150 }),
withTiming(toValue, { duration: 150 })
),
3, // Repeat 3 times
false // Don't reverse
);
};
Conditional Animations
const conditionalAnimation = (toValue) => {
'worklet';
// Different animation based on target value
if (toValue === 0) {
// Returning to original position - use spring
return withSpring(toValue, { damping: 15 });
} else {
// Snapping to drop zone - use timing
return withTiming(toValue, { duration: 200 });
}
};
Context-Aware Animations
State-Based Animations
function StatefulDraggable({ data, isSuccess }) {
const getAnimation = useCallback((toValue) => {
'worklet';
if (isSuccess) {
// Success animation - bouncy and celebratory
return withSequence(
withTiming(toValue * 1.2, { duration: 100 }),
withSpring(toValue, { damping: 8, stiffness: 150 })
);
} else {
// Default animation - smooth return
return withSpring(toValue, { damping: 15 });
}
}, [isSuccess]);
return (
<Draggable
data={data}
animationFunction={getAnimation}
>
<Text>State-aware animation</Text>
</Draggable>
);
}
Performance-Based Animations
function PerformanceDraggable({ data, isLowEndDevice }) {
const getAnimation = useCallback((toValue) => {
'worklet';
if (isLowEndDevice) {
// Simple, fast animation for low-end devices
return withTiming(toValue, { duration: 150 });
} else {
// Rich animation for high-end devices
return withSequence(
withTiming(toValue * 1.1, { duration: 100 }),
withSpring(toValue, { damping: 12, stiffness: 200 })
);
}
}, [isLowEndDevice]);
return (
<Draggable
data={data}
animationFunction={getAnimation}
>
<Text>Performance-aware animation</Text>
</Draggable>
);
}
Animation Presets
Material Design Animations
// Material Design motion curves
const materialAnimations = {
standard: (toValue) => {
'worklet';
return withTiming(toValue, {
duration: 300,
easing: Easing.bezier(0.4, 0.0, 0.2, 1),
});
},
decelerate: (toValue) => {
'worklet';
return withTiming(toValue, {
duration: 250,
easing: Easing.bezier(0.0, 0.0, 0.2, 1),
});
},
accelerate: (toValue) => {
'worklet';
return withTiming(toValue, {
duration: 200,
easing: Easing.bezier(0.4, 0.0, 1, 1),
});
},
};
iOS-Style Animations
const iOSAnimations = {
default: (toValue) => {
'worklet';
return withSpring(toValue, {
damping: 20,
stiffness: 300,
mass: 1,
});
},
gentle: (toValue) => {
'worklet';
return withTiming(toValue, {
duration: 350,
easing: Easing.out(Easing.cubic),
});
},
snappy: (toValue) => {
'worklet';
return withSpring(toValue, {
damping: 15,
stiffness: 400,
mass: 0.8,
});
},
};
Game-Style Animations
const gameAnimations = {
powerUp: (toValue) => {
'worklet';
return withSequence(
withTiming(toValue * 1.3, { duration: 100 }),
withTiming(toValue * 0.9, { duration: 100 }),
withSpring(toValue, { damping: 10 })
);
},
impact: (toValue) => {
'worklet';
return withSequence(
withTiming(toValue * 0.8, { duration: 50 }),
withSpring(toValue, { damping: 8, stiffness: 200 })
);
},
float: (toValue) => {
'worklet';
return withRepeat(
withSequence(
withTiming(toValue - 5, { duration: 1000 }),
withTiming(toValue + 5, { duration: 1000 })
),
-1, // Infinite
true // Reverse
);
},
};
Dynamic Animation Selection
Animation Factory
class AnimationFactory {
static create(type: string, options: any = {}) {
const animations = {
spring: (toValue) => {
'worklet';
return withSpring(toValue, {
damping: options.damping || 15,
stiffness: options.stiffness || 150,
...options
});
},
timing: (toValue) => {
'worklet';
return withTiming(toValue, {
duration: options.duration || 300,
easing: options.easing || Easing.out(Easing.cubic),
...options
});
},
bounce: (toValue) => {
'worklet';
return withTiming(toValue, {
duration: options.duration || 600,
easing: Easing.bounce,
...options
});
},
elastic: (toValue) => {
'worklet';
return withTiming(toValue, {
duration: options.duration || 800,
easing: Easing.elastic(options.elasticity || 2),
...options
});
},
};
return animations[type] || animations.spring;
}
}
// Usage
function ConfigurableDraggable({ animationType, animationOptions }) {
const animation = AnimationFactory.create(animationType, animationOptions);
return (
<Draggable
data={data}
animationFunction={animation}
>
<Text>Configurable animation</Text>
</Draggable>
);
}
Theme-Based Animations
const useThemeAnimations = (theme) => {
return useMemo(() => {
const animations = {
light: {
gentle: (toValue) => {
'worklet';
return withSpring(toValue, { damping: 20, stiffness: 200 });
},
quick: (toValue) => {
'worklet';
return withTiming(toValue, { duration: 200 });
},
},
dark: {
smooth: (toValue) => {
'worklet';
return withTiming(toValue, {
duration: 300,
easing: Easing.out(Easing.cubic)
});
},
dramatic: (toValue) => {
'worklet';
return withSequence(
withTiming(toValue * 1.2, { duration: 150 }),
withSpring(toValue, { damping: 10 })
);
},
},
playful: {
bounce: (toValue) => {
'worklet';
return withSpring(toValue, { damping: 8, stiffness: 150 });
},
wiggle: (toValue) => {
'worklet';
return withSequence(
withTiming(toValue + 5, { duration: 50 }),
withTiming(toValue - 5, { duration: 50 }),
withSpring(toValue)
);
},
},
};
return animations[theme] || animations.light;
}, [theme]);
};
Performance Considerations
Optimized Animations
// Prefer spring animations for better performance
const optimizedSpring = (toValue) => {
'worklet';
return withSpring(toValue, {
damping: 15,
stiffness: 150,
// Reduce precision for better performance
restDisplacementThreshold: 0.1,
restSpeedThreshold: 5,
});
};
// Use shorter durations for timing animations
const optimizedTiming = (toValue) => {
'worklet';
return withTiming(toValue, {
duration: 200, // Shorter duration
easing: Easing.out(Easing.quad), // Simpler easing
});
};
Conditional Performance
const adaptiveAnimation = (toValue) => {
'worklet';
// Use simpler animations during heavy operations
const isHeavyOperation = global.isHeavyOperation || false;
if (isHeavyOperation) {
return withTiming(toValue, { duration: 100 });
} else {
return withSpring(toValue, { damping: 15 });
}
};
Testing Animations
Animation Testing Utilities
const createTestAnimation = (duration = 100) => {
return (toValue) => {
'worklet';
return withTiming(toValue, { duration });
};
};
// Use in tests
function TestDraggable() {
const isTest = process.env.NODE_ENV === 'test';
const animation = isTest
? createTestAnimation(0) // Instant for tests
: springAnimation; // Normal for app
return (
<Draggable
data={data}
animationFunction={animation}
>
<Text>Test-friendly animation</Text>
</Draggable>
);
}
Best Practices
Animation Guidelines
- Use spring animations for natural feel
- Keep durations under 500ms for responsiveness
- Match animations to context (gentle for forms, bouncy for games)
- Test on low-end devices to ensure smooth performance
- Provide reduced motion options for accessibility
Common Patterns
// Good: Contextual animation selection
const getContextualAnimation = (context) => {
if (context.isForm) {
return gentleSpring;
} else if (context.isGame) {
return bouncySpring;
} else {
return standardSpring;
}
};
// Good: Performance-aware animations
const getPerformanceAnimation = (deviceCapability) => {
return deviceCapability === 'high'
? richAnimation
: simpleAnimation;
};
// Avoid: Overly complex animations
const avoidComplexAnimation = (toValue) => {
'worklet';
// Too many nested animations can hurt performance
return withSequence(
withRepeat(withTiming(toValue * 1.1, { duration: 50 }), 5),
withDelay(100, withSpring(toValue)),
withRepeat(withTiming(toValue * 0.9, { duration: 25 }), 3)
);
};
See Also
- Draggable Component - Using animation functions
- useDraggable Hook - Hook-level animation configuration
- React Native Reanimated - Animation library documentation
- Performance Guide - Performance optimization tips