@@ -42,6 +42,11 @@ type EventDataType = {
4242type EventEndType = {
4343 transdir : number
4444}
45+ // bindtransition回调的参数
46+ type EventTransition = {
47+ dx : number
48+ dy : number
49+ }
4550
4651interface SwiperProps {
4752 children ?: ReactNode
@@ -78,6 +83,8 @@ interface SwiperProps {
7883 'simultaneous-handlers' ?: Array < GestureHandler >
7984 disableGesture ?: boolean
8085 bindchange ?: ( event : NativeSyntheticEvent < TouchEvent > | unknown ) => void
86+ bindtransition ?: ( event : NativeSyntheticEvent < TouchEvent > | unknown ) => void
87+ bindanimationfinish ?: ( event : NativeSyntheticEvent < TouchEvent > | unknown ) => void
8188}
8289
8390/**
@@ -158,7 +165,9 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
158165 circular = false ,
159166 disableGesture = false ,
160167 current : propCurrent = 0 ,
161- bindchange
168+ bindchange,
169+ bindtransition,
170+ bindanimationfinish
162171 } = props
163172
164173 const dotCommonStyle = {
@@ -238,6 +247,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
238247 const intervalTimer = props . interval || 500
239248 // 记录是否首次,首次不能触发bindchange回调
240249 const isFirstRef = useRef ( true )
250+ // 记录每次过渡动画起点item的offset坐标,用于bindtransition计算
251+ const transitionSourceOffset = useSharedValue ( 0 )
241252
242253 const simultaneousHandlers = flatGesture ( originSimultaneousHandlers )
243254 const waitForHandlers = flatGesture ( waitFor )
@@ -395,6 +406,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
395406 const { loop, pauseLoop, resumeLoop } = useMemo ( ( ) => {
396407 function createAutoPlay ( ) {
397408 if ( ! step . value ) return
409+ // 记录起点:circular 减去 preMargin
410+ transitionSourceOffset . value = circularShared . value ? - ( currentIndex . value + patchElmNumShared . value ) * step . value + preMarginShared . value : - currentIndex . value * step . value
398411 let targetOffset = 0
399412 let nextIndex = currentIndex . value
400413 if ( ! circularShared . value ) {
@@ -412,6 +425,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
412425 } , ( ) => {
413426 currentIndex . value = nextIndex
414427 runOnJS ( runOnJSCallback ) ( 'loop' )
428+ bindanimationfinish && runOnJS ( runOnJSCallback ) ( 'handleAnimationfinish' , nextIndex )
415429 } )
416430 } else {
417431 // 默认向右, 向下
@@ -427,6 +441,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
427441 offset . value = initOffset
428442 currentIndex . value = nextIndex
429443 runOnJS ( runOnJSCallback ) ( 'loop' )
444+ bindanimationfinish && runOnJS ( runOnJSCallback ) ( 'handleAnimationfinish' , nextIndex )
430445 } )
431446 } else {
432447 nextIndex = currentIndex . value + 1
@@ -438,6 +453,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
438453 } , ( ) => {
439454 currentIndex . value = nextIndex
440455 runOnJS ( runOnJSCallback ) ( 'loop' )
456+ bindanimationfinish && runOnJS ( runOnJSCallback ) ( 'handleAnimationfinish' , nextIndex )
441457 } )
442458 }
443459 }
@@ -470,11 +486,23 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
470486 bindchange && bindchange ( eventData )
471487 }
472488
489+ function handleTransition ( transData : EventTransition ) {
490+ const eventData = getCustomEvent ( 'change' , { } , { detail : transData , layoutRef : layoutRef } )
491+ bindtransition && bindtransition ( eventData )
492+ }
493+
494+ function handleAnimationfinish ( current : number ) {
495+ const eventData = getCustomEvent ( 'change' , { } , { detail : { current, source : 'touch' } , layoutRef : layoutRef } )
496+ bindanimationfinish && bindanimationfinish ( eventData )
497+ }
498+
473499 const runOnJSCallbackRef = useRef ( {
474500 loop,
475501 pauseLoop,
476502 resumeLoop,
477- handleSwiperChange
503+ handleSwiperChange,
504+ handleTransition,
505+ handleAnimationfinish
478506 } )
479507 const runOnJSCallback = useRunOnJSCallback ( runOnJSCallbackRef )
480508
@@ -495,11 +523,14 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
495523 if ( targetOffset !== offset . value ) {
496524 // 内部基于props.current!==currentIndex.value决定是否使用动画及更新currentIndex.value
497525 if ( propCurrent !== undefined && propCurrent !== currentIndex . value ) {
526+ // 记录起点(变更前的当前 item 坐标)
527+ transitionSourceOffset . value = circularShared . value ? - ( currentIndex . value + patchElmNumShared . value ) * stepValue + preMarginShared . value : - currentIndex . value * stepValue
498528 offset . value = withTiming ( targetOffset , {
499529 duration : easeDuration ,
500530 easing : easeMap [ easeingFunc ]
501531 } , ( ) => {
502532 currentIndex . value = propCurrent
533+ bindanimationfinish && runOnJS ( runOnJSCallback ) ( 'handleAnimationfinish' , propCurrent )
503534 } )
504535 } else {
505536 offset . value = targetOffset
@@ -513,6 +544,49 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
513544 pauseLoop ( )
514545 }
515546 }
547+ // AutoPlay的优化,基于offset变化,提前更新currentIndex
548+ useAnimatedReaction (
549+ ( ) => offset . value ,
550+ ( newOffset ) => {
551+ // 只在自动播放动画过程中(非手势)处理
552+ if ( ! touchfinish . value ) return
553+ if ( ! step . value || childrenLength . value <= 1 ) return
554+
555+ // 与 computeHalf 逻辑对齐:计算当前 offset 距离当前 index "home位置" 是否超过一半
556+ let homeOffset = ( currentIndex . value + patchElmNumShared . value ) * step . value
557+ if ( circularShared . value ) {
558+ homeOffset -= preMarginShared . value
559+ }
560+ const diff = homeOffset - Math . abs ( newOffset )
561+ if ( Math . abs ( diff ) > step . value / 2 ) {
562+ // 超过一半,提前更新 currentIndex
563+ // diff > 0 正向运动,diff < 0 反向运动
564+ const transdir = diff < 0 ? - 1 : 1
565+ // 内联 getTargetPosition 的简化版
566+ const rawIndex = Math . abs ( newOffset ) / step . value
567+ const moveToIndex = transdir < 0 ? Math . ceil ( rawIndex ) : Math . floor ( rawIndex )
568+ let nextIndex : number
569+ if ( circularShared . value ) {
570+ // 与 getTargetPosition 的边界分支对齐
571+ if ( moveToIndex >= childrenLength . value + patchElmNumShared . value ) {
572+ // 超过末尾 clone → 绕回头部
573+ nextIndex = moveToIndex - ( childrenLength . value + patchElmNumShared . value )
574+ } else if ( moveToIndex <= patchElmNumShared . value - 1 ) {
575+ // 超过头部 clone → 绕回尾部
576+ nextIndex = moveToIndex === 0 ? childrenLength . value - patchElmNumShared . value : childrenLength . value - 1
577+ } else {
578+ nextIndex = moveToIndex - patchElmNumShared . value
579+ }
580+ } else {
581+ // 非循环:正常 clamp
582+ nextIndex = Math . max ( 0 , Math . min ( moveToIndex , childrenLength . value - 1 ) )
583+ }
584+ if ( nextIndex !== currentIndex . value ) {
585+ currentIndex . value = nextIndex
586+ }
587+ }
588+ }
589+ )
516590 // 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画
517591 useAnimatedReaction ( ( ) => currentIndex . value , ( newIndex : number , preIndex : number ) => {
518592 // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
@@ -522,6 +596,25 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
522596 isFirstRef . current = false
523597 } )
524598
599+ useAnimatedReaction ( ( ) => offset . value , ( curOffset , preOffset ) => {
600+ if ( ! bindtransition ) return
601+ // 修正 isEqual:circular 需减去 preMargin
602+ const computeOffset = step . value * ( currentIndex . value + patchElmNumShared . value )
603+ - ( circularShared . value ? preMarginShared . value : 0 )
604+ const isEqual = Math . abs ( Math . floor ( computeOffset ) - Math . floor ( Math . abs ( curOffset ) ) ) <= 2
605+ if ( curOffset !== ( preOffset ?? 0 ) && ! isEqual ) {
606+ // trans = 起点坐标 - 当前坐标
607+ // x方向向前(向左)滑动:offset 变负 → trans > 0
608+ const trans = transitionSourceOffset . value - curOffset
609+ const transData = {
610+ dx : dir === 'x' ? trans : 0 ,
611+ dy : dir === 'y' ? trans : 0
612+ }
613+ console . log ( '-------------------------useAnimatedReaction3' , transitionSourceOffset . value , curOffset , trans )
614+ runOnJS ( runOnJSCallback ) ( 'handleTransition' , transData )
615+ }
616+ } )
617+
525618 useEffect ( ( ) => {
526619 let patchStep = 0
527620 if ( preMargin !== preMarginShared . value ) {
@@ -649,6 +742,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
649742 currentIndex . value = selectedIndex
650743 offset . value = resetOffset
651744 runOnJS ( runOnJSCallback ) ( 'resumeLoop' )
745+ bindanimationfinish && runOnJS ( runOnJSCallback ) ( 'handleAnimationfinish' , selectedIndex )
652746 }
653747 } )
654748 } else {
@@ -659,6 +753,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
659753 if ( touchfinish . value !== false ) {
660754 currentIndex . value = selectedIndex
661755 runOnJS ( runOnJSCallback ) ( 'resumeLoop' )
756+ bindanimationfinish && runOnJS ( runOnJSCallback ) ( 'handleAnimationfinish' , selectedIndex )
662757 }
663758 } )
664759 }
@@ -681,6 +776,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
681776 if ( touchfinish . value !== false ) {
682777 currentIndex . value = moveToIndex
683778 runOnJS ( runOnJSCallback ) ( 'resumeLoop' )
779+ bindanimationfinish && runOnJS ( runOnJSCallback ) ( 'handleAnimationfinish' , moveToIndex )
684780 }
685781 } )
686782 }
@@ -769,9 +865,11 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
769865 if ( ! step . value ) return
770866 touchfinish . value = false
771867 cancelAnimation ( offset )
868+ transitionSourceOffset . value = offset . value
772869 runOnJS ( runOnJSCallback ) ( 'pauseLoop' )
773870 preAbsolutePos . value = e [ strAbso ]
774871 moveTranstion . value = e [ strAbso ]
872+ console . log ( '-------------------------onBegin3' , transitionSourceOffset . value )
775873 } )
776874 . onUpdate ( ( e : GestureStateChangeEvent < PanGestureHandlerEventPayload > ) => {
777875 'worklet'
0 commit comments