-
Notifications
You must be signed in to change notification settings - Fork 392
修复安卓更新absolute值问题&去掉触发change的条件 #2313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
xiao19880917lu
wants to merge
8
commits into
master
Choose a base branch
from
fix-swiper-android-absolute
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
fd88166
修复安卓更新absolute值问题&去掉触发change的条件
d849504
补充下类型
4e03e07
feat: 增加安卓部分机型兼容备注
4587a68
修复lint
b90ebe5
feat: 修正方向
4efce57
修正滑动方向&滑动超过一半的逻辑
735df65
修复lint
2ec8793
修复首次触发change、支持swiper-item渲染、修复快速滑动索引变更
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,10 @@ type EventDataType = { | |
| // onUpdate时根据上一个判断方向,onFinalize根据transformStart判断 | ||
| transdir: number | ||
| } | ||
| // 只基于方向 + offset 计算最终的索引 | ||
| type EventEndType = { | ||
| transdir: number | ||
| } | ||
|
|
||
| interface SwiperProps { | ||
| children?: ReactNode | ||
|
|
@@ -188,7 +192,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| const patchElmNum = circular ? (preMargin ? 2 : 1) : 0 | ||
| const patchElmNumShared = useSharedValue(patchElmNum) | ||
| const circularShared = useSharedValue(circular) | ||
| const children = Array.isArray(props.children) ? props.children.filter(child => child) : (props.children ? [props.children] : []) | ||
| // 支持swiper-item 同时存在<swiper-item wx:for/>和<swiper-item>并列的情况 | ||
| const children = (Array.isArray(props.children) ? props.children.filter(child => child) : (props.children ? [props.children] : [])).flat() | ||
| // 对有变化的变量,在worklet中只能使用sharedValue变量,useRef不能更新 | ||
| const childrenLength = useSharedValue(children.length) | ||
| const initWidth = typeof normalStyle?.width === 'number' ? normalStyle.width - preMargin - nextMargin : normalStyle.width | ||
|
|
@@ -203,6 +208,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| // const initOffset = getOffset(props.current || 0, initStep) | ||
| // 记录元素的偏移量 | ||
| const offset = useSharedValue(getOffset(propCurrent, initStep)) | ||
| // 记录起始的offset,用于判断是否超过一半(当前offset + 起始或修正起始offset),基于索引判断是否超过一半不可行(1.滑动过程中索引会变更导致计算反向, 2.边界场景会更新offset也会导致基于索引+offset判断实效) | ||
| const preOffset = useSharedValue(0) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 为啥需要这个,超过一半与否用offset%step与step/2比较就可以了吧 |
||
| const strAbso = 'absolute' + dir.toUpperCase() as StrAbsoType | ||
| const strVelocity = 'velocity' + dir.toUpperCase() as StrVelocityType | ||
| // 标识手指触摸和抬起, 起点在onBegin | ||
|
|
@@ -211,8 +218,12 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| const preAbsolutePos = useSharedValue(0) | ||
| // 记录从onBegin 到 onTouchesUp 时移动的距离 | ||
| const moveTranstion = useSharedValue(0) | ||
| // 记录用户手滑动的方向 | ||
| const moveDir = useSharedValue(0) | ||
| const timerId = useRef(0 as number | ReturnType<typeof setTimeout>) | ||
| const intervalTimer = props.interval || 500 | ||
| // 记录是否首次,首次不能触发bindchange回调 | ||
| const isFirstRef = useRef(true) | ||
|
|
||
| const simultaneousHandlers = flatGesture(originSimultaneousHandlers) | ||
| const waitForHandlers = flatGesture(waitFor) | ||
|
|
@@ -429,11 +440,9 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| } | ||
| }, []) | ||
|
|
||
| function handleSwiperChange (current: number, pCurrent: number) { | ||
| if (pCurrent !== currentIndex.value) { | ||
| const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef }) | ||
| bindchange && bindchange(eventData) | ||
| } | ||
| function handleSwiperChange (current: number) { | ||
| const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef }) | ||
| bindchange && bindchange(eventData) | ||
| } | ||
|
|
||
| const runOnJSCallbackRef = useRef({ | ||
|
|
@@ -482,9 +491,10 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| // 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画 | ||
| useAnimatedReaction(() => currentIndex.value, (newIndex: number, preIndex: number) => { | ||
| // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息 | ||
| if (newIndex !== preIndex && bindchange) { | ||
| if (newIndex !== preIndex && bindchange && !isFirstRef.current) { | ||
| runOnJS(runOnJSCallback)('handleSwiperChange', newIndex, propCurrent) | ||
| } | ||
| isFirstRef.current = false | ||
| }) | ||
|
|
||
| useEffect(() => { | ||
|
|
@@ -542,7 +552,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| } | ||
| }, [circular, preMargin]) | ||
| const { gestureHandler } = useMemo(() => { | ||
| function getTargetPosition (eventData: EventDataType) { | ||
| // 基于transdir + 当前offset计算索引 | ||
| function getTargetPosition (eventData: EventEndType) { | ||
| 'worklet' | ||
| // 移动的距离 | ||
| const { transdir } = eventData | ||
|
|
@@ -601,7 +612,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| return true | ||
| } | ||
| } | ||
| function handleEnd (eventData: EventDataType) { | ||
| function handleEnd (eventData: EventEndType) { | ||
| 'worklet' | ||
| const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(eventData) | ||
| if (isCriticalItem) { | ||
|
|
@@ -627,7 +638,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| }) | ||
| } | ||
| } | ||
| function handleBack (eventData: EventDataType) { | ||
| function handleBack (eventData: EventEndType) { | ||
| 'worklet' | ||
| const { transdir } = eventData | ||
| // 向右滑动的back:trans < 0, 向左滑动的back: trans < 0 | ||
|
|
@@ -648,10 +659,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| } | ||
| }) | ||
| } | ||
| // 当前的offset和index多对应的offset进行对比,判断是否超过一半 | ||
| function computeHalf (eventData: EventDataType) { | ||
| function computeHalf () { | ||
| 'worklet' | ||
| const { transdir } = eventData | ||
| const currentOffset = Math.abs(offset.value) | ||
| let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value | ||
| if (circularShared.value) { | ||
|
|
@@ -660,53 +669,39 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| // 正常事件中拿到的translation值(正向滑动<0,倒着滑>0) | ||
| const diffOffset = preOffset - currentOffset | ||
| const half = Math.abs(diffOffset) > step.value / 2 | ||
| const isTriggerUpdateHalf = (transdir < 0 && currentOffset < preOffset) || (transdir > 0 && currentOffset > preOffset) | ||
| return { | ||
| diffOffset, | ||
| half, | ||
| isTriggerUpdateHalf | ||
| } | ||
| } | ||
| function handleLongPress (eventData: EventDataType) { | ||
| 'worklet' | ||
| const { diffOffset, half, isTriggerUpdateHalf } = computeHalf(eventData) | ||
| if (+diffOffset === 0) { | ||
| runOnJS(runOnJSCallback)('resumeLoop') | ||
| } else if (isTriggerUpdateHalf) { | ||
| // 如果触发了onUpdate时的索引变更 | ||
| handleEnd(eventData) | ||
| } else if (half) { | ||
| handleEnd(eventData) | ||
| } else { | ||
| handleBack(eventData) | ||
| } | ||
| return half | ||
| } | ||
| function reachBoundary (eventData: EventDataType) { | ||
| 'worklet' | ||
| // 1. 基于当前的offset和translation判断是否超过当前边界值 | ||
| const { translation } = eventData | ||
| const boundaryStart = -patchElmNumShared.value * step.value | ||
| // 与终点的逻辑对齐,都是超过补位元素对应的起点offset | ||
| const boundaryStart = 0 | ||
| const boundaryEnd = -(childrenLength.value + patchElmNumShared.value) * step.value | ||
| const moveToOffset = offset.value + translation | ||
| let isBoundary = false | ||
| let resetOffset = 0 | ||
| let resetPreOffset = 0 | ||
| if (moveToOffset < boundaryEnd) { | ||
| isBoundary = true | ||
| // 超过边界的距离 | ||
| const exceedLength = Math.abs(moveToOffset) - Math.abs(boundaryEnd) | ||
| // 计算对标正常元素所在的offset | ||
| resetOffset = patchElmNumShared.value * step.value + exceedLength | ||
| resetPreOffset = patchElmNumShared.value * step.value | ||
| } | ||
| if (moveToOffset > boundaryStart) { | ||
| isBoundary = true | ||
| // 超过边界的距离 | ||
| const exceedLength = Math.abs(boundaryStart) - Math.abs(moveToOffset) | ||
| // 计算对标正常元素所在的offset | ||
| resetOffset = (patchElmNumShared.value + childrenLength.value - 1) * step.value + (step.value - exceedLength) | ||
| resetOffset = (patchElmNumShared.value + childrenLength.value - 1) * step.value - exceedLength | ||
| resetPreOffset = (patchElmNumShared.value + childrenLength.value) * step.value | ||
| } | ||
| return { | ||
| isBoundary, | ||
| resetOffset: -resetOffset | ||
| resetOffset: -resetOffset, | ||
| resetPreOffset: -resetPreOffset | ||
| } | ||
| } | ||
| // 非循环超出边界,应用阻力; 开始滑动少阻力小,滑动越长阻力越大 | ||
|
|
@@ -748,6 +743,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| runOnJS(runOnJSCallback)('pauseLoop') | ||
| preAbsolutePos.value = e[strAbso] | ||
| moveTranstion.value = e[strAbso] | ||
| preOffset.value = offset.value | ||
| }) | ||
| .onUpdate((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => { | ||
| 'worklet' | ||
|
|
@@ -758,9 +754,10 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| transdir: moveDistance | ||
| } | ||
| // 1. 支持滑动中超出一半更新索引的能力:只更新索引并不会影响onFinalize依据当前offset计算的索引 | ||
| const { half } = computeHalf(eventData) | ||
| if (childrenLength.value > 1 && half) { | ||
| const { selectedIndex } = getTargetPosition(eventData) | ||
| // const offsetHalf = Math.abs(Math.abs(preOffset.value) - Math.abs(offset.value)) > step.value / 2 | ||
| const offsetHalf = computeHalf() | ||
| if (childrenLength.value > 1 && offsetHalf) { | ||
| const { selectedIndex } = getTargetPosition({ transdir: moveDistance } as EventEndType) | ||
| currentIndex.value = selectedIndex | ||
| } | ||
| // 2. 非循环: 处理用户一直拖拽到临界点的场景,如果放到onFinalize无法阻止offset.value更新为越界的值 | ||
|
|
@@ -771,34 +768,49 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| const finalOffset = handleResistanceMove(eventData) | ||
| offset.value = finalOffset | ||
| } | ||
| moveDir.value = e[strAbso] - preAbsolutePos.value | ||
| preAbsolutePos.value = e[strAbso] | ||
| return | ||
| } | ||
| // 3. 循环更新: 只有一个元素时可滑动,加入阻力 | ||
| if (circularShared.value && childrenLength.value === 1) { | ||
| const finalOffset = handleResistanceMove(eventData) | ||
| offset.value = finalOffset | ||
| moveDir.value = e[strAbso] - preAbsolutePos.value | ||
| preAbsolutePos.value = e[strAbso] | ||
| return | ||
| } | ||
| // 4. 循环更新:正常 | ||
| const { isBoundary, resetOffset } = reachBoundary(eventData) | ||
| const { isBoundary, resetOffset, resetPreOffset } = reachBoundary(eventData) | ||
| if (childrenLength.value > 1 && isBoundary && circularShared.value) { | ||
| offset.value = resetOffset | ||
| preOffset.value = resetPreOffset | ||
| } else { | ||
| offset.value = moveDistance + offset.value | ||
| } | ||
| moveDir.value = e[strAbso] - preAbsolutePos.value | ||
| preAbsolutePos.value = e[strAbso] | ||
| }) | ||
| .onFinalize((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => { | ||
| 'worklet' | ||
| if (touchfinish.value) return | ||
| touchfinish.value = true | ||
| // 触发过onUpdate正常情况下e[strAbso] - preAbsolutePos.value=0; 未触发过onUpdate的情况下e[strAbso] - preAbsolutePos.value 不为0 | ||
| // 正常状态下基于onUpdate时的moveDir判断方向、未触发onUpdate的则基于onBegin的moveTranstion判断方向 | ||
| const moveDistance = e[strAbso] - preAbsolutePos.value | ||
| // 默认兜底方向: 以onBegin为起点,因一些原因未触发onUpdate但是触发了位移 | ||
| const defaultDir = e[strAbso] - moveTranstion.value | ||
| // 实时方向:方向基于onUpdate时的方向,滑动的速度超过阈值时基于实时的滑动方向计算 | ||
| const realtimeData = { | ||
| transdir: moveDir.value !== 0 ? moveDir.value : defaultDir | ||
| } | ||
| // 起始方向:基于offset和(修正)preOffset判断 | ||
| const originData = { | ||
| transdir: offset.value - preOffset.value !== 0 ? offset.value - preOffset.value : defaultDir | ||
| } | ||
| const eventData = { | ||
| translation: moveDistance, | ||
| transdir: moveDistance !== 0 ? moveDistance : e[strAbso] - moveTranstion.value | ||
| transdir: realtimeData.transdir | ||
| } | ||
| // 1. 只有一个元素:循环 和 非循环状态,都走回弹效果 | ||
| if (childrenLength.value === 1) { | ||
|
|
@@ -812,19 +824,33 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr | |
| // 非循环支持最后元素可滑动能力后,向左快速移动未超过最大可移动范围一半,因为offset为正值,向左滑动handleBack,默认向上取整 | ||
| // 但是在offset大于0时,取0。[-100, 0](back取0), [0, 100](back取1), 所以handleLongPress里的处理逻辑需要兼容支持,因此这里直接单独处理,不耦合下方公共的判断逻辑。 | ||
| if (!circularShared.value && !canMove(eventData)) { | ||
| if (eventData.transdir < 0) { | ||
| handleBack(eventData) | ||
| if (realtimeData.transdir < 0) { | ||
| handleBack(realtimeData) | ||
| } else { | ||
| handleEnd(eventData) | ||
| handleEnd(realtimeData) | ||
| } | ||
| return | ||
| } | ||
| // 3. 非循环状态可移动态、循环状态, 正常逻辑处理 | ||
| const velocity = e[strVelocity] | ||
| if (Math.abs(velocity) < longPressRatio) { | ||
| handleLongPress(eventData) | ||
| // 用于判断是否超过一半(当前offset + 起始或修正起始offset),基于索引判断是否超过一半不可行(1.滑动过程中索引会变更导致计算反向, 2.边界场景会更新offset也会导致基于索引+offset判断实效) | ||
| const offsetHalf = Math.abs(Math.abs(preOffset.value) - Math.abs(offset.value)) > step.value / 2 | ||
| if (offsetHalf) { | ||
| if (Math.abs(velocity) > longPressRatio) { | ||
| // 超过速度阈值,按照实时方向(快速来回滑动) | ||
| handleEnd(realtimeData) | ||
| } else { | ||
| // 超过速度阈值,按照起始方向(慢速长按) | ||
| handleEnd(originData) | ||
| } | ||
| } else { | ||
| handleEnd(eventData) | ||
| if (Math.abs(velocity) > longPressRatio) { | ||
| // 超过速度阈值,按照实时方向(快速来回滑动) | ||
| handleEnd(realtimeData) | ||
| } else { | ||
| // 超过速度阈值,按照起始方向(慢速长按) | ||
| handleBack(originData) | ||
| } | ||
| } | ||
| }) | ||
| .withRef(swiperGestureRef) | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
直接走Children.toArray吧