11import React from 'react' ;
22import { type LayoutChangeEvent , View , TextInput , type TextInputProps , TouchableNativeFeedback } from 'react-native' ;
3- import { PanGestureHandler , type PanGestureHandlerGestureEvent } from 'react-native-gesture-handler' ;
3+ import { Gesture , GestureDetector } from 'react-native-gesture-handler' ;
44import Animated , {
55 type SharedValue ,
6- runOnJS ,
7- useAnimatedGestureHandler ,
86 useAnimatedProps ,
97 useAnimatedStyle ,
108 useDerivedValue ,
11- useSharedValue
9+ useSharedValue ,
10+ withTiming
1211} from 'react-native-reanimated' ;
12+ import { scheduleOnRN } from 'react-native-worklets' ;
1313
1414import styles from './styles' ;
1515import { useTheme } from '../../theme' ;
1616import { SEEK_HIT_SLOP , THUMB_SEEK_SIZE , ACTIVE_OFFSET_X , DEFAULT_TIME_LABEL } from './constants' ;
1717
18- Animated . addWhitelistedNativeProps ( { text : true } ) ;
1918const AnimatedTextInput = Animated . createAnimatedComponent ( TextInput ) ;
2019
2120interface ISeek {
@@ -50,6 +49,7 @@ const Seek = ({ currentTime, duration, loaded = false, onChangeTime }: ISeek) =>
5049 const timeLabel = useSharedValue ( DEFAULT_TIME_LABEL ) ;
5150 const scale = useSharedValue ( 1 ) ;
5251 const isPanning = useSharedValue ( false ) ;
52+ const contextX = useSharedValue ( 0 ) ;
5353
5454 const styleLine = useAnimatedStyle ( ( ) => ( {
5555 width : translateX . value
@@ -64,21 +64,30 @@ const Seek = ({ currentTime, duration, loaded = false, onChangeTime }: ISeek) =>
6464 maxWidth . value = width ;
6565 } ;
6666
67- const onGestureEvent = useAnimatedGestureHandler < PanGestureHandlerGestureEvent , { offsetX : number } > ( {
68- onStart : ( _event , ctx ) => {
67+ const panGesture = Gesture . Pan ( )
68+ . enabled ( loaded )
69+ . activeOffsetX ( [ - ACTIVE_OFFSET_X , ACTIVE_OFFSET_X ] )
70+ . onStart ( ( ) => {
6971 isPanning . value = true ;
70- ctx . offsetX = translateX . value ;
71- } ,
72- onActive : ( { translationX } , ctx ) => {
73- translateX . value = clamp ( ctx . offsetX + translationX , 0 , maxWidth . value ) ;
74- scale . value = 1.3 ;
75- } ,
76- onFinish ( ) {
77- scale . value = 1 ;
72+ contextX . value = translateX . value ;
73+ scale . value = withTiming ( 1.3 , { duration : 150 } ) ;
74+ } )
75+ . onUpdate ( event => {
76+ const newX = contextX . value + event . translationX ;
77+ translateX . value = clamp ( newX , 0 , maxWidth . value ) ;
78+ } )
79+ . onEnd ( ( ) => {
80+ scheduleOnRN ( onChangeTime , Math . round ( currentTime . value * 1000 ) ) ;
81+ } )
82+ . onFinalize ( ( _ , didSucceed ) => {
83+ if ( isPanning . value && ! didSucceed ) {
84+ translateX . value = contextX . value ;
85+ currentTime . value = ( contextX . value * duration . value ) / maxWidth . value || 0 ;
86+ }
87+
7888 isPanning . value = false ;
79- runOnJS ( onChangeTime ) ( Math . round ( currentTime . value * 1000 ) ) ;
80- }
81- } ) ;
89+ scale . value = withTiming ( 1 , { duration : 150 } ) ;
90+ } ) ;
8291
8392 useDerivedValue ( ( ) => {
8493 if ( isPanning . value ) {
@@ -118,9 +127,9 @@ const Seek = ({ currentTime, duration, loaded = false, onChangeTime }: ISeek) =>
118127 < View style = { [ styles . line , { backgroundColor : colors . strokeLight } ] } >
119128 < Animated . View style = { [ styles . line , styleLine , { backgroundColor : colors . buttonBackgroundPrimaryDefault } ] } />
120129 </ View >
121- < PanGestureHandler enabled = { loaded } onGestureEvent = { onGestureEvent } activeOffsetX = { [ - ACTIVE_OFFSET_X , ACTIVE_OFFSET_X ] } >
130+ < GestureDetector gesture = { panGesture } >
122131 < Animated . View hitSlop = { SEEK_HIT_SLOP } style = { [ styles . thumbSeek , { backgroundColor : thumbColor } , styleThumb ] } />
123- </ PanGestureHandler >
132+ </ GestureDetector >
124133 </ View >
125134 </ View >
126135 </ TouchableNativeFeedback >
0 commit comments