Перейти к содержимому

20. React Native Reanimated

Reanimated 3 — анимации полностью в нативном потоке. 60/120fps без JS. Замена встроенного Animated API для сложных анимаций.

Окно терминала
npx expo install react-native-reanimated
// babel.config.js — плагин ОБЯЗАТЕЛЕН (в конце массива!)
module.exports = {
presets: ['babel-preset-expo'],
plugins: ['react-native-reanimated/plugin'],
};
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withSpring,
withRepeat,
withSequence,
runOnJS,
} from 'react-native-reanimated';
const offset = useSharedValue(0);
// Анимация
offset.value = withTiming(100, { duration: 300 });
offset.value = withSpring(0, { damping: 10, stiffness: 100 });
// Стили на основе sharedValue
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateX: offset.value }],
opacity: offset.value / 100,
}));
<Animated.View style={[styles.box, animatedStyle]} />
// Fade in при монтировании
function FadeIn({ children }) {
const opacity = useSharedValue(0);
useEffect(() => {
opacity.value = withTiming(1, { duration: 500 });
}, []);
const style = useAnimatedStyle(() => ({ opacity: opacity.value }));
return <Animated.View style={style}>{children}</Animated.View>;
}
// Bounce кнопка
function BounceButton({ onPress, title }) {
const scale = useSharedValue(1);
const style = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }] }));
const handlePress = () => {
scale.value = withSequence(
withTiming(0.9, { duration: 80 }),
withSpring(1, { damping: 3, stiffness: 200 })
);
runOnJS(onPress)();
};
return (
<AnimatedPressable style={style} onPress={handlePress}>
<Text>{title}</Text>
</AnimatedPressable>
);
}
// Shake (ошибка ввода)
offset.value = withSequence(
withTiming(-10, { duration: 60 }),
withTiming(10, { duration: 60 }),
withTiming(-10, { duration: 60 }),
withSpring(0),
);
// Pulse (бесконечная)
scale.value = withRepeat(
withSequence(
withTiming(1.2, { duration: 300 }),
withTiming(1, { duration: 300 })
),
-1, // -1 = бесконечно
true // reverse
);
import { useAnimatedScrollHandler } from 'react-native-reanimated';
function ParallaxHeader() {
const scrollY = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => { scrollY.value = event.contentOffset.y; },
});
const headerStyle = useAnimatedStyle(() => ({
transform: [{ translateY: scrollY.value * 0.5 }],
opacity: 1 - scrollY.value / 200,
}));
return (
<>
<Animated.View style={[styles.header, headerStyle]} />
<Animated.ScrollView onScroll={scrollHandler} scrollEventThrottle={16}>
{/* Контент */}
</Animated.ScrollView>
</>
);
}
import Animated, { FadeIn, FadeOut, SlideInRight, BounceIn } from 'react-native-reanimated';
// Автоматическая анимация появления/исчезновения
<Animated.View entering={FadeIn.duration(500)} exiting={FadeOut}>
<Text>Появлюсь с fade</Text>
</Animated.View>
<Animated.View entering={SlideInRight.delay(200)} exiting={FadeOut}>
<Text>Слайд справа</Text>
</Animated.View>
<Animated.View entering={BounceIn} exiting={FadeOut}>
<Text>Bounce!</Text>
</Animated.View>
  • Reanimated 3 работает полностью в нативном потоке
  • Reanimated 3 работает полностью в нативном потоке
  • useSharedValue вместо Animated.Value
  • useAnimatedStyle — стили вычисляются нативно
  • withTiming, withSpring — анимации
  • withSequence, withRepeat — комбинации
  • entering / exiting — декларативные layout анимации
  • runOnJS() — вызов JS из нативного потока