UNPKG

@mpxjs/webpack-plugin

Version:

mpx compile core

798 lines (797 loc) 35.1 kB
import { View } from 'react-native'; import { GestureDetector, Gesture } from 'react-native-gesture-handler'; import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation } from 'react-native-reanimated'; import React, { forwardRef, useRef, useEffect, useMemo, createElement } from 'react'; import useInnerProps, { getCustomEvent } from './getInnerListeners'; import useNodesRef from './useNodesRef'; // 引入辅助函数 import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren, extendObject, flatGesture } from './utils'; import { SwiperContext } from './context'; import Portal from './mpx-portal'; /** * 默认的Style类型 */ const styles = { pagination_x: { position: 'absolute', bottom: 25, left: 0, right: 0, flexDirection: 'row', flex: 1, justifyContent: 'center', alignItems: 'center' }, pagination_y: { position: 'absolute', right: 15, top: 0, bottom: 0, flexDirection: 'column', flex: 1, justifyContent: 'center', alignItems: 'center' }, pagerWrapperx: { position: 'absolute', flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }, pagerWrappery: { position: 'absolute', flexDirection: 'column', justifyContent: 'center', alignItems: 'center' }, swiper: { overflow: 'scroll', display: 'flex', justifyContent: 'flex-start' } }; const dotCommonStyle = { width: 8, height: 8, borderRadius: 4, marginLeft: 3, marginRight: 3, marginTop: 3, marginBottom: 3, zIndex: 98 }; const activeDotStyle = { zIndex: 99 }; const longPressRatio = 100; const easeMap = { default: Easing.inOut(Easing.cubic), linear: Easing.linear, easeInCubic: Easing.in(Easing.cubic), easeOutCubic: Easing.out(Easing.cubic), easeInOutCubic: Easing.inOut(Easing.cubic) }; const SwiperWrapper = forwardRef((props, ref) => { const { 'indicator-dots': showPagination, 'indicator-color': dotColor = 'rgba(0, 0, 0, .3)', 'indicator-active-color': activeDotColor = '#000000', 'enable-var': enableVar = false, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'external-var-context': externalVarContext, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, autoplay = false, circular = false, disableGesture = false, bindchange } = props; const easeingFunc = props['easing-function'] || 'default'; const easeDuration = props.duration || 500; const horizontal = props.vertical !== undefined ? !props.vertical : true; const nodeRef = useRef(null); // 手势协同gesture 1.0 const swiperGestureRef = useRef(); useNodesRef(props, ref, nodeRef, { // scrollView内部会过滤是否绑定了gestureRef,withRef(swiperGestureRef)给gesture对象设置一个ref(2.0版本) gestureRef: swiperGestureRef }); // 计算transfrom之类的 const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, hasPositionFixed, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight }); const { textStyle } = splitStyle(normalStyle); const { textProps } = splitProps(props); const preMargin = props['previous-margin'] ? global.__formatValue(props['previous-margin']) : 0; const nextMargin = props['next-margin'] ? global.__formatValue(props['next-margin']) : 0; const preMarginShared = useSharedValue(preMargin); const nextMarginShared = useSharedValue(nextMargin); const autoplayShared = useSharedValue(autoplay); // 默认前后补位的元素个数 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] : []); // 对有变化的变量,在worklet中只能使用sharedValue变量,useRef不能更新 const childrenLength = useSharedValue(children.length); const initWidth = typeof normalStyle?.width === 'number' ? normalStyle.width - preMargin - nextMargin : normalStyle.width; const initHeight = typeof normalStyle?.height === 'number' ? normalStyle.height - preMargin - nextMargin : normalStyle.height; const dir = horizontal === false ? 'y' : 'x'; const pstep = dir === 'x' ? initWidth : initHeight; const initStep = isNaN(pstep) ? 0 : pstep; // 每个元素的宽度 or 高度,有固定值直接初始化无则0 const step = useSharedValue(initStep); // 记录选中元素的索引值 const currentIndex = useSharedValue(props.current || 0); // const initOffset = getOffset(props.current || 0, initStep) // 记录元素的偏移量 const offset = useSharedValue(getOffset(props.current || 0, initStep)); const strAbso = 'absolute' + dir.toUpperCase(); const strVelocity = 'velocity' + dir.toUpperCase(); // 标识手指触摸和抬起, 起点在onBegin const touchfinish = useSharedValue(true); // 记录上一帧的绝对定位坐标 const preAbsolutePos = useSharedValue(0); // 记录从onBegin 到 onTouchesUp 时移动的距离 const moveTranstion = useSharedValue(0); const timerId = useRef(0); const intervalTimer = props.interval || 500; const simultaneousHandlers = flatGesture(originSimultaneousHandlers); const waitForHandlers = flatGesture(waitFor); // 判断gesture手势是否需要协同处理、等待手势失败响应 const gestureSwitch = useRef(false); // 初始化上一次的手势 const prevSimultaneousHandlersRef = useRef(originSimultaneousHandlers || []); const prevWaitForHandlersRef = useRef(waitFor || []); const hasSimultaneousHandlersChanged = prevSimultaneousHandlersRef.current.length !== (originSimultaneousHandlers?.length || 0) || (originSimultaneousHandlers || []).some((handler, index) => handler !== prevSimultaneousHandlersRef.current[index]); const hasWaitForHandlersChanged = prevWaitForHandlersRef.current.length !== (waitFor?.length || 0) || (waitFor || []).some((handler, index) => handler !== prevWaitForHandlersRef.current[index]); if (hasSimultaneousHandlersChanged || hasWaitForHandlersChanged) { gestureSwitch.current = !gestureSwitch.current; } // 存储上一次的手势 prevSimultaneousHandlersRef.current = originSimultaneousHandlers || []; prevWaitForHandlersRef.current = waitFor || []; const { // 存储layout布局信息 layoutRef, layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout }); const innerProps = useInnerProps(extendObject({}, props, { ref: nodeRef }), [ 'style', 'indicator-dots', 'indicator-color', 'indicator-active-color', 'previous-margin', 'vertical', 'previous-margin', 'next-margin', 'easing-function', 'autoplay', 'circular', 'interval', 'easing-function' ], { layoutRef: layoutRef }); function onWrapperLayout(e) { const { width, height } = e.nativeEvent.layout; const realWidth = dir === 'x' ? width - preMargin - nextMargin : width; const realHeight = dir === 'y' ? height - preMargin - nextMargin : height; const iStep = dir === 'x' ? realWidth : realHeight; if (iStep !== step.value) { step.value = iStep; updateCurrent(props.current || 0, iStep); updateAutoplay(); } } const dotAnimatedStyle = useAnimatedStyle(() => { if (!step.value) return {}; const dotStep = dotCommonStyle.width + dotCommonStyle.marginRight + dotCommonStyle.marginLeft; if (dir === 'x') { return { transform: [{ translateX: currentIndex.value * dotStep }] }; } else { return { transform: [{ translateY: currentIndex.value * dotStep }] }; } }); function renderPagination() { const activeColor = activeDotColor || '#007aff'; const unActionColor = dotColor || 'rgba(0,0,0,.2)'; // 正常渲染所有dots const dots = []; for (let i = 0; i < children.length; i++) { dots.push(<View style={[dotCommonStyle, { backgroundColor: unActionColor }]} key={i}></View>); } return (<View pointerEvents="none" style={styles['pagination_' + dir]}> <View style={[styles['pagerWrapper' + dir]]}> <Animated.View style={[ dotCommonStyle, activeDotStyle, { backgroundColor: activeColor, position: 'absolute', left: 0, top: 0 }, dotAnimatedStyle ]}/> {dots} </View> </View>); } function renderItems() { const intLen = children.length; let renderChild = children.slice(); if (circular && intLen > 1) { // 最前面加最后一个元素 const lastChild = React.cloneElement(children[intLen - 1], { key: 'clone0' }); // 最后面加第一个元素 const firstChild = React.cloneElement(children[0], { key: 'clone1' }); if (preMargin) { const lastChild1 = React.cloneElement(children[intLen - 2], { key: 'clone2' }); const firstChild1 = React.cloneElement(children[1], { key: 'clone3' }); renderChild = [lastChild1, lastChild].concat(renderChild).concat([firstChild, firstChild1]); } else { renderChild = [lastChild].concat(renderChild).concat([firstChild]); } } const arrChildren = renderChild.map((child, index) => { const extraStyle = {}; if (index === 0 && !circular) { preMargin && dir === 'x' && (extraStyle.marginLeft = preMargin); preMargin && dir === 'y' && (extraStyle.marginTop = preMargin); } if (index === intLen - 1 && !circular) { nextMargin && dir === 'x' && (extraStyle.marginRight = nextMargin); nextMargin && dir === 'y' && (extraStyle.marginBottom = nextMargin); } // 业务swiper-item自己生成key,内部添加的元素自定义key const newChild = React.cloneElement(child, { itemIndex: index, customStyle: extraStyle }); return newChild; }); const contextValue = { offset, step, scale: props.scale, dir }; return (<SwiperContext.Provider value={contextValue}>{arrChildren}</SwiperContext.Provider>); } const { loop, pauseLoop, resumeLoop } = useMemo(() => { function createAutoPlay() { if (!step.value) return; let targetOffset = 0; let nextIndex = currentIndex.value; if (!circularShared.value) { // 获取下一个位置的坐标, 循环到最后一个元素,直接停止, 取消定时器 if (currentIndex.value === childrenLength.value - 1) { pauseLoop(); return; } nextIndex += 1; // targetOffset = -nextIndex * step.value - preMarginShared.value targetOffset = -nextIndex * step.value; offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] }, () => { currentIndex.value = nextIndex; runOnJS(loop)(); }); } else { // 默认向右, 向下 if (nextIndex === childrenLength.value - 1) { nextIndex = 0; targetOffset = -(childrenLength.value + patchElmNumShared.value) * step.value + preMarginShared.value; // 执行动画到下一帧 offset.value = withTiming(targetOffset, { duration: easeDuration }, () => { const initOffset = -step.value * patchElmNumShared.value + preMarginShared.value; // 将开始位置设置为真正的位置 offset.value = initOffset; currentIndex.value = nextIndex; runOnJS(loop)(); }); } else { nextIndex = currentIndex.value + 1; targetOffset = -(nextIndex + patchElmNumShared.value) * step.value + preMarginShared.value; // 执行动画到下一帧 offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] }, () => { currentIndex.value = nextIndex; runOnJS(loop)(); }); } } } // loop在JS线程中调用,createAutoPlay + useEffect中 function loop() { timerId.current && clearTimeout(timerId.current); timerId.current = setTimeout(createAutoPlay, intervalTimer); } function pauseLoop() { timerId.current && clearTimeout(timerId.current); } // resumeLoop在worklet中调用 function resumeLoop() { if (autoplayShared.value && childrenLength.value > 1) { loop(); } } return { loop, pauseLoop, resumeLoop }; }, []); function handleSwiperChange(current) { if (props.current !== currentIndex.value) { const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef }); bindchange && bindchange(eventData); } } function getOffset(index, stepValue) { if (!stepValue) return 0; let targetOffset = 0; if (circular && children.length > 1) { const targetIndex = index + patchElmNum; targetOffset = -(stepValue * targetIndex - preMargin); } else { targetOffset = -index * stepValue; } return targetOffset; } function updateCurrent(index, stepValue) { const targetOffset = getOffset(index || 0, stepValue); if (targetOffset !== offset.value) { // 内部基于props.current!==currentIndex.value决定是否使用动画及更新currentIndex.value if (props.current !== undefined && props.current !== currentIndex.value) { offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] }, () => { currentIndex.value = props.current || 0; }); } else { offset.value = targetOffset; } } } function updateAutoplay() { if (autoplay && children.length > 1) { loop(); } else { pauseLoop(); } } // 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画 useAnimatedReaction(() => currentIndex.value, (newIndex, preIndex) => { // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息 if (newIndex !== preIndex && bindchange) { runOnJS(handleSwiperChange)(newIndex); } }); useEffect(() => { let patchStep = 0; if (preMargin !== preMarginShared.value) { patchStep += preMargin - preMarginShared.value; } if (nextMargin !== nextMarginShared.value) { patchStep += nextMargin - nextMarginShared.value; } preMarginShared.value = preMargin; nextMarginShared.value = nextMargin; const newStep = step.value - patchStep; if (step.value !== newStep) { step.value = newStep; offset.value = getOffset(currentIndex.value, newStep); } }, [preMargin, nextMargin]); useEffect(() => { childrenLength.value = children.length; if (children.length - 1 < currentIndex.value) { pauseLoop(); currentIndex.value = 0; offset.value = getOffset(0, step.value); if (autoplay && children.length > 1) { loop(); } } }, [children.length]); useEffect(() => { // 1. 如果用户在touch的过程中, 外部更新了current以外部为准(小程序表现) // 2. 手指滑动过程中更新索引,外部会把current再传入进来,导致offset直接更新,增加判断不同才更新 if (props.current !== currentIndex.value) { updateCurrent(props.current || 0, step.value); } }, [props.current]); useEffect(() => { autoplayShared.value = autoplay; updateAutoplay(); return () => { if (autoplay) { pauseLoop(); } }; }, [autoplay]); useEffect(() => { if (circular !== circularShared.value) { circularShared.value = circular; patchElmNumShared.value = circular ? (preMargin ? 2 : 1) : 0; offset.value = getOffset(currentIndex.value, step.value); } }, [circular, preMargin]); const { gestureHandler } = useMemo(() => { function getTargetPosition(eventData) { 'worklet'; // 移动的距离 const { transdir } = eventData; let resetOffsetPos = 0; let selectedIndex = currentIndex.value; // 是否临界点 let isCriticalItem = false; // 真实滚动到的偏移量坐标 let moveToTargetPos = 0; const tmp = !circularShared.value ? 0 : preMarginShared.value; const currentOffset = transdir < 0 ? offset.value - tmp : offset.value + tmp; const computedIndex = Math.abs(currentOffset) / step.value; const moveToIndex = transdir < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex); // 实际应该定位的索引值 if (!circularShared.value) { selectedIndex = moveToIndex; moveToTargetPos = selectedIndex * step.value; } else { if (moveToIndex >= childrenLength.value + patchElmNumShared.value) { selectedIndex = moveToIndex - (childrenLength.value + patchElmNumShared.value); resetOffsetPos = (selectedIndex + patchElmNumShared.value) * step.value - preMarginShared.value; moveToTargetPos = moveToIndex * step.value - preMarginShared.value; isCriticalItem = true; } else if (moveToIndex <= patchElmNumShared.value - 1) { selectedIndex = moveToIndex === 0 ? childrenLength.value - patchElmNumShared.value : childrenLength.value - 1; resetOffsetPos = (selectedIndex + patchElmNumShared.value) * step.value - preMarginShared.value; moveToTargetPos = moveToIndex * step.value - preMarginShared.value; isCriticalItem = true; } else { selectedIndex = moveToIndex - patchElmNumShared.value; moveToTargetPos = moveToIndex * step.value - preMarginShared.value; } } return { selectedIndex, isCriticalItem, resetOffset: -resetOffsetPos, targetOffset: -moveToTargetPos }; } function canMove(eventData) { 'worklet'; // 旧版:如果在快速多次滑动时,只根据当前的offset判断,会出现offset没超出,加上translation后越界的场景(如在倒数第二个元素快速滑动) // 新版:会加上translation const { translation, transdir } = eventData; const gestureMovePos = offset.value + translation; if (!circularShared.value) { // 如果只判断区间,中间非滑动状态(handleResistanceMove)向左滑动,突然改为向右滑动,但是还在非滑动态,本应该可滑动判断为了不可滑动 const posEnd = -step.value * (childrenLength.value - 1); if (transdir < 0) { return gestureMovePos > posEnd; } else { return gestureMovePos < 0; } } else { return true; } } function handleEnd(eventData) { 'worklet'; const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(eventData); if (isCriticalItem) { offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] }, () => { if (touchfinish.value !== false) { currentIndex.value = selectedIndex; offset.value = resetOffset; runOnJS(resumeLoop)(); } }); } else { offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] }, () => { if (touchfinish.value !== false) { currentIndex.value = selectedIndex; runOnJS(resumeLoop)(); } }); } } function handleBack(eventData) { 'worklet'; const { transdir } = eventData; // 向右滑动的back:trans < 0, 向左滑动的back: trans < 0 let currentOffset = Math.abs(offset.value); if (circularShared.value) { currentOffset += transdir < 0 ? preMarginShared.value : -preMarginShared.value; } const curIndex = currentOffset / step.value; const moveToIndex = (transdir < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElmNumShared.value; const targetOffset = -(moveToIndex + patchElmNumShared.value) * step.value + (circularShared.value ? preMarginShared.value : 0); offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] }, () => { if (touchfinish.value !== false) { currentIndex.value = moveToIndex; runOnJS(resumeLoop)(); } }); } // 当前的offset和index多对应的offset进行对比,判断是否超过一半 function computeHalf(eventData) { 'worklet'; const { transdir } = eventData; const currentOffset = Math.abs(offset.value); let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value; if (circularShared.value) { preOffset -= preMarginShared.value; } // 正常事件中拿到的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) { 'worklet'; const { diffOffset, half, isTriggerUpdateHalf } = computeHalf(eventData); if (+diffOffset === 0) { runOnJS(resumeLoop)(); } else if (isTriggerUpdateHalf) { // 如果触发了onUpdate时的索引变更,则直接以update时的index为准 const targetIndex = !circularShared.value ? currentIndex.value : currentIndex.value + patchElmNumShared.value - 1; offset.value = withTiming(-targetIndex * step.value, { duration: easeDuration, easing: easeMap[easeingFunc] }, () => { if (touchfinish.value !== false) { currentIndex.value = targetIndex; runOnJS(resumeLoop)(); } }); } else if (half) { handleEnd(eventData); } else { handleBack(eventData); } } function reachBoundary(eventData) { 'worklet'; // 1. 基于当前的offset和translation判断是否超过当前边界值 const { translation } = eventData; const boundaryStart = -patchElmNumShared.value * step.value; const boundaryEnd = -(childrenLength.value + patchElmNumShared.value) * step.value; const moveToOffset = offset.value + translation; let isBoundary = false; let resetOffset = 0; if (moveToOffset < boundaryEnd) { isBoundary = true; // 超过边界的距离 const exceedLength = Math.abs(moveToOffset) - Math.abs(boundaryEnd); // 计算对标正常元素所在的offset resetOffset = patchElmNumShared.value * step.value + exceedLength; } 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); } return { isBoundary, resetOffset: -resetOffset }; } // 非循环超出边界,应用阻力; 开始滑动少阻力小,滑动越长阻力越大 function handleResistanceMove(eventData) { 'worklet'; const { translation, transdir } = eventData; const moveToOffset = offset.value + translation; const maxOverDrag = Math.floor(step.value / 2); const maxOffset = translation < 0 ? -(childrenLength.value - 1) * step.value : 0; let resistance = 0.1; let overDrag = 0; let finalOffset = 0; // 向右向下小于0, 向左向上大于0; if (transdir < 0) { overDrag = Math.abs(moveToOffset - maxOffset); } else { overDrag = Math.abs(moveToOffset); } // 滑动越多resistance越小 resistance = 1 - overDrag / maxOverDrag; // 确保阻力在合理范围内 resistance = Math.min(0.5, resistance); // 限制在最大拖拽范围内 if (transdir < 0) { const adjustOffset = offset.value + translation * resistance; finalOffset = Math.max(adjustOffset, maxOffset - maxOverDrag); } else { const adjustOffset = offset.value + translation * resistance; finalOffset = Math.min(adjustOffset, maxOverDrag); } return finalOffset; } const gesturePan = Gesture.Pan() .onBegin((e) => { 'worklet'; if (!step.value) return; touchfinish.value = false; cancelAnimation(offset); runOnJS(pauseLoop)(); preAbsolutePos.value = e[strAbso]; moveTranstion.value = e[strAbso]; }) .onUpdate((e) => { 'worklet'; if (touchfinish.value) return; const moveDistance = e[strAbso] - preAbsolutePos.value; const eventData = { translation: moveDistance, transdir: moveDistance }; // 1. 支持滑动中超出一半更新索引的能力:只更新索引并不会影响onFinalize依据当前offset计算的索引 const { half } = computeHalf(eventData); if (childrenLength.value > 1 && half) { const { selectedIndex } = getTargetPosition(eventData); currentIndex.value = selectedIndex; } // 2. 非循环: 处理用户一直拖拽到临界点的场景,如果放到onFinalize无法阻止offset.value更新为越界的值 if (!circularShared.value) { if (canMove(eventData)) { offset.value = moveDistance + offset.value; } else { const finalOffset = handleResistanceMove(eventData); offset.value = finalOffset; } preAbsolutePos.value = e[strAbso]; return; } // 3. 循环更新: 只有一个元素时可滑动,加入阻力 if (circularShared.value && childrenLength.value === 1) { const finalOffset = handleResistanceMove(eventData); offset.value = finalOffset; preAbsolutePos.value = e[strAbso]; return; } // 4. 循环更新:正常 const { isBoundary, resetOffset } = reachBoundary(eventData); if (childrenLength.value > 1 && isBoundary && circularShared.value) { offset.value = resetOffset; } else { offset.value = moveDistance + offset.value; } preAbsolutePos.value = e[strAbso]; }) .onFinalize((e) => { 'worklet'; if (touchfinish.value) return; touchfinish.value = true; // 触发过onUpdate正常情况下e[strAbso] - preAbsolutePos.value=0; 未触发过onUpdate的情况下e[strAbso] - preAbsolutePos.value 不为0 const moveDistance = e[strAbso] - preAbsolutePos.value; const eventData = { translation: moveDistance, transdir: moveDistance !== 0 ? moveDistance : e[strAbso] - moveTranstion.value }; // 1. 只有一个元素:循环 和 非循环状态,都走回弹效果 if (childrenLength.value === 1) { offset.value = withTiming(0, { duration: easeDuration, easing: easeMap[easeingFunc] }); return; } // 2.非循环状态不可移动态:最后一个元素 和 第一个元素 // 非循环支持最后元素可滑动能力后,向左快速移动未超过最大可移动范围一半,因为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); } else { handleEnd(eventData); } return; } // 3. 非循环状态可移动态、循环状态, 正常逻辑处理 const velocity = e[strVelocity]; if (Math.abs(velocity) < longPressRatio) { handleLongPress(eventData); } else { handleEnd(eventData); } }) .withRef(swiperGestureRef); // swiper横向,当y轴滑动5像素手势失效;swiper纵向只响应swiper的滑动事件 if (dir === 'x') { gesturePan.activeOffsetX([-2, 2]).failOffsetY([-5, 5]); } else { gesturePan.activeOffsetY([-2, 2]).failOffsetX([-5, 5]); } // 手势协同2.0 if (simultaneousHandlers && simultaneousHandlers.length) { gesturePan.simultaneousWithExternalGesture(...simultaneousHandlers); } if (waitForHandlers && waitForHandlers.length) { gesturePan.requireExternalGestureToFail(...waitForHandlers); } return { gestureHandler: gesturePan }; }, [gestureSwitch.current]); const animatedStyles = useAnimatedStyle(() => { if (dir === 'x') { return { transform: [{ translateX: offset.value }], opacity: step.value > 0 ? 1 : 0 }; } else { return { transform: [{ translateY: offset.value }], opacity: step.value > 0 ? 1 : 0 }; } }); let finalComponent; const arrPages = renderItems(); const mergeProps = Object.assign({ style: [normalStyle, layoutStyle, styles.swiper] }, layoutProps, innerProps); const animateComponent = createElement(Animated.View, { style: [{ flexDirection: dir === 'x' ? 'row' : 'column', width: '100%', height: '100%' }, animatedStyles] }, wrapChildren({ children: arrPages }, { hasVarDec, varContext: varContextRef.current, textStyle, textProps })); const renderChildrens = showPagination ? [animateComponent, renderPagination()] : animateComponent; finalComponent = createElement(View, mergeProps, renderChildrens); if (!disableGesture) { finalComponent = createElement(GestureDetector, { gesture: gestureHandler }, finalComponent); } if (hasPositionFixed) { finalComponent = createElement(Portal, null, finalComponent); } return finalComponent; }); SwiperWrapper.displayName = 'MpxSwiperWrapper'; export default SwiperWrapper;