┌───────────────────────────────┐ │ Spring and Decay Animations │ ├───────────────────────────────┤ │ │ │ ○ │ │ │ │ Drag the circle left or right │ │ Release to see spring or decay│ └───────────────────────────────┘
Spring and decay animations in React Native - Mini App: Build & Ship
import React, { useRef } from 'react'; import { View, Text, Animated, PanResponder, StyleSheet } from 'react-native'; export default function SpringDecayAnimationScreen() { const translateX = useRef(new Animated.Value(0)).current; const panResponder = useRef( PanResponder.create({ onStartShouldSetPanResponder: () => true, onPanResponderMove: (e, gestureState) => { // TODO: Update translateX with gestureState.dx }, onPanResponderRelease: (e, gestureState) => { // TODO: Animate translateX with spring or decay based on velocity }, }) ).current; return ( <View style={styles.container}> <Animated.View style={[styles.circle, { transform: [{ translateX }] }]} {...panResponder.panHandlers} /> <Text style={styles.instructions}> Drag the circle left or right{' '}Release to see spring or decay </Text> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#fff', }, circle: { width: 80, height: 80, borderRadius: 40, backgroundColor: '#4a90e2', }, instructions: { marginTop: 20, fontSize: 16, color: '#333', textAlign: 'center', }, });
import React, { useRef } from 'react'; import { View, Text, Animated, PanResponder, StyleSheet } from 'react-native'; export default function SpringDecayAnimationScreen() { const translateX = useRef(new Animated.Value(0)).current; const panResponder = useRef( PanResponder.create({ onStartShouldSetPanResponder: () => true, onPanResponderMove: (e, gestureState) => { translateX.setValue(gestureState.dx); }, onPanResponderRelease: (e, gestureState) => { const velocity = gestureState.vx; if (Math.abs(velocity) < 0.5) { Animated.spring(translateX, { toValue: 0, useNativeDriver: false, bounciness: 10, }).start(); } else { Animated.decay(translateX, { velocity: velocity, deceleration: 0.997, useNativeDriver: false, }).start(); } }, }) ).current; return ( <View style={styles.container}> <Animated.View style={[styles.circle, { transform: [{ translateX }] }]} {...panResponder.panHandlers} accessibilityLabel="Draggable circle" accessible={true} /> <Text style={styles.instructions} accessibilityRole="text"> Drag the circle left or right{' '}Release to see spring or decay </Text> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#fff', }, circle: { width: 80, height: 80, borderRadius: 40, backgroundColor: '#4a90e2', }, instructions: { marginTop: 20, fontSize: 16, color: '#333', textAlign: 'center', }, });
This solution uses React Native's Animated API to create a draggable circle that responds to user gestures.
We use PanResponder to track the horizontal drag movement and update the translateX animated value accordingly.
When the user releases the drag, we check the horizontal velocity (gestureState.vx). If the velocity is small (less than 0.5), we animate the circle back to the center using a spring animation, which gives a natural bounce effect.
If the velocity is larger, we use a decay animation that simulates momentum, letting the circle continue moving and gradually slow down.
We set useNativeDriver: false because the translation affects layout and gesture handling.
Accessibility labels are added for screen readers, and the UI is simple and clear for easy interaction.
┌───────────────────────────────┐ │ Spring and Decay Animations │ ├───────────────────────────────┤ │ │ │ ○ │ │ │ │ Drag the circle left or right │ │ Release to see spring or decay│ └───────────────────────────────┘
Practice
Solution
Step 1: Understand spring animation behavior
Spring animations mimic real-world springs, causing bouncy, natural movements.Step 2: Compare with other animation types
Unlike decay or timing animations, spring animations create a bounce effect.Final Answer:
To create a bouncy and natural movement effect -> Option AQuick Check:
Spring animation = bouncy movement [OK]
- Confusing spring with decay animation
- Thinking spring is for instant changes
- Assuming spring repeats infinitely
Solution
Step 1: Identify correct method for spring animation
The method is Animated.spring with parameters including toValue and then .start() to begin.Step 2: Check method usage and syntax
Animated.decay is for decay animations, .run() is not a valid method, and duration is not used in spring.Final Answer:
Animated.spring(value, {toValue: 1}).start() -> Option DQuick Check:
Use Animated.spring(...).start() for spring animation [OK]
- Using .run() instead of .start()
- Confusing decay with spring method
- Adding duration to spring config
const anim = new Animated.Value(0);
Animated.decay(anim, {velocity: 3, deceleration: 0.997}).start();
anim.addListener(({value}) => console.log(value));Solution
Step 1: Understand decay animation behavior
Decay animation starts with a velocity and slows down gradually due to deceleration.Step 2: Analyze given parameters
Velocity 3 means value increases fast initially, deceleration 0.997 slows it down over time.Final Answer:
The value will increase quickly then slow down gradually -> Option AQuick Check:
Decay animation = fast start + slow stop [OK]
- Thinking decay jumps instantly to velocity
- Assuming decay decreases value immediately
- Believing decay loops infinitely
const anim = new Animated.Value(0);
Animated.spring(anim, {toValue: 1, velocity: 5}).run();Solution
Step 1: Check method to start animation
Animated animations use .start() to begin, not .run().Step 2: Verify spring config properties
toValue and velocity are valid properties; initial value 0 is fine.Final Answer:
Using .run() instead of .start() to begin animation -> Option BQuick Check:
Use .start() to run animations [OK]
- Using .run() instead of .start()
- Removing toValue from config
- Initializing Animated.Value incorrectly
Solution
Step 1: Understand desired animation sequence
First move right with spring (bouncy), then slow down with decay on release.Step 2: Analyze options for correct order and methods
Animated.spring(anim, {toValue: 100}).start(() => Animated.decay(anim, {velocity: 2}).start()) runs spring first, then decay in callback, matching desired sequence.Step 3: Check other options
Animated.spring(anim, {toValue: 100, velocity: 2}).start() performs only spring without decay. Animated.decay(anim, {velocity: 2}).start(() => Animated.spring(anim, {toValue: 100}).start()) (Animated.decay(anim, {velocity: 2}).start(() => Animated.spring(anim, {toValue: 100}).start())) reverses order. Animated.timing(anim, {toValue: 100, duration: 500}).start(() => Animated.decay(anim, {velocity: 2}).start()) uses timing instead of spring.Final Answer:
Animated.spring(anim, {toValue: 100}).start(() => Animated.decay(anim, {velocity: 2}).start()) -> Option CQuick Check:
Spring then decay = move then slow [OK]
- Running decay before spring
- Performing only spring without decay
- Using timing instead of spring for bounce
Ready to prove it?
Pass the test and this pattern moves unknown
