Baund의 영상화면을 탭(tap) 후 나타나는 seek bar를 작년에 개발했습니다.
🔼 드래그를 통해서 사용자가 원하는 영상의 위치에 도달하게 해줌
그 이후, 논의를 거쳐 더블 탭과 스킵버튼을 이용한 스킵 기능이 추가되었습니다.
TapGestureHandler
는 태그 내부에 포함된 View의 탭을 조절합니다. (Handler 컴포넌트를 중첩시켜 singleTap
과 doubleTap
활성화할 수 있습니다.)
1️⃣ singleTap : video 메인 화면(프로필, 설명, 아이콘 등 노출) ↔ seek bar
2️⃣ doubleTap : skip forward rewind 기능 구현
// videoItem.js
const videoItem = () => {
const doubleTap = useRef(React.createRef().current);
const onSingleTap = (event) => {
if (event.nativeEvent.state === State.ACTIVE) {
console.log('single tap!');
}
};
const onDoubleTap = (event) => {
if (event.nativeEvent.state === State.ACTIVE) {
console.log('double tap!');
}
};
return (
<TapGestureHandler onHandlerStateChange={onSingleTap} waitFor={doubleTap}>
<TapGestureHandler ref={doubleTap} onHandlerStateChange={onDoubleTap} numberOfTaps={2}>
<View />
</TapGestureHandler>
</TapGestureHandler>
);
};
onHandlerStateChange
콜백 함수로 onVideoDoubleTap
함수를 호출하게 되는데, 파라미터로 사용자 tab의 위치를 알려주는 point의 x 와 y 값을 넘겨줍니다.
point.x 값을 통해 화면의 좌측인 지(rewind), 우측인 지(forward) 확인할 수 있었습니다👆
// 화면 더블 탭 - 좋아요
const onVideoDoubleTap = (point) => {
// point의 x값이 화면 왼쪽을 터치할 때
if (point.x < width / 2) {
controlSkipRewind();
}
// point의 x값이 화면 오른쪽을 터치할 때
else {
controlSkipForward();
}
};
/**
더블 탭 rewind skip 함수
*/
const controlSkipRewind = () => {
let rewindTime = currentPosition - SKIP_TIME;
// skip Animated.Value 적용 함수
skipAnimationRewind();
if (rewindTime < 0) { // 만약 -10초를 했을 때 0초 미만일 때 0초로 맞춰줌
rewindTime = 0;
}
setCurrentPosition(rewindTime); //현재 시간 업데이트
setMoveDirection(SeekBarMoveDirectionType.BACKWARD); //현재 상태 업데이트
videoProps?.videoRef?.seek(rewindTime); //Video 컴포넌트의 ref에 seek 함수에 시간 추가
};
디자인 팀에게 받은 프로토파이 작업물을 보니, 더블탭 후에 화면에 나타나야하는 요소들이 3가지가 있었습니다.
문제는 각자 다른 타이밍으로 애니메이션이 들어가야 한다는 것이었습니다. (특히 background
는 text
와 lottie
와 다르게 애니메이션이 스멀스멀(부드럽게?) 나왔다가 사라졌습니다.)
그래서 skip의 애니메이션 총 시간을 500ms로 설정한 후, background
는 나타날 때 (opacity value가 0 → 1 로 바뀔 때) 260ms, 사라질 때(opacity value가 1 → 0 로 바뀔 때)) 240ms를 적용하였습니다.
그리고 마지막으로 Animated.sequence를 사용해 각각의 애니메이션을 묶고 Animated.Parallel로 한꺼번에 실행해주었습니다.
/**
skip 적용 animation
*/
const rewindSkipOpacityAnimation = Animated.sequence([
Animated.timing(animatedRewindSkipOpacity, {
toValue: 1,
duration: 260,
useNativeDriver: true,
}),
Animated.timing(animatedRewindSkipOpacity, {
toValue: 0,
duration: 240,
useNativeDriver: true,
}),
]);
const rewindSkipProgressAnimation = Animated.sequence([
Animated.timing(animatedRewindSkipProgress, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}),
Animated.timing(animatedRewindSkipProgress, {
toValue: 0,
duration: 0,
useNativeDriver: true,
}),
]);
const skipAnimationRewind = () => {
Animated.parallel([rewindSkipOpacityAnimation, rewindSkipProgressAnimation]).start();
};