애니메이션
Animated
라이브러리는 애니메이션을 유연하고 강력하게 만들며, 구축과 유지보수를 간편하게 하는 데 초점을 맞춘다. Animated
는 입력과 출력 간의 선언적 관계, 그 사이에 설정 가능한 변환, 그리고 시간 기반 애니메이션 실행을 제어하는 start
/stop
메서드에 주력한다.
애니메이션을 생성하는 핵심 워크플로는 Animated.Value
를 생성하고, 이를 애니메이션 컴포넌트의 하나 이상의 스타일 속성에 연결한 다음, Animated.timing()
을 사용해 애니메이션을 통해 업데이트를 주도하는 것이다.
애니메이션 값을 직접 수정하지 않는다.
useRef
Hook을 사용해 변경 가능한 ref 객체를 반환할 수 있다. 이 ref 객체의current
프로퍼티는 주어진 인자로 초기화되며, 컴포넌트 생명주기 동안 유지된다.
예제
다음 예제는 애니메이션 값 fadeAnim
에 따라 페이드 인과 페이드 아웃 효과를 적용하는 View
를 보여준다.
Animated
를 활용한 추가 예제는 애니메이션 가이드를 참고한다.
개요
Animated
와 함께 사용할 수 있는 값 타입은 두 가지다:
- 단일 값에는
Animated.Value()
를 사용 - 벡터에는
Animated.ValueXY()
를 사용
Animated.Value
는 스타일 속성이나 다른 props에 바인딩할 수 있으며, 보간(interpolation)도 가능하다. 하나의 Animated.Value
로 여러 속성을 제어할 수 있다.
애니메이션 설정하기
Animated
는 세 가지 유형의 애니메이션을 제공한다. 각 애니메이션 유형은 초기값에서 최종값까지 어떻게 변화할지 제어하는 특정 애니메이션 곡선을 제공한다:
Animated.decay()
는 초기 속도로 시작해 점차 멈추는 애니메이션을 만든다.Animated.spring()
은 기본적인 스프링 물리 모델을 제공한다.Animated.timing()
은 이징 함수를 사용해 시간에 따라 값을 변화시킨다.
대부분의 경우 timing()
을 사용하게 된다. 기본적으로 이 함수는 대칭적인 easeInOut 곡선을 사용해, 객체가 점차 가속한 후 다시 점차 감속하며 멈추는 효과를 만든다.
애니메이션 작업하기
애니메이션은 start()
를 호출해 시작한다. start()
는 애니메이션이 완료될 때 실행될 콜백 함수를 인자로 받는다. 애니메이션이 정상적으로 완료되면 콜백 함수는 {finished: true}
와 함께 호출된다. 만약 애니메이션이 완료되기 전에 stop()
이 호출되어 중단된 경우(예: 제스처나 다른 애니메이션에 의해 중단된 경우), 콜백 함수는 {finished: false}
를 받는다.
Animated.timing({}).start(({finished}) => {
/* 완료 콜백 */
});
네이티브 드라이버를 사용하면 애니메이션을 시작하기 전에 애니메이션 관련 모든 정보를 네이티브로 전송한다. 이렇게 하면 네이티브 코드가 UI 스레드에서 애니메이션을 수행할 수 있으며, 매 프레임마다 브릿지를 거칠 필요가 없다. 애니메이션이 시작된 후에는 JS 스레드가 블로킹되더라도 애니메이션에 영향을 미치지 않는다.
애니메이션 설정에서 useNativeDriver: true
를 지정해 네이티브 드라이버를 사용할 수 있다. 자세한 내용은 애니메이션 가이드를 참고한다.
애니메이션 가능한 컴포넌트
애니메이션 가능한 컴포넌트만 애니메이션을 적용할 수 있다. 이러한 특수한 컴포넌트는 애니메이션 값을 속성에 바인딩하는 마법 같은 작업을 수행하며, 매 프레임마다 React의 렌더링과 조정 과정을 거치지 않고도 타겟팅된 네이티브 업데이트를 실행한다. 또한 언마운트 시 자동으로 정리 작업을 처리하므로 기본적으로 안전하다.
createAnimatedComponent()
를 사용하면 컴포넌트를 애니메이션 가능하게 만들 수 있다.
Animated
는 위의 래퍼를 사용해 다음과 같은 애니메이션 가능한 컴포넌트를 제공한다:
Animated.Image
Animated.ScrollView
Animated.Text
Animated.View
Animated.FlatList
Animated.SectionList
애니메이션 합성하기
애니메이션은 합성 함수를 사용해 복잡한 방식으로 조합할 수 있다:
Animated.delay()
는 주어진 지연 시간 후에 애니메이션을 시작한다.Animated.parallel()
은 여러 애니메이션을 동시에 시작한다.Animated.sequence()
는 애니메이션을 순서대로 시작하고, 각 애니메이션이 완료된 후에 다음 애니메이션을 시작한다.Animated.stagger()
는 애니메이션을 순서대로 시작하지만, 연속적인 지연 시간을 두고 병렬로 실행한다.
또한, 하나의 애니메이션의 toValue
를 다른 Animated.Value
로 설정해 애니메이션을 연결할 수도 있다. 자세한 내용은 Animations 가이드에서 동적 값 추적을 참고한다.
기본적으로 하나의 애니메이션이 중단되거나 중지되면, 그룹 내의 다른 모든 애니메이션도 중지된다.
애니메이션 값 결합하기
두 애니메이션 값을 더하거나, 빼거나, 곱하거나, 나누거나, 나머지 연산을 통해 새로운 애니메이션 값을 만들 수 있다:
보간(Interpolation)
interpolate()
함수는 입력 범위를 다양한 출력 범위로 매핑할 수 있게 해준다. 기본적으로 주어진 범위를 넘어서도 곡선을 외삽하지만, 출력 값을 고정하도록 설정할 수도 있다. 기본적으로 선형 보간을 사용하지만, easing 함수도 지원한다.
애니메이션 가이드에서 보간에 대해 더 자세히 알아볼 수 있다.
제스처 및 이벤트 처리
패닝이나 스크롤링과 같은 제스처 및 기타 이벤트는 Animated.event()
를 사용해 애니메이션 값에 직접 매핑할 수 있다. 이때 구조화된 맵 구문을 사용해 복잡한 이벤트 객체에서 값을 추출한다. 첫 번째 레벨은 여러 인자를 매핑할 수 있도록 배열로 구성되며, 이 배열은 중첩된 객체를 포함한다.
예를 들어, 가로 스크롤 제스처를 처리할 때 event.nativeEvent.contentOffset.x
를 scrollX
(Animated.Value
)에 매핑하려면 다음과 같이 작성한다:
onScroll={Animated.event(
// scrollX = e.nativeEvent.contentOffset.x
[{nativeEvent: {
contentOffset: {
x: scrollX
}
}
}]
)}
참고 자료
메서드
주어진 값이 Value 대신 ValueXY 타입일 때, 각 설정 옵션은 스칼라 값 대신 {x: ..., y: ...}
형태의 벡터로 표현될 수 있다.
decay()
static decay(value, config): CompositeAnimation;
초기 속도에서 감쇠 계수에 따라 점점 속도가 줄어들며 애니메이션을 적용한다.
config
는 다음과 같은 옵션을 가진 객체다:
velocity
: 초기 속도. 필수 값이다.deceleration
: 감쇠율. 기본값은 0.997이다.isInteraction
: 이 애니메이션이InteractionManager
에 "상호작용 핸들"을 생성하는지 여부. 기본값은 true이다.useNativeDriver
: true일 경우 네이티브 드라이버를 사용한다. 필수 값이다.
timing()
static timing(value, config): CompositeAnimation;
특정 시간 동안 이징 곡선을 따라 값을 애니메이션화한다. Easing
모듈에는 다양한 미리 정의된 곡선이 있지만, 직접 만든 함수를 사용할 수도 있다.
config
는 다음과 같은 옵션을 가질 수 있는 객체다:
duration
: 애니메이션 지속 시간(밀리초). 기본값은 500이다.easing
: 곡선을 정의하는 이징 함수. 기본값은Easing.inOut(Easing.ease)
이다.delay
: 애니메이션 시작 전 지연 시간(밀리초). 기본값은 0이다.isInteraction
: 이 애니메이션이InteractionManager
에 "interaction handle"을 생성하는지 여부. 기본값은 true이다.useNativeDriver
: true일 경우 네이티브 드라이버를 사용한다. 필수 항목이다.
spring()
static spring(value, config): CompositeAnimation;
spring()
은 감쇠 조화 진동을 기반으로 한 분석적 스프링 모델에 따라 값을 애니메이션화한다. toValue
가 업데이트될 때 유연한 움직임을 만들기 위해 속도 상태를 추적하며, 여러 애니메이션을 연결할 수 있다.
config
는 다음과 같은 옵션을 가질 수 있는 객체다.
단, bounciness/speed, tension/friction, 또는 stiffness/damping/mass 중 하나만 정의할 수 있으며, 둘 이상을 동시에 정의할 수 없다는 점에 유의해야 한다.
friction/tension 또는 bounciness/speed 옵션은 Facebook Pop
, Rebound, 그리고 Origami의 스프링 모델과 일치한다.
friction
: "튕김" 또는 오버슈트를 제어한다. 기본값은 7이다.tension
: 애니메이션 속도를 제어한다. 기본값은 40이다.speed
: 애니메이션 속도를 제어한다. 기본값은 12이다.bounciness
: 튕김 정도를 제어한다. 기본값은 8이다.
stiffness/damping/mass를 매개변수로 지정하면 Animated.spring
은 감쇠 조화 진동의 운동 방정식을 기반으로 한 분석적 스프링 모델을 사용한다. 이 동작은 스프링 역학의 물리적 원리를 더 정확하고 충실하게 따르며, iOS의 CASpringAnimation 구현과 매우 유사하다.
stiffness
: 스프링의 강성 계수다. 기본값은 100이다.damping
: 마찰력으로 인해 스프링의 움직임이 어떻게 감쇠되어야 하는지 정의한다. 기본값은 10이다.mass
: 스프링 끝에 부착된 물체의 질량이다. 기본값은 1이다.
다른 구성 옵션은 다음과 같다:
velocity
: 스프링에 부착된 물체의 초기 속도다. 기본값은 0(정지 상태)이다.overshootClamping
: 스프링이 튕기지 않고 고정되어야 하는지 여부를 나타내는 불리언 값이다. 기본값은 false다.restDisplacementThreshold
: 스프링이 정지 상태로 간주되어야 하는 변위 임계값이다. 기본값은 0.001이다.restSpeedThreshold
: 스프링이 정지 상태로 간주되어야 하는 속도(초당 픽셀 단위)다. 기본값은 0.001이다.delay
: 애니메이션 시작 전 지연 시간(밀리초)이다. 기본값은 0이다.isInteraction
: 이 애니메이션이InteractionManager
에 "interaction handle"을 생성하는지 여부다. 기본값은 true이다.useNativeDriver
: true일 경우 네이티브 드라이버를 사용한다. 필수 옵션이다.
add()
static add(a: Animated, b: Animated): AnimatedAddition;
두 Animated 값을 더한 결과로 새로운 Animated 값을 생성한다.
subtract()
static subtract(a: Animated, b: Animated): AnimatedSubtraction;
첫 번째 Animated 값에서 두 번째 Animated 값을 뺀 새로운 Animated 값을 생성한다.
divide()
static divide(a: Animated, b: Animated): AnimatedDivision;
첫 번째 Animated 값을 두 번째 Animated 값으로 나눈 결과로 새로운 Animated 값을 생성한다.
multiply()
static multiply(a: Animated, b: Animated): AnimatedMultiplication;
두 Animated 값을 곱한 결과로 새로운 Animated 값을 생성한다.
modulo()
static modulo(a: Animated, modulus: number): AnimatedModulo;
제공된 Animated 값의 (음수가 아닌) 모듈로를 계산하여 새로운 Animated 값을 생성한다.
diffClamp()
static diffClamp(a: Animated, min: number, max: number): AnimatedDiffClamp;
두 값 사이로 제한된 새로운 Animated 값을 생성한다. 이 함수는 마지막 값과의 차이를 사용하기 때문에, 값이 범위에서 멀리 떨어져 있어도 다시 가까워지기 시작하면 값이 변하기 시작한다. (value = clamp(value + diff, min, max)
).
이 기능은 스크롤 이벤트와 함께 사용할 때 유용하다. 예를 들어, 위로 스크롤할 때 네비게이션 바를 보여주고, 아래로 스크롤할 때 숨기는 데 활용할 수 있다.
delay()
static delay(time: number): CompositeAnimation;
주어진 지연 시간 이후에 애니메이션을 시작한다.
sequence()
static sequence(animations: CompositeAnimation[]): CompositeAnimation;
여러 애니메이션을 순서대로 시작한다. 각 애니메이션이 완료된 후에 다음 애니메이션을 시작한다. 현재 실행 중인 애니메이션이 중단되면, 이후 애니메이션은 시작되지 않는다.
parallel()
static parallel(
animations: CompositeAnimation[],
config?: ParallelConfig
): CompositeAnimation;
여러 애니메이션을 동시에 시작한다. 기본적으로 하나의 애니메이션이 중지되면 모든 애니메이션이 중지된다. 이 동작은 stopTogether
플래그를 통해 변경할 수 있다.
stagger()
static stagger(
time: number,
animations: CompositeAnimation[]
): CompositeAnimation;
여러 애니메이션을 배열로 전달하면, 이 애니메이션들은 병렬로 실행될 수 있지만, 순차적으로 시작되며 각각의 시작 사이에 지정된 시간만큼의 지연이 발생한다. 이 기능은 연속적인 효과를 구현할 때 유용하다.
loop()
static loop(
animation: CompositeAnimation[],
config?: LoopAnimationConfig
): CompositeAnimation;
주어진 애니메이션을 연속적으로 반복한다. 애니메이션이 끝에 도달할 때마다 초기 상태로 돌아가 다시 시작한다. 자식 애니메이션이 useNativeDriver: true
로 설정된 경우 JS 스레드를 블로킹하지 않고 루프를 실행한다. 또한, 애니메이션이 실행되는 동안 VirtualizedList
기반 컴포넌트가 추가 행을 렌더링하지 못하도록 방지할 수 있다. 이 문제를 해결하려면 자식 애니메이션 설정에서 isInteraction: false
를 전달하면 된다.
config
는 다음과 같은 옵션을 가질 수 있는 객체이다:
iterations
: 애니메이션이 반복되어야 하는 횟수. 기본값은-1
(무한 반복).
event()
static event(
argMapping: Mapping[],
config?: EventConfig
): (...args: any[]) => void;
매핑 배열을 받아 각 인자에서 값을 추출한 후, 매핑된 출력에 대해 setValue
를 호출한다. 예를 들면:
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {x: this._scrollX}}}],
{listener: (event: ScrollEvent) => console.log(event)}, // 선택적 비동기 리스너
)}
...
onPanResponderMove: Animated.event(
[
null, // 원시 이벤트 인자는 무시
{dx: this._panX},
], // 제스처 상태 인자
{
listener: (
event: GestureResponderEvent,
gestureState: PanResponderGestureState
) => console.log(event, gestureState),
} // 선택적 비동기 리스너
);
config
는 다음과 같은 옵션을 가질 수 있는 객체이다:
listener
: 선택적 비동기 리스너.useNativeDriver
: true일 경우 네이티브 드라이버를 사용한다. 필수.
forkEvent()
static forkEvent(event: AnimatedEvent, listener: Function): AnimatedEvent;
forkEvent()
는 props를 통해 전달되는 애니메이션 이벤트를 감시하기 위한 고급 명령형 API다. 기존 AnimatedEvent
에 새로운 자바스크립트 리스너를 추가할 수 있게 해준다. animatedEvent
가 자바스크립트 리스너일 경우, 두 리스너를 하나로 합친다. animatedEvent
가 null이나 undefined일 경우, 자바스크립트 리스너를 직접 할당한다. 가능한 경우 값을 직접 사용한다.
unforkEvent()
static unforkEvent(event: AnimatedEvent, listener: Function);
start()
static start(callback?: (result: {finished: boolean}) => void);
애니메이션은 start()
메서드를 호출해 시작한다. start()
는 애니메이션이 완료되거나, 완료되기 전에 stop()
이 호출되어 중단될 때 실행되는 완료 콜백 함수를 인자로 받는다.
매개변수:
이름 | 타입 | 필수 여부 | 설명 |
---|---|---|---|
callback | (result: {finished: boolean}) => void | 아니오 | 애니메이션이 정상적으로 완료되거나, 완료되기 전에 stop() 이 호출되어 중단될 때 실행되는 함수 |
콜백 함수를 사용한 start()
예제:
Animated.timing({}).start(({finished}) => {
/* 완료 콜백 */
});
stop()
static stop();
실행 중인 애니메이션을 중지한다.
reset()
static reset();
실행 중인 애니메이션을 중지하고 값을 원래 상태로 초기화한다.
속성
Value
애니메이션을 제어하기 위한 표준 값 클래스이다. 일반적으로 useAnimatedValue(0);
또는 클래스 컴포넌트에서 new Animated.Value(0);
로 초기화한다.
Animated.Value
API에 대해 더 자세히 알고 싶다면 별도의 페이지를 참고한다.
ValueXY
2D 애니메이션(예: 패닝 제스처)을 제어하기 위한 2차원 값 클래스이다.
Animated.ValueXY
API에 대한 자세한 내용은 별도의 페이지에서 확인할 수 있다.
Interpolation
Flow에서 Interpolation 타입을 사용하기 위해 내보낸다.
Node
타입 체크를 편리하게 하기 위해 내보낸 클래스이다. 모든 애니메이션 값은 이 클래스에서 파생된다.
createAnimatedComponent
React 컴포넌트를 애니메이션 가능하게 만드는 함수. Animated.View
등을 생성하는 데 사용한다.
attachNativeEvent
뷰의 이벤트에 애니메이션 값을 연결하기 위한 명령형 API다. 가능하다면 useNativeDriver: true
와 함께 Animated.event
를 사용하는 것을 권장한다.