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

19. Анимации (Animated API)

React Native имеет встроенный Animated API для плавных анимаций. Для сложных — Reanimated 3 (следующий урок).

import { Animated } from 'react-native';
import { useRef, useEffect } from 'react';
function FadeIn({ children }) {
const opacity = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 500,
useNativeDriver: true, // ОБЯЗАТЕЛЬНО для transform и opacity!
}).start();
}, []);
return <Animated.View style={{ opacity }}>{children}</Animated.View>;
}
// Линейная
Animated.timing(value, { toValue: 1, duration: 300, useNativeDriver: true });
// Spring (пружина)
Animated.spring(value, { toValue: 1, tension: 40, friction: 7, useNativeDriver: true });
// Decay (затухание)
Animated.decay(value, { velocity: 0.5, deceleration: 0.997, useNativeDriver: true });
const rotation = opacity.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const color = progress.interpolate({
inputRange: [0, 0.5, 1],
outputRange: ['#f38ba8', '#f9e2af', '#a6e3a1'],
});
<Animated.View style={{ opacity, transform: [{ rotate: rotation }] }} />
// Последовательно
Animated.sequence([
Animated.timing(opacity, { toValue: 1, duration: 300, useNativeDriver: true }),
Animated.timing(translateY, { toValue: 0, duration: 500, useNativeDriver: true }),
]).start();
// Параллельно
Animated.parallel([
Animated.timing(opacity, { toValue: 1, useNativeDriver: true }),
Animated.spring(scale, { toValue: 1, useNativeDriver: true }),
]).start();
// Stagger (с задержкой)
Animated.stagger(100, items.map(item =>
Animated.timing(item.opacity, { toValue: 1, useNativeDriver: true })
)).start();

Автоматически анимирует изменения layout:

import { LayoutAnimation, UIManager, Platform } from 'react-native';
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
function ExpandableCard() {
const [expanded, setExpanded] = useState(false);
const toggle = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setExpanded(e => !e);
};
return (
<TouchableOpacity onPress={toggle}>
<View style={{ height: expanded ? 200 : 80 }}>
<Text>Нажми для раскрытия</Text>
</View>
</TouchableOpacity>
);
}
function Spinner() {
const rotation = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.loop(
Animated.timing(rotation, { toValue: 1, duration: 1000, useNativeDriver: true })
).start();
}, []);
const rotate = rotation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
return <Animated.View style={{ transform: [{ rotate }] }}><Text>⚙️</Text></Animated.View>;
}