
โ ์ง์ ๊ตฌํํด๋ณด๋ Animation
import { useEffect, useState } from "react";
import styled from "styled-components/native";
const Container = styled.View`
flex: 1;
justify-content: center;
align-items: center;
`;
const Box = styled.TouchableOpacity`
background-color: tomato;
width: 200px;
height: 200px;
`;
export default function App() {
const [y, setY] = useState(0);
const [intervalId, setIntervalId] = useState(null);
const moveUp = () => {
const id = setInterval(() => setY((prev) => prev + 1), 10);
setIntervalId(id);
};
useEffect(() => {
if (y === 200) {
clearInterval(intervalId);
}
}, [y, intervalId]);
console.log("rendering");
return (
<Container>
<Box onPress={moveUp} style={{ transform: [{ translateY: y }] }} />
</Container>
);
}
๊ธด ์ฝ๋์ ์ ๋๋ฉ์ด์ ์ด ์ด๋ฃจ์ด์ง ๋๋ง๋ค ๋ ๋๋ง์ ํ๋ ํผํฌ๋จผ์ค ๋ฌธ์ ๊ฐ ์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด React Native์ Animated๋ฅผ ์ฌ์ฉํ๋ค.
โ Animated
https://reactnative.dev/docs/animated
Animated · React Native
The Animated library is designed to make animations fluid, powerful, and painless to build and maintain. Animated focuses on declarative relationships between inputs and outputs, configurable transforms in between, and start/stop methods to control time-ba
reactnative.dev
1. animation์ ์ํ value๊ฐ ํ์ํ๋ค๋ฉด state๋ ๋ค๋ฅธ ๋ณ์๋ฅผ ์ฌ์ฉํ์ง ์๊ณ new Animated.Value()๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
const Y = new Animated.Value(0);
2. ์ ๋ value๋ฅผ ์ง์ ๋ณ๊ฒฝํ์ง ์๋๋ค. (๋ฐ 3๊ฐ์ง๋ฅผ ์ฌ์ฉํ์ฌ ๋ณ๊ฒฝํ๋ค.)
Animated.decay() starts with an initial velocity and gradually slows to a stop.
Animated.spring() provides a basic spring physics model.
Animated.timing() animates a value over time using easing functions.
3. ์ค์ง Animatable Components์๋ง animation์ ์ค ์ ์๋ค. (์๋ฌด๊ฑฐ์๋ ์ฆ์ animation์ ์ค ์ ์๋ค.)
ex)
Animated.Image
Animated.ScrollView
Animated.Text
Animated.View
Animated.FlatList
Animated.SectionList
animation์ ์ฃผ๊ณ ์ถ์ components๊ฐ ์์ ํด๋นํ์ง ์๋๋ค๋ฉด createAnimatedComponent()๋ฅผ ์ฌ์ฉํด์ animated components๋ก ๋ณ๊ฒฝํ๋ค.
<option 1>
const Box = styled(Animated.createAnimatedComponent(TouchableOpacity))`
background-color: tomato;
width: 200px;
height: 200px;
`;
๋จ์ : TouchableOpacity๋ฅผ import ํด์ผ ํ๋ค.
<option 2>
const Box = styled.TouchableOpacity`
background-color: tomato;
width: 200px;
height: 200px;
`;
const AnimatedBox = Animated.createAnimatedComponent(Box);
Usage
export default function App() {
const [up, setUp] = useState(false);
const Y_POSITION = useRef(new Animated.Value(300)).current;
const toggleUp = () => setUp((prev) => !prev);
const moveUp = () => {
Animated.timing(Y_POSITION, {
toValue: up ? 300 : -300,
useNativeDriver: true,
easing: Easing.cubic,
}).start(toggleUp);
};
const opacity = Y_POSITION.interpolate({
inputRange: [-300, 0, 300],
outputRange: [1, 0, 1],
});
Y_POSITION.addListener(() => console.log(opacity));
return (
<Container>
<AnimatedBox
onPress={moveUp}
style={{ opacity, transform: [{ translateY: Y_POSITION }] }}
/>
</Container>
);
}
extrapolate
interpolate์ inputRange๊ฐ ๋ฒ์๋ฅผ ์ด๊ณผํ์ ๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง ๋ช ์ํ ์ ์๋ ์์ฑ
const rotation = position.interpolate({
inputRange: [-windowWidth / 3, windowWidth / 3],
outputRange: ["-15deg", "15deg"],
extrapolate: "extend",
});
extend(default): ๋ฒ์๊ฐ ๋์ด๊ฐ๋ ๊ฐ์ ๋น์จ๋ก ๊ณ์ ๊ณ์ฐ
clamp: ๋ฒ์๊ฐ ๋์ด๊ฐ๋ฉด ๋๊ฐ์ผ๋ก ๊ณ ์
identity: input๊ฐ์ ๊ทธ๋๋ก output์ผ๋ก ๋ฐํ
getTranslateTransform()
translateY: position.y ํน์ translateX: position.x ์ด๋ ๊ฒ ์ธ ํ์ ์์ด getTranslateTransform()์ผ๋ก ๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์๋ค.
(translate ๊ฐ์ ๋ฐฐ์ด๋ก ๋ฐํ)
<Container>
<AnimatedBox
onPress={moveUp}
style={{
transform: [
...position.getTranslateTransform(),
{ rotate: rotation },
],
borderRadius,
backgroundColor,
}}
/>
</Container>
โ PanResponder
https://reactnative.dev/docs/panresponder
PanResponder · React Native
PanResponder reconciles several touches into a single gesture. It makes single-touch gestures resilient to extra touches, and can be used to recognize basic multi-touch gestures.
reactnative.dev
onStartShouldSetPanResponder
์ด ์์ฑ์ ํจ์๋ฅผ ๋ฐ์ผ๋ฉฐ, ํด๋น ํจ์๊ฐ true ์ฌ๋ถ์ ๋ฐ๋ผ View๊ฐ touch๋ฅผ ๊ฐ์งํ ์ง ๊ฒฐ์ ํด์ค๋ค.
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
}),
).current;
onPanResponderMove
์์ง์์ด ๊ฐ์ง๋์์ ๋ ๋์์ ์ค์ ํ ์ ์๋ ์์ฑ
(์ฝ๋ฐฑ ํจ์์ ์ธ์๋ก evt, gestureState๋ฅผ ๋ฐ๋๋ค)
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (_, { dx, dy }) => {
position.setValue({
x: dx,
y: dy,
});
},
}),
).current;
onPanResponderRelease
์์ง์์ด ๋๋ฌ์ ๋ ๋์์ ์ค์ ํ ์ ์๋ ์์ฑ
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (_, { dx, dy }) => {
position.setValue({
x: dx,
y: dy,
});
},
onPanResponderRelease: () => {
Animated.spring(position, {
toValue: {
x: 0,
y: 0,
},
useNativeDriver: true,
}).start();
},
}),
).current;
onPanResponderGrant
์ฌ์ฉ์๊ฐ ํฐ์น๋ฅผ ์์ํ ๋ ๋ฑ ํ ๋ฒ ํธ์ถ๋๋ค.
๋๋๊ทธ๋ฅผ ๋๋ด๊ณ ๋ค์ ๋๋๊ทธ๋ฅผ ์์ํ ๋๋ง๋ค 0, 0 ์์น๋ก ๋์๊ฐ๋ค(setValue์ dx์ dy๋ฅผ ์ฃผ๊ณ ์๋๋ฐ dx์ dy๋ ์ด๋ํ ๊ฑฐ๋ฆฌ์ด๊ธฐ ๋๋ฌธ์ 0์ผ๋ก ์ด๊ธฐํ ๋๊ธฐ ๋๋ฌธ)
extractOffset()์ผ๋ก ํด๊ฒฐํ๋ค.
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
position.extractOffset();
},
onPanResponderMove: (_, { dx, dy }) => {
console.log("moving");
position.setValue({
x: dx,
y: dy,
});
},
onPanResponderRelease: () => {
console.log("touch finished");
},
}),
).current;
โ Spring์ ์ ๋๋ฉ์ด์ ํจ๊ณผ๋ฅผ ๋น ๋ฅด๊ฒ ๋๋ด๋ ๋ฐฉ๋ฒ
spring animation์ ๋๋๊ธฐ๊น์ง ์ฝ๊ฐ์ ํ ์ด ์๋ค.
restSpeedThreshold
- ์๋ ์๊ณ๊ฐ
- ์ ๋๋ฉ์ด์ ์ ๋๋๊ฑธ๋ก ๊ฐ์ฃผํ๋ ์๋(์ด๋น ๋ช ํฝ์ ๋งํผ ์์ง์ด๊ณ ์๋์ ๋ํ ๊ฐ)
- ๊ธฐ๋ณธ๊ฐ์ 0.001
restDisplacementThreshold
- ๊ฑฐ๋ฆฌ ์๊ณ๊ฐ
- ์ ๋๋ฉ์ด์ ์ด ๋๋ ๊ฑธ๋ก ๊ฐ์ฃผํ๋ ๊ฑฐ๋ฆฌ
- ๊ธฐ๋ณธ๊ฐ์ 0.001
โ LayoutAnimation
https://reactnative.dev/docs/layoutanimation
LayoutAnimation · React Native
Automatically animates views to their new positions when the next layout happens.
reactnative.dev
'๐ธ React Native' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| React Native - ์ ์ฉํ Component, API (0) | 2026.01.30 |
|---|---|
| React Native - react native web swiper (0) | 2026.01.27 |
| React Native - Dark Mode (0) | 2026.01.26 |
| React Native - npx expo prebuild, ๊ธฐ๊ธฐ ๋ฌด์ ์ฐ๊ฒฐ ๋ชจ๋ํฐ๋ง (0) | 2026.01.21 |
| React Native - React Navigation (0) | 2026.01.21 |