Animasi di React Native
react-native-reanimated adalah library animasi yang berjalan di UI thread — bukan JS thread. Hasilnya animasi 60fps yang smooth bahkan saat JS thread sibuk.
Setup
# Install
npx expo install react-native-reanimated
// babel.config.js — tambahkan plugin
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-reanimated/plugin'], // harus terakhir!
};
};
Basic Animation: useSharedValue + useAnimatedStyle
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
withTiming,
} from 'react-native-reanimated';
function BouncyBox() {
const scale = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
const handlePress = () => {
// Spring animation
scale.value = withSpring(1.5, {}, () => {
scale.value = withSpring(1);
});
};
return (
<Pressable onPress={handlePress}>
<Animated.View style={[styles.box, animatedStyle]} />
</Pressable>
);
}
Entering & Exiting Animations
import Animated, {
FadeIn,
FadeOut,
SlideInRight,
BounceIn,
} from 'react-native-reanimated';
function AnimatedCard({ visible }) {
if (!visible) return null;
return (
<Animated.View
entering={SlideInRight.duration(300).springify()}
exiting={FadeOut.duration(200)}
style={styles.card}
>
<Text>Animated Card</Text>
</Animated.View>
);
}
// Layout Animations (auto-animate layout changes)
<Animated.View layout={LinearTransition.springify()}>
{items.map(item => (
<Animated.View
key={item.id}
entering={FadeIn}
exiting={FadeOut}
layout={LinearTransition}
>
<Text>{item.name}</Text>
</Animated.View>
))}
</Animated.View>
Gesture Handler + Reanimated
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
useSharedValue,
useAnimatedStyle,
withDecay,
} from 'react-native-reanimated';
function DraggableBox() {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const pan = Gesture.Pan()
.onUpdate((e) => {
translateX.value = e.translationX;
translateY.value = e.translationY;
})
.onEnd((e) => {
// Momentum/decay setelah release
translateX.value = withDecay({ velocity: e.velocityX });
translateY.value = withDecay({ velocity: e.velocityY });
});
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{ translateX: translateX.value },
{ translateY: translateY.value },
],
}));
return (
<GestureDetector gesture={pan}>
<Animated.View style={[styles.box, animatedStyle]} />
</GestureDetector>
);
}
Tips Animasi Performant
- Gunakan
useSharedValue— Bukan useState, karena shared value berjalan di UI thread - Hindari re-render — Animasi Reanimated tidak trigger React re-render
- worklet — Fungsi yang berjalan di UI thread, ditandai otomatis oleh Reanimated plugin
- Spring > Timing — Spring animation terasa lebih natural di mobile