From 13bbdabea21b3b8e0fc95fea799f141e6c32ae72 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Wed, 6 Nov 2024 19:09:52 +0800 Subject: [PATCH 01/26] swiper refactor init --- .../react/mpx-picker-view-column.tsx | 36 +- .../components/react/mpx-picker-view.tsx | 26 +- .../runtime/components/react/mpx-swiper.tsx | 431 ++++++++++++++ .../components/react/mpx-swiper/carouse.tsx | 525 ------------------ .../components/react/mpx-swiper/index.tsx | 79 --- .../components/react/mpx-swiper/type.ts | 87 --- .../lib/template-compiler/compiler.js | 2 +- packages/webpack-plugin/package.json | 2 +- 8 files changed, 464 insertions(+), 724 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx delete mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/carouse.tsx delete mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/index.tsx delete mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/type.ts diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 24bd62291..e27f68522 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,5 +1,5 @@ -import { View, Animated, SafeAreaView, NativeScrollEvent, NativeSyntheticEvent, LayoutChangeEvent, ScrollView } from 'react-native' +import { View, SafeAreaView, NativeScrollEvent, NativeSyntheticEvent, LayoutChangeEvent, ScrollView } from 'react-native' import React, { forwardRef, useRef, useState, useEffect, ReactElement, ReactNode } from 'react' import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数 @@ -100,21 +100,9 @@ const _PickerViewColumn = forwardRef, const strKey = 'picker' + props.prefix + '-column' + index const arrHeight = (wrapperStyle.itemHeight + '').match(/\d+/g) || [] const iHeight = (arrHeight[0] || defaultItemHeight) as number - return - {wrapChildren( - { - children: item - }, - { - hasVarDec, - varContext: varContextRef.current, - textStyle, - textProps - } - )} - + return {item} }) - const totalHeight = itemH * 5 + const totalHeight = (itemH || defaultItemHeight) * 5 if (wrapperStyle.height && totalHeight !== wrapperStyle.height) { const fix = Math.ceil((totalHeight - wrapperStyle.height) / 2) arrChild.unshift() @@ -129,9 +117,9 @@ const _PickerViewColumn = forwardRef, } return arrChild } - + const innerChild = renderInnerchild() const renderScollView = () => { - return (, automaticallyAdjustContentInsets={false} {...layoutProps} onMomentumScrollEnd={onMomentumScrollEnd}> - {renderInnerchild()} - ) + {wrapChildren( + { + children: innerChild + }, + { + hasVarDec, + varContext: varContextRef.current, + textStyle, + textProps + } + )} + ) } return ( diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index 0b4a61e60..ff6bdddec 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -140,17 +140,7 @@ const _PickerView = forwardRef, PickerViewProp ...extraProps } const realElement = React.cloneElement(child as ReactElement, childProps) - return wrapChildren( - { - children: realElement - }, - { - hasVarDec, - varContext: varContextRef.current, - textStyle, - textProps - } - ) + return realElement } const renderTopMask = () => { @@ -206,10 +196,22 @@ const _PickerView = forwardRef, PickerViewProp return cloneChild(children, 0) } } + return ( {renderTopMask()} - {renderSubChild()} + {/*renderSubChild()*/} + {wrapChildren( + { + children: renderSubChild() + }, + { + hasVarDec, + varContext: varContextRef.current, + textStyle, + textProps + } + )} {renderBottomMask()} {!isSetW && renderLine()} diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx new file mode 100644 index 000000000..dd2857ab9 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -0,0 +1,431 @@ +import { View, NativeSyntheticEvent, Dimensions, NativeScrollPoint,NativeScrollEvent, LayoutChangeEvent } from 'react-native' +import { GestureDetector, Gesture } from 'react-native-gesture-handler'; +import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing } from 'react-native-reanimated'; + +import { JSX, forwardRef, useRef, useEffect, useState, ReactNode } from 'react' +import useInnerProps, { getCustomEvent } from './getInnerListeners' +import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数 +import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from './utils' +/** + * ✔ indicator-dots + * ✔ indicator-color + * ✔ indicator-active-color + * ✔ autoplay + * ✔ current + * ✔ interval(暂时不支持, Android可以, IOS不可以) + * ✔ duration + * ✔ circular + * ✔ vertical + * ✘ display-multiple-items + * ✘ previous-margin + * ✘ next-margin + * ✔ easing-function ="easeOutCubic" + * ✘ snap-to-edge + */ +type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic' +interface SwiperProps { + children?: ReactNode; + circular?: boolean; + current?: number; + interval?: number; + autoplay?: boolean; + // scrollView 只有安卓可以设 + duration?: number; + 'indicator-dots'?: boolean; + 'indicator-color'?: string; + 'indicator-active-color'?: string; + vertical?: boolean; + style: { + [key: string]: any + }; + 'easing-function'?: EaseType; + 'previous-margin'?: string; + 'next-margin'?: string; + 'enable-offset'?: boolean; + 'enable-var': boolean; + 'parent-font-size'?: number; + 'parent-width'?: number; + 'parent-height'?: number; + 'external-var-context'?: Record; + bindchange?: (event: NativeSyntheticEvent | unknown) => void; +} + +interface CarouseState { + width: number; + height: number; + index: number; + total: number; + // 设置scrollView初始的滚动坐标,contentOffset + offset: { + x: number, + y: number + }; + dir: 'x' | 'y'; +} + +/** + * 默认的Style类型 + */ +const styles: { [key: string]: Object } = { + slide: { + backgroundColor: 'transparent' + }, + container_x: { + position: 'relative' + }, + container_y: { + position: 'relative' + }, + 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' + } +} + +const dotCommonStyle = { + width: 8, + height: 8, + borderRadius: 4, + marginLeft: 3, + marginRight: 3, + marginTop: 3, + marginBottom: 3 +} + +const _SwiperWrapper = forwardRef, SwiperProps>((props: SwiperProps, ref): JSX.Element => { + const { + 'indicator-dots': showsPagination, + '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, + style = {} + } = props + const previousMargin = props['previous-margin'] ? parseInt(props['previous-margin']) : 0 + const nextMargin = props['next-margin'] ? parseInt(props['next-margin']) : 0 + const horizontal = props.vertical !== undefined ? !props.vertical : true + + const nodeRef = useRef(null) + useNodesRef(props, ref, nodeRef, {}) + + // 默认取水平方向的width + const { width } = Dimensions.get('window') + // 计算transfrom之类的 + const { + normalStyle, + hasVarDec, + varContextRef, + hasSelfPercent, + setWidth, + setHeight + } = useTransformStyle(style, { + enableVar, + externalVarContext, + parentFontSize, + parentWidth, + parentHeight + }) + const { textStyle, innerStyle } = splitStyle(normalStyle) + const { textProps } = splitProps(props) + const children = Array.isArray(props.children) ? props.children.filter(child => child) : (props.children ? [props.children] : []) + const totalElements = children.length + const defaultHeight = (normalStyle?.height || 150) + const defaultWidth = (normalStyle?.width || width || 375) + const dir = horizontal === false ? 'y' : 'x' + // state的offset默认值 + // const initIndex = props.circular ? props.current + 1: (props.current || 0) + // 记录真正的下标索引, 不包括循环前后加入的索引, 游标 + const initIndex = props.current || 0 + // 内部存储上一次的offset值 + const autoplayTimerRef = useRef | null>(null) + // 内部存储上一次的偏移量 + const internalsRef = useRef({ + offset: { + x: 0, + y: 0 + }, + isScrolling: false + }) + + const { + // 存储layout布局信息 + layoutRef, + layoutProps, + layoutStyle + } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout }) + + const [state, setState] = useState({ + width: dir === 'x' && typeof defaultWidth === 'number' ? defaultWidth - previousMargin - nextMargin : defaultWidth, + height: dir === 'y' && typeof defaultHeight === 'number' ? defaultHeight - previousMargin - nextMargin : defaultHeight, + // 真正的游标索引, 从0开始 + index: initIndex, + offset: { + x: 0, + y: 0 + }, + dir + } as CarouseState) + + const innerProps = useInnerProps(props, { + ref: nodeRef, + }, [ + 'style', + 'indicator-dots', + 'indicator-color', + 'indicator-active-color', + 'previous-margin', + 'vertical', + 'previous-margin', + 'next-margin', + 'easing-function' + ], { layoutRef: layoutRef }) + + + + /** + * @desc: 水平方向时,获取元素的布局,更新, 其中如果传递100%时需要依赖measure计算元算的宽高 + */ + function onWrapperLayout (e: LayoutChangeEvent) { + nodeRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => { + layoutRef.current = { x, y, width, height, offsetLeft, offsetTop } + const isWDiff = state.width !== width + const isHDiff = state.height !== height + if (isWDiff || isHDiff) { + const changeState = { + width: isWDiff ? width : state.width, + height: isHDiff ? height : state.height + } + const attr = state.dir === 'x' ? 'width' : 'height' + changeState[attr] = changeState[attr] - previousMargin - nextMargin + state.width = changeState.width + state.height = changeState.height + // 这里setState之后,会再触发重新渲染, renderScrollView会再次触发onScrollEnd, + setState((preState) => { + return { + ...preState, + width: changeState.width, + height: changeState.height + } + }) + } + }) + } + + function renderPagination () { + if (state.total <= 1) return null + const dots: Array = [] + const activeDotStyle = Object.assign({ backgroundColor: activeDotColor || '#007aff' }, dotCommonStyle) + const dotStyle = Object.assign({ backgroundColor: dotColor || 'rgba(0,0,0,.2)' }, dotCommonStyle) + for (let i = 0; i < state.total; i++) { + if (i === state.index) { + dots.push() + } else { + dots.push() + } + } + // 这里也可以用动画实现 + return ( + + {dots} + + ) + } + + function renderItems () { + const { width, height } = state + const pageStyle = { width: width, height: height } + // pages = ["0", "1", "2", "0", "1"] + const renderChild = children.slice() + if (props.circular && totalElements > 1) { + if (totalElements === 2) { + renderChild.concat(children).concat(children) + } else { + // 最前加两个 + renderChild.unshift(children[totalElements - 1]) + renderChild.unshift(children[totalElements - 2]) + // 最后加两个 + renderChild.push(children[0]) + renderChild.push(children[1]) + } + } + // 1. 不支持循环 + margin 模式 + return renderChild.map((child, i) => { + const extraStyle = {} as { + [key: string]: any + } + if (i === 0 && dir === 'x' && typeof width === 'number') { + previousMargin && (extraStyle.marginLeft = previousMargin) + } else if (i === totalElements - 1 && typeof width === 'number') { + nextMargin && (extraStyle.marginRight = nextMargin) + } + return ({child}) + }) + } + + + const arrPages: Array | ReactNode = renderItems() + + const targetIndex = useRef(0) + const initOffset = getInitIndex() + const offset = useSharedValue(initOffset) + const start = useSharedValue(initOffset); + const step = dir === 'x' ? state.width : state.height + const strTrans = 'translation' + dir.toUpperCase() + + function createAutoPlay () { + autoplayTimerRef.current && clearTimeout(autoplayTimerRef.current) + autoplayTimerRef.current = setTimeout(() => { + if (Number.isNaN(+step)) return false + // 获取下一个位置的坐标 + if (!props.circular) { + const targetPos = -(targetIndex.current + 1) * step + } else { + + } + }, props.interval || 500) + } + + useEffect(() => { + if (props.autoplay) { + createAutoPlay() + } else { + const targetOffset = getInitIndex() + offset.value = targetOffset + start.value = targetOffset + } + }, [props.autoplay, props.current, state.width, state.height]) + + function getInitIndex () { + if (Number.isNaN(+step)) return { x: 0, y: 0 } + const targetOffset = { x: 0, y: 0 } + if(props.circular) { + const targetIndex = (props.current || 0) + 2 + targetOffset[dir] = -step * targetIndex + } else if (props.current){ + targetOffset[dir] = -props.current * step + } + return targetOffset + } + + function getTargetPosition (e) { + let targetPos = 0 + const posView = e[dir] + // 移动的目标步长 + const moveDistance = Math.ceil(Math.abs(e[strTrans]) / step) * step + // 移动的目标步长之后的坐标, e[strTrans] < 0) 代表正向滚动 否则反向 + const moveTargetPos = e[strTrans] < 0 ? posView + moveDistance : posView - moveDistance + // 目标索引值 + const index = Math.floor(moveTargetPos / step) + if (!props.circular) { + targetIndex.current = index + targetPos = -targetIndex.current * step + } else { + if (e[strTrans] < 0) { + const a1 = index - (totalElements + 2) + const a2 = index - 2 + targetIndex.current = a1 >= 0 ? a1 : a2 + targetPos = -(targetIndex.current + 2) * step + } else { + targetIndex.current = index > 1 ? index - 2 : ( index === 0 ? totalElements - 2 : totalElements - 1) + targetPos = -(targetIndex.current + 2) * step + } + } + return { + targetOffset: { + x: dir === 'x' ? targetPos : offset.value.x, + y: dir === 'y' ? targetPos : offset.value.y + } + } + } + + function canMove (e) { + if (!props.circular && (e[strTrans] < 0 && e[dir] >= (totalElements - 1) * step || e[strTrans] > 0 && e[dir] <= step)) { + return false + } else { + return true + } + } + + const animatedStyles = useAnimatedStyle(() => { + return { + transform: [ + { + translateX: offset.value.x + } + ] + } + }) + + const gesture = Gesture.Pan() + .onBegin((e) => { + autoplayTimerRef.current && clearTimeout(autoplayTimerRef.current) + }) + .onUpdate((e) => { + if (!canMove(e)) { + return + } + offset.value = { + x: e.translationX + start.value.x, + y: e.translationY + start.value.y, + }; + }) + .onEnd((e) => { + if (!canMove(e)) { + return + } + const { targetOffset } = getTargetPosition(e) + console.log('------------onEnd1', e, targetOffset) + console.log('------------onEnd2', offset.value.x) + /* + withTiming(offset.value, { + easing: Easing.bounce, + }); + */ + offset.value = targetOffset + start.value = targetOffset + const eventData = getCustomEvent('change', {}, { detail: { current: targetIndex.current, source: 'touch' }, layoutRef: layoutRef }) + props.bindchange && props.bindchange(eventData) + }) + .onFinalize(() => { + }); + + return ( + + + {arrPages} + {/*wrapChildren({ + children: arrPages + }, { + hasVarDec, + varContext: varContextRef.current, + textStyle, + textProps + })*/} + + + {showsPagination && renderPagination()} + ) +}) +_SwiperWrapper.displayName = 'mpx-swiper' + +export default _SwiperWrapper diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/carouse.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/carouse.tsx deleted file mode 100644 index 009894687..000000000 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/carouse.tsx +++ /dev/null @@ -1,525 +0,0 @@ -/** - * swiper 实现 - */ -import { Animated, View, ScrollView, Dimensions, NativeSyntheticEvent, NativeScrollEvent, NativeScrollPoint, Platform, LayoutChangeEvent } from 'react-native' -import { JSX, forwardRef, useState, useRef, useEffect, ReactNode } from 'react' -import { CarouseProps, CarouseState } from './type' -import { getCustomEvent } from '../getInnerListeners' -import useNodesRef, { HandlerRef } from '../useNodesRef' // 引入辅助函数 -import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from '../utils' - -/** - * 默认的Style类型 - */ -const styles: { [key: string]: Object } = { - slide: { - backgroundColor: 'transparent' - }, - container_x: { - position: 'relative' - }, - container_y: { - position: 'relative' - }, - 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' - } -} - -const _Carouse = forwardRef, CarouseProps>((props, ref): JSX.Element => { - // 默认取水平方向的width - const { width } = Dimensions.get('window') - const { - style, - previousMargin = 0, - nextMargin = 0, - enableVar, - externalVarContext, - parentFontSize, - parentWidth, - parentHeight - } = props - // 计算transfrom之类的 - const { - normalStyle, - hasVarDec, - varContextRef, - hasSelfPercent, - setWidth, - setHeight - } = useTransformStyle(style, { - enableVar, - externalVarContext, - parentFontSize, - parentWidth, - parentHeight - }) - const { textStyle, innerStyle } = splitStyle(normalStyle) - const { textProps } = splitProps(props) - const newChild = Array.isArray(props.children) ? props.children.filter(child => child) : props.children - const totalElements = Array.isArray(newChild) ? newChild.length : (newChild ? 1 : 0) - const defaultHeight = (normalStyle?.height || 150) - const defaultWidth = (normalStyle?.width || width || 375) - const dir = props.horizontal === false ? 'y' : 'x' - // state的offset默认值 - // const initIndex = props.circular ? props.current + 1: (props.current || 0) - // 记录真正的下标索引, 不包括循环前后加入的索引, 游标 - const initIndex = props.current || 0 - // 这里要排除超过元素个数的设置 - const initOffsetIndex = initIndex + (props.circular && totalElements > 1 ? 1 : 0) - const defaultX = (defaultWidth * initOffsetIndex) || 0 - const defaultY = (defaultHeight * initOffsetIndex) || 0 - // 主动scorllTo时是否要出发onScrollEnd - const needTriggerScrollEnd = useRef(true) - // 内部存储上一次的offset值 - const autoplayTimerRef = useRef | null>(null) - const scrollViewRef = useRef(null) - useNodesRef(props, ref, scrollViewRef, {}) - const { - // 存储layout布局信息 - layoutRef, - layoutProps, - layoutStyle - } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout: onWrapperLayout }) - // 内部存储上一次的偏移量 - const internalsRef = useRef({ - offset: { - x: 0, - y: 0 - }, - isScrolling: false - }) - const isDragRef = useRef(false) - const [state, setState] = useState({ - width: dir === 'x' && typeof defaultWidth === 'number' ? defaultWidth - previousMargin - nextMargin : defaultWidth, - height: dir === 'y' && typeof defaultHeight === 'number' ? defaultHeight - previousMargin - nextMargin : defaultHeight, - // 真正的游标索引, 从0开始 - index: initIndex, - total: totalElements, - offset: { - x: 0, - y: 0 - }, - dir - } as CarouseState) - /** - * @desc: 开启下一次自动轮播 - */ - function createAutoPlay () { - autoplayTimerRef.current && clearTimeout(autoplayTimerRef.current) - autoplayTimerRef.current = setTimeout(() => { - startAutoPlay() - }, props.interval || 500) - } - - useEffect(() => { - // 确认这个是变化的props变化的时候才执行,还是初始化的时候就执行 - if (props.autoplay) { - createAutoPlay() - } - }, [props.autoplay, props.current, state.index, state.width, state.height]) - - useEffect(() => { - // 确认这个是变化的props变化的时候才执行,还是初始化的时候就执行 - if (!props.autoplay && props.current !== undefined && props.current !== state.index) { - const initIndex = props.current || 0 - // 这里要排除超过元素个数的设置 - const { nextIndex, nextOffset } = getMultiNextConfig(props.current) - // 1. 安卓需要主动更新下内部状态, 2. IOS不能触发完wcrollTo之后立即updateState, 会造成滑动两次 - // 2. setTimeout 是fix 当再渲染过程中触发scrollTo失败的问题 - if (Platform.OS === 'ios') { - needTriggerScrollEnd.current = false - setTimeout(() => { - scrollViewRef.current?.scrollTo({ - ...nextOffset, - animated: true - }) - }, 50) - } else { - updateState(nextIndex, nextOffset) - } - } - }, [props.current, state.width, state.height]) - - function getMultiNextConfig (target: number) { - const step = state.dir === 'x' ? state.width : state.height - const targetPos = step * props.current - const targetOffset = { - x: dir === 'x' ? targetPos : 0, - y: dir === 'y' ? targetPos : 0 - } - return { - nextIndex: target, - nextOffset: targetOffset - } - } - /** - * @desc: 更新状态: index和offset, 并响应索引变化的事件 - * scrollViewOffset: 移动到的目标位置 - */ - function updateIndex (scrollViewOffset: NativeScrollPoint, useIndex = false) { - const { nextIndex, nextOffset } = getNextConfig(scrollViewOffset) - updateState(nextIndex, nextOffset) - // 更新完状态之后, 开启新的loop - } - - /** - * 更新索引状态 - */ - function updateState (index: number, offset: { x: number, y: number}) { - internalsRef.current.offset = offset - setState((preState) => { - const newState = { - ...preState, - index: index, - // offset用来指示当前scrollView的偏移量 - offset: offset - } - return newState - }) - internalsRef.current.isScrolling = false - // getCustomEvent - const eventData = getCustomEvent('change', {}, { detail: { current: index, source: 'touch' }, layoutRef: layoutRef }) - props.bindchange && props.bindchange(eventData) - } - - /** - * @desc: 获取下一个位置的索引、scrollView的contentOffset、scrollTo到的offset - * @desc: 包括正循环、反向循环、不循环 - * 其中循环模式为了实现无缝链接, 会将结合contentOffset, 和 scrollTo的offset, - * 先scrollTo一个位置的坐标, 然后通过updateIndex设置真正的index和内容的offset,视觉上是无缝 - */ - function getNextConfig (scrollViewOffset: NativeScrollPoint) { - const step = state.dir === 'x' ? state.width : state.height - const currentOffset = state.offset - let nextIndex = state.index + 1 - let nextOffset = currentOffset - // autoMoveOffset scrollView 滚动到前后增加的位置 - let autoMoveOffset = currentOffset - let isBack = false - let isAutoEnd = false - // 如果是循环反向的 - if (scrollViewOffset?.[state.dir] < currentOffset[state.dir]) { - isBack = true - nextIndex = isBack ? nextIndex - 2 : nextIndex - } - if (!props.circular) { - nextOffset = Object.assign({}, currentOffset, { [state.dir]: step * nextIndex }) - } else { - if (isBack) { - if (nextIndex < 0) { - // 反向: scollView 滚动到虚拟的位置 - autoMoveOffset = Object.assign({}, currentOffset, { [state.dir]: 0 }) - nextIndex = state.total - 1 - // 反向: 数组最后一个index - nextOffset = Object.assign({}, currentOffset, { [state.dir]: step * state.total }) - isAutoEnd = true - } else { - // 反向非最后一个 - nextOffset = Object.assign({}, currentOffset, { [state.dir]: (nextIndex + 1) * step }) - } - } else { - if (nextIndex > state.total - 1) { - autoMoveOffset = Object.assign({}, currentOffset, { [state.dir]: step * (nextIndex + 1) }) - nextIndex = 0 - nextOffset = Object.assign({}, currentOffset, { [state.dir]: step }) - isAutoEnd = true - } else { - // nextIndex = nextIndex, - nextOffset = Object.assign({}, currentOffset, { [state.dir]: (nextIndex + 1) * step }) - } - } - } - return { - // 下一个要滚动到的实际元素的索引 - nextIndex, - // 下一个要滚动到实际元素的offset - nextOffset, - // scrollTo一个位置的坐标, 虚拟元素的位置 - autoMoveOffset, - isAutoEnd - } - } - - /** - * @desc: 开启自动轮播 - */ - function startAutoPlay () { - if (state.width && isNaN(+state.width)) { - createAutoPlay() - return - } - if (!Array.isArray(props.children)) { - return - } - const step = state.dir === 'x' ? state.width : state.height - const { nextOffset, autoMoveOffset, isAutoEnd } = getNextConfig(state.offset) - // 这里可以scroll到下一个元素, 但是把scrollView的偏移量在设置为content,视觉效果就没了吧 - if (Platform.OS === 'ios') { - if (!isAutoEnd) { - scrollViewRef.current?.scrollTo({ x: nextOffset.x, y: nextOffset.y, animated: true }) - } else { - if (state.dir === 'x') { - scrollViewRef.current?.scrollTo({ x: autoMoveOffset.x, y: autoMoveOffset.x, animated: true }) - } else { - scrollViewRef.current?.scrollTo({ x: autoMoveOffset.y, y: autoMoveOffset.y, animated: true }) - } - } - } else { - if (!isAutoEnd) { - scrollViewRef.current?.scrollTo({ x: nextOffset.x, y: nextOffset.y, animated: true }) - onScrollEnd({ - nativeEvent: { - contentOffset: { - x: +nextOffset.x, - y: +nextOffset.y - } - } - } as NativeSyntheticEvent) - } else { - // 安卓无法实现视觉的无缝连接, 只能回到真正的位置, 且安卓调用scrollTo不能触发onMomentumScrollEnd,还未找到为啥 - if (state.dir === 'x') { - scrollViewRef.current?.scrollTo({ x: step, y: step, animated: true }) - } else { - scrollViewRef.current?.scrollTo({ x: autoMoveOffset.x, y: step, animated: true }) - } - updateState(0, nextOffset) - } - } - } - - /** - * 当用户开始拖动此视图时调用此函数, 更新当前在滚动态 - */ - function onScrollBegin () { - internalsRef.current.isScrolling = true - } - - /** - * 当用户开始拖动结束 - * 注意: 当手动调用scrollTo的时候, 安卓不会触发onMomentumScrollEnd, IOS会触发onMomentumScrollEnd - */ - function onScrollEnd (event: NativeSyntheticEvent) { - if (Platform.OS === 'ios' && !needTriggerScrollEnd.current) { - const { nextIndex, nextOffset } = getMultiNextConfig(props.current) - updateState(nextIndex, nextOffset) - needTriggerScrollEnd.current = true - return - } - if (totalElements === 1) { - return - } - internalsRef.current.isScrolling = false - // 用户手动滑动更新索引后,如果开启了自动轮播等重新开始 - updateIndex(event.nativeEvent.contentOffset, true) - } - - /** - * 当拖拽结束时,检测是否可滚动 - */ - function onScrollEndDrag (event: NativeSyntheticEvent) { - const { contentOffset } = event.nativeEvent - const { index, total } = state - isDragRef.current = true - const internalOffset = internalsRef.current.offset - const previousOffset = props.horizontal ? internalOffset.x : internalOffset.y - const moveOffset = props.horizontal ? contentOffset.x : contentOffset.y - if (previousOffset === moveOffset && (index === 0 || index === total - 1)) { - internalsRef.current.isScrolling = false - } - } - - /** - * @desc: 水平方向时,获取元素的布局,更新, 其中如果传递100%时需要依赖measure计算元算的宽高 - */ - function onWrapperLayout (e: LayoutChangeEvent) { - scrollViewRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => { - layoutRef.current = { x, y, width, height, offsetLeft, offsetTop } - const isWDiff = state.width !== width - const isHDiff = state.height !== height - if (isWDiff || isHDiff) { - const changeState = { - width: isWDiff ? width : state.width, - height: isHDiff ? height : state.height - } - const attr = state.dir === 'x' ? 'width' : 'height' - changeState[attr] = changeState[attr] - previousMargin - nextMargin - const correctOffset = Object.assign({}, state.offset, { - [state.dir]: initOffsetIndex * (state.dir === 'x' ? changeState.width : changeState.height) - }) - state.width = changeState.width - state.height = changeState.height - // 这里setState之后,会再触发重新渲染, renderScrollView会再次触发onScrollEnd, - setState((preState) => { - return { - ...preState, - width: changeState.width, - height: changeState.height - } - }) - } - props.getInnerLayout && props.getInnerLayout(layoutRef) - }) - } - - function getOffset (): Array { - const step = state.dir === 'x' ? state.width : state.height - if (!step || Number.isNaN(+step)) return [] - const offsetArray = [] - for (let i = 0; i < totalElements; i++) { - offsetArray.push(i * step) - } - return offsetArray - } - - function renderScrollView (pages: ReactNode) { - const offsetsArray = getOffset() - const scrollElementProps = { - ref: scrollViewRef, - horizontal: props.horizontal, - pagingEnabled: true, - snapToOffsets: offsetsArray, - decelerationRate: 0.99, // 'fast' - showsHorizontalScrollIndicator: false, - showsVerticalScrollIndicator: false, - bounces: false, - scrollsToTop: false, - removeClippedSubviews: true, - automaticallyAdjustContentInsets: false - } - const layoutStyle = dir === 'x' ? { width: defaultWidth, height: defaultHeight } : { width: defaultWidth } - return ( - - {pages} - - ) - } - - function renderPagination () { - if (state.total <= 1) return null - const dots: Array = [] - const activeDotStyle = [{ - backgroundColor: props.activeDotColor || '#007aff', - width: 8, - height: 8, - borderRadius: 4, - marginLeft: 3, - marginRight: 3, - marginTop: 3, - marginBottom: 3 - }] - const dotStyle = [{ - backgroundColor: props.dotColor || 'rgba(0,0,0,.2)', - width: 8, - height: 8, - borderRadius: 4, - marginLeft: 3, - marginRight: 3, - marginTop: 3, - marginBottom: 3 - }] - for (let i = 0; i < state.total; i++) { - if (i === state.index) { - dots.push() - } else { - dots.push() - } - } - return ( - - {dots} - - ) - } - - function renderPages () { - const { width, height } = state - const { children } = props - const { circular } = props - const pageStyle = { width: width, height: height } - // 设置了previousMargin或者nextMargin, - // 1. 元素的宽度是减去这两个数目之和 - // 2. previousMargin设置marginLeft正值, nextmargin设置marginRight负值 - // 3. 第一个元素设置previousMargin 和 nextMargin, 最后一个元素 - if (totalElements > 1 && Array.isArray(children)) { - let arrElements: (Array) = [] - // pages = ["2", "0", "1", "2", "0"] - const pages = Array.isArray(children) ? Object.keys(children) : [] - /* 无限循环的时候 */ - if (circular) { - pages.unshift(totalElements - 1 + '') - pages.push('0') - } - arrElements = pages.map((page, i) => { - const extraStyle = {} as { - [key: string]: any - } - if (i === 0 && dir === 'x' && typeof width === 'number') { - previousMargin && (extraStyle.marginLeft = previousMargin) - } else if (i === pages.length - 1 && typeof width === 'number') { - nextMargin && (extraStyle.marginRight = nextMargin) - } - return ( - {wrapChildren( - { - children: children[+page] - }, - { - hasVarDec, - varContext: varContextRef.current, - textStyle, - textProps - } - )} - ) - }) - return arrElements - } else { - const realElement = ( - - {children} - - ) - return realElement - } - } - - const pages: Array | ReactNode = renderPages() - return ( - {renderScrollView(pages)} - {props.showsPagination && renderPagination()} - ) -}) - -_Carouse.displayName = '_Carouse' - -export default _Carouse diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/index.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/index.tsx deleted file mode 100644 index f379f7388..000000000 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/index.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { ScrollView } from 'react-native' -import { JSX, MutableRefObject, forwardRef, useRef } from 'react' -import Carouse from './carouse' -import { SwiperProps } from './type' -import useInnerProps from '../getInnerListeners' -import useNodesRef, { HandlerRef } from '../useNodesRef' // 引入辅助函数 -/** - * ✔ indicator-dots - * ✔ indicator-color - * ✔ indicator-active-color - * ✔ autoplay - * ✔ current - * ✔ interval - * ✔ duration - * ✔ circular - * ✔ vertical - * ✘ display-multiple-items - * ✘ previous-margin - * ✘ next-margin - * ✔ easing-function ="easeOutCubic" - * ✘ snap-to-edge - */ -const _SwiperWrapper = forwardRef, SwiperProps>((props: SwiperProps, ref): JSX.Element => { - const { children } = props - const innerLayout = useRef({}) - const swiperProp = { - circular: props.circular || false, - current: props.current || 0, - autoplay: props.autoplay || false, - duration: props.duration || 500, - interval: props.interval || 5000, - showsPagination: props['indicator-dots'], - dotColor: props['indicator-color'] || 'rgba(0, 0, 0, .3)', - activeDotColor: props['indicator-active-color'] || '#000000', - horizontal: props.vertical !== undefined ? !props.vertical : true, - previousMargin: props['previous-margin'] ? parseInt(props['previous-margin']) : 0, - nextMargin: props['next-margin'] ? parseInt(props['next-margin']) : 0, - enableOffset: props['enable-offset'] || true, - enableVar: props['enable-var'] || false, - parentFontSize: props['parent-font-size'], - parentWidth: props['parent-width'], - parentHeight: props['parent-height'], - style: props.style || {}, - externalVarContext: props['external-var-context'], - bindchange: props.bindchange, - easingFunction: props['easing-function'] || 'default' - } - - const nodeRef = useRef(null) - useNodesRef(props, ref, nodeRef, {}) - - const innerProps = useInnerProps(props, { - ref: nodeRef - }, [ - 'indicator-dots', - 'indicator-color', - 'indicator-active-color', - 'previous-margin', - 'vertical', - 'previous-margin', - 'next-margin', - 'easing-function' - ], { layoutRef: innerLayout }) - - const getInnerLayout = (layout: MutableRefObject<{}>) => { - innerLayout.current = layout.current - } - - return - {children} - -}) -_SwiperWrapper.displayName = 'mpx-swiper' - -export default _SwiperWrapper diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/type.ts b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/type.ts deleted file mode 100644 index 2815f9acc..000000000 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper/type.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { ReactNode } from 'react' -import { NativeSyntheticEvent } from 'react-native' - -export type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic' - -export interface SwiperProps { - children?: ReactNode; - circular?: boolean; - current?: number; - interval?: number; - autoplay?: boolean; - duration?: number; - 'indicator-dots'?: boolean; - 'indicator-color'?: string; - 'indicator-active-color'?: string; - vertical?: boolean; - style: { - [key: string]: any - }; - 'easing-function'?: EaseType; - 'previous-margin'?: string; - 'next-margin'?: string; - 'enable-offset'?: boolean; - 'enable-var': boolean; - 'parent-font-size'?: number; - 'parent-width'?: number; - 'parent-height'?: number; - 'external-var-context'?: Record; - bindchange?: (event: NativeSyntheticEvent | unknown) => void; -} - -export interface CarouseProps { - children?: ReactNode; - circular?: boolean; - current: number; - autoplay?: boolean; - duration?: number; - interval?: number; - showsPagination?: boolean; - dotColor?: string; - activeDotColor?: string; - horizontal?: boolean; - easingFunction?: EaseType; - previousMargin?: number; - nextMargin?: number; - enableOffset?: boolean; - parentFontSize?: number; - parentWidth?: number; - parentHeight?: number; - bindchange?: (event: NativeSyntheticEvent | unknown) => void; - getInnerLayout: Function; - innerProps: Object; - style: { - [key: string]: any - }; - enableVar: boolean; - externalVarContext?: Record; -} - -export interface CarouseState { - children: Array | ReactNode, - width: number; - height: number; - index: number; - total: number; - // 设置scrollView初始的滚动坐标,contentOffset - offset: { - x: number, - y: number - }; - // 是否结束自动轮播,手动设置滚动到具体位置时结束 - autoplayEnd: boolean; - loopJump: boolean; - dir: 'x' | 'y'; - isScrollEnd: boolean -} - -export interface ScrollElementProps { - pagingEnabled: boolean, - showsHorizontalScrollIndicator: boolean, - showsVerticalScrollIndicator: boolean, - bounces: boolean, - scrollsToTop: boolean, - removeClippedSubviews: boolean, - automaticallyAdjustContentInsets: boolean, - horizontal: boolean -} diff --git a/packages/webpack-plugin/lib/template-compiler/compiler.js b/packages/webpack-plugin/lib/template-compiler/compiler.js index 11eab8a1d..5d43f0197 100644 --- a/packages/webpack-plugin/lib/template-compiler/compiler.js +++ b/packages/webpack-plugin/lib/template-compiler/compiler.js @@ -2233,7 +2233,7 @@ function processBuiltInComponents (el, meta) { const tag = el.tag if (!meta.builtInComponentsMap[tag]) { if (isReact(mode)) { - meta.builtInComponentsMap[tag] = `${builtInComponentsPrefix}/react/dist/${tag}` + meta.builtInComponentsMap[tag] = `${builtInComponentsPrefix}/react/${tag}` } else { meta.builtInComponentsMap[tag] = `${builtInComponentsPrefix}/${mode}/${tag}` } diff --git a/packages/webpack-plugin/package.json b/packages/webpack-plugin/package.json index 911be8687..c9d82b704 100644 --- a/packages/webpack-plugin/package.json +++ b/packages/webpack-plugin/package.json @@ -89,7 +89,7 @@ "react-native": "^0.74.5", "react-native-gesture-handler": "^2.18.1", "react-native-linear-gradient": "^2.8.3", - "react-native-safe-area-context": "^4.12.0", + "react-native-safe-area-context": "^4.14.0", "react-native-webview": "^13.12.2", "rimraf": "^6.0.1" }, From e3f09a4c8072afff60067c89b7f94dcf84427111 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Wed, 6 Nov 2024 23:30:18 +0800 Subject: [PATCH 02/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=BD=AE=E6=92=AD=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 105 ++++++++---------- 1 file changed, 49 insertions(+), 56 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index dd2857ab9..a46172852 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -67,15 +67,6 @@ interface CarouseState { * 默认的Style类型 */ const styles: { [key: string]: Object } = { - slide: { - backgroundColor: 'transparent' - }, - container_x: { - position: 'relative' - }, - container_y: { - position: 'relative' - }, pagination_x: { position: 'absolute', bottom: 25, @@ -152,20 +143,8 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const defaultHeight = (normalStyle?.height || 150) const defaultWidth = (normalStyle?.width || width || 375) const dir = horizontal === false ? 'y' : 'x' - // state的offset默认值 - // const initIndex = props.circular ? props.current + 1: (props.current || 0) - // 记录真正的下标索引, 不包括循环前后加入的索引, 游标 - const initIndex = props.current || 0 // 内部存储上一次的offset值 - const autoplayTimerRef = useRef | null>(null) - // 内部存储上一次的偏移量 - const internalsRef = useRef({ - offset: { - x: 0, - y: 0 - }, - isScrolling: false - }) + const autoplayTimerRef = useRef | null>(null) const { // 存储layout布局信息 @@ -173,12 +152,11 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout }) + console.log('--------------layoutStyle', layoutStyle) const [state, setState] = useState({ width: dir === 'x' && typeof defaultWidth === 'number' ? defaultWidth - previousMargin - nextMargin : defaultWidth, height: dir === 'y' && typeof defaultHeight === 'number' ? defaultHeight - previousMargin - nextMargin : defaultHeight, - // 真正的游标索引, 从0开始 - index: initIndex, offset: { x: 0, y: 0 @@ -200,11 +178,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p 'easing-function' ], { layoutRef: layoutRef }) - - - /** - * @desc: 水平方向时,获取元素的布局,更新, 其中如果传递100%时需要依赖measure计算元算的宽高 - */ function onWrapperLayout (e: LayoutChangeEvent) { nodeRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => { layoutRef.current = { x, y, width, height, offsetLeft, offsetTop } @@ -256,18 +229,26 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const pageStyle = { width: width, height: height } // pages = ["0", "1", "2", "0", "1"] const renderChild = children.slice() - if (props.circular && totalElements > 1) { - if (totalElements === 2) { - renderChild.concat(children).concat(children) - } else { - // 最前加两个 - renderChild.unshift(children[totalElements - 1]) - renderChild.unshift(children[totalElements - 2]) - // 最后加两个 - renderChild.push(children[0]) - renderChild.push(children[1]) + console.log('------------renderItems', step) + if (!Number.isNaN(+step)) { + console.log('-------------renderItems2') + if (props.circular && totalElements > 1) { + if (totalElements === 2) { + renderChild.concat(children).concat(children) + } else { + // 最前加两个 + renderChild.unshift(children[totalElements - 1]) + renderChild.unshift(children[totalElements - 2]) + // 最后加两个 + renderChild.push(children[0]) + renderChild.push(children[1]) + } } - } + console.log('-------------renderItems3') + const targetOffset = getInitIndex() + offset.value = targetOffset + start.value = targetOffset + } // 1. 不支持循环 + margin 模式 return renderChild.map((child, i) => { const extraStyle = {} as { @@ -282,8 +263,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }) } - - const arrPages: Array | ReactNode = renderItems() const targetIndex = useRef(0) const initOffset = getInitIndex() @@ -291,23 +270,38 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const start = useSharedValue(initOffset); const step = dir === 'x' ? state.width : state.height const strTrans = 'translation' + dir.toUpperCase() + const arrPages: Array | ReactNode = renderItems() function createAutoPlay () { - autoplayTimerRef.current && clearTimeout(autoplayTimerRef.current) - autoplayTimerRef.current = setTimeout(() => { - if (Number.isNaN(+step)) return false - // 获取下一个位置的坐标 + autoplayTimerRef.current && clearInterval(autoplayTimerRef.current) + autoplayTimerRef.current = setInterval(() => { + const targetOffset = getInitIndex() if (!props.circular) { - const targetPos = -(targetIndex.current + 1) * step + // 获取下一个位置的坐标, 循环到最后一个元素,直接停止 + if (targetIndex.current === totalElements - 1) { + autoplayTimerRef.current && clearTimeout(autoplayTimerRef.current) + autoplayTimerRef.current = null + return + } + targetIndex.current = targetIndex.current + 1 + targetOffset[dir] = -targetIndex.current * step } else { - + if (targetIndex.current === totalElements - 1) { + targetIndex.current = 0 + } else { + targetIndex.current = targetIndex.current + 1 + } + targetOffset[dir] = -(targetIndex.current + 2) * step + console.log('---------createAutoPlay----2-', targetIndex, targetOffset) } + offset.value = targetOffset + start.value = targetOffset }, props.interval || 500) } useEffect(() => { if (props.autoplay) { - createAutoPlay() + !Number.isNaN(+step) && createAutoPlay() } else { const targetOffset = getInitIndex() offset.value = targetOffset @@ -367,12 +361,10 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } const animatedStyles = useAnimatedStyle(() => { - return { - transform: [ - { - translateX: offset.value.x - } - ] + if (dir === 'x') { + return { transform: [{ translateX: offset.value.x }]} + } else { + return { transform: [{ translateY: offset.value.y }]} } }) @@ -405,13 +397,14 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p start.value = targetOffset const eventData = getCustomEvent('change', {}, { detail: { current: targetIndex.current, source: 'touch' }, layoutRef: layoutRef }) props.bindchange && props.bindchange(eventData) + !Number.isNaN(+step) && createAutoPlay() }) .onFinalize(() => { }); return ( - + {arrPages} {/*wrapChildren({ children: arrPages From 0ce5bb5d3a93e174d4d8e2fa7f10bd26def23f50 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Thu, 7 Nov 2024 23:48:59 +0800 Subject: [PATCH 03/26] add interpolate animateion --- .../lib/runtime/components/react/mpx-swiper.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index a46172852..dd731c12a 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -152,7 +152,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout }) - console.log('--------------layoutStyle', layoutStyle) + console.log('--------------layoutStyle', layoutStyle, layoutRef.current) const [state, setState] = useState({ width: dir === 'x' && typeof defaultWidth === 'number' ? defaultWidth - previousMargin - nextMargin : defaultWidth, @@ -304,6 +304,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p !Number.isNaN(+step) && createAutoPlay() } else { const targetOffset = getInitIndex() + console.log('---------useEffect---',targetOffset) offset.value = targetOffset start.value = targetOffset } @@ -362,8 +363,10 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const animatedStyles = useAnimatedStyle(() => { if (dir === 'x') { + console.log(1111) return { transform: [{ translateX: offset.value.x }]} } else { + console.log(2222, offset.value.y) return { transform: [{ translateY: offset.value.y }]} } }) @@ -397,14 +400,14 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p start.value = targetOffset const eventData = getCustomEvent('change', {}, { detail: { current: targetIndex.current, source: 'touch' }, layoutRef: layoutRef }) props.bindchange && props.bindchange(eventData) - !Number.isNaN(+step) && createAutoPlay() + props.autoplay && !Number.isNaN(+step) && createAutoPlay() }) .onFinalize(() => { }); - + return ( - + {arrPages} {/*wrapChildren({ children: arrPages From 763d32add20b4ea223c1cee5f117a5a743925485 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Fri, 8 Nov 2024 17:26:49 +0800 Subject: [PATCH 04/26] animation fix & ts --- .../runtime/components/react/mpx-swiper.tsx | 180 ++++++++++++------ 1 file changed, 124 insertions(+), 56 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index dd731c12a..c74ee1e09 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -1,5 +1,5 @@ -import { View, NativeSyntheticEvent, Dimensions, NativeScrollPoint,NativeScrollEvent, LayoutChangeEvent } from 'react-native' -import { GestureDetector, Gesture } from 'react-native-gesture-handler'; +import { View, NativeSyntheticEvent, Dimensions, LayoutChangeEvent } from 'react-native' +import { GestureDetector, Gesture, GestureUpdateEvent, PanGestureHandlerEventPayload } from 'react-native-gesture-handler'; import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing } from 'react-native-reanimated'; import { JSX, forwardRef, useRef, useEffect, useState, ReactNode } from 'react' @@ -12,7 +12,7 @@ import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } fr * ✔ indicator-active-color * ✔ autoplay * ✔ current - * ✔ interval(暂时不支持, Android可以, IOS不可以) + * ✔ interval * ✔ duration * ✔ circular * ✔ vertical @@ -23,6 +23,7 @@ import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } fr * ✘ snap-to-edge */ type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic' +type StrTransType = 'translationX' | 'translationY' interface SwiperProps { children?: ReactNode; circular?: boolean; @@ -77,7 +78,6 @@ const styles: { [key: string]: Object } = { justifyContent: 'center', alignItems: 'center' }, - pagination_y: { position: 'absolute', right: 15, @@ -100,6 +100,14 @@ const dotCommonStyle = { marginBottom: 3 } +const easeMap = { + 'default': Easing.ease, + 'linear': Easing.linear, + 'easeInCubic': Easing.in(Easing.cubic), + 'easeOutCubic': Easing.out(Easing.cubic), + 'easeInOutCubic': Easing.inOut(Easing.cubic) +} + const _SwiperWrapper = forwardRef, SwiperProps>((props: SwiperProps, ref): JSX.Element => { const { 'indicator-dots': showsPagination, @@ -114,6 +122,8 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } = props const previousMargin = props['previous-margin'] ? parseInt(props['previous-margin']) : 0 const nextMargin = props['next-margin'] ? parseInt(props['next-margin']) : 0 + const easeingFunc = props['easing-function'] || 'default' + const easeDuration = props['duration'] || 500 const horizontal = props.vertical !== undefined ? !props.vertical : true const nodeRef = useRef(null) @@ -152,16 +162,10 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout }) - console.log('--------------layoutStyle', layoutStyle, layoutRef.current) const [state, setState] = useState({ width: dir === 'x' && typeof defaultWidth === 'number' ? defaultWidth - previousMargin - nextMargin : defaultWidth, - height: dir === 'y' && typeof defaultHeight === 'number' ? defaultHeight - previousMargin - nextMargin : defaultHeight, - offset: { - x: 0, - y: 0 - }, - dir + height: dir === 'y' && typeof defaultHeight === 'number' ? defaultHeight - previousMargin - nextMargin : defaultHeight } as CarouseState) const innerProps = useInnerProps(props, { @@ -175,6 +179,10 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p 'vertical', 'previous-margin', 'next-margin', + 'easing-function', + 'autoplay', + 'circular', + 'interval', 'easing-function' ], { layoutRef: layoutRef }) @@ -188,7 +196,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p width: isWDiff ? width : state.width, height: isHDiff ? height : state.height } - const attr = state.dir === 'x' ? 'width' : 'height' + const attr = dir === 'x' ? 'width' : 'height' changeState[attr] = changeState[attr] - previousMargin - nextMargin state.width = changeState.width state.height = changeState.height @@ -229,9 +237,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const pageStyle = { width: width, height: height } // pages = ["0", "1", "2", "0", "1"] const renderChild = children.slice() - console.log('------------renderItems', step) if (!Number.isNaN(+step)) { - console.log('-------------renderItems2') if (props.circular && totalElements > 1) { if (totalElements === 2) { renderChild.concat(children).concat(children) @@ -244,10 +250,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p renderChild.push(children[1]) } } - console.log('-------------renderItems3') - const targetOffset = getInitIndex() - offset.value = targetOffset - start.value = targetOffset } // 1. 不支持循环 + margin 模式 return renderChild.map((child, i) => { @@ -265,17 +267,18 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const targetIndex = useRef(0) - const initOffset = getInitIndex() + const initOffset = getInitOffset() const offset = useSharedValue(initOffset) const start = useSharedValue(initOffset); const step = dir === 'x' ? state.width : state.height const strTrans = 'translation' + dir.toUpperCase() + const isAutoFirst = useRef(true) const arrPages: Array | ReactNode = renderItems() function createAutoPlay () { autoplayTimerRef.current && clearInterval(autoplayTimerRef.current) autoplayTimerRef.current = setInterval(() => { - const targetOffset = getInitIndex() + const targetOffset = { x: 0, y: 0 } if (!props.circular) { // 获取下一个位置的坐标, 循环到最后一个元素,直接停止 if (targetIndex.current === totalElements - 1) { @@ -285,32 +288,59 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } targetIndex.current = targetIndex.current + 1 targetOffset[dir] = -targetIndex.current * step + + offset.value = withTiming(targetOffset, { + duration: easeDuration, + easing: easeMap[easeingFunc] + }) + start.value = targetOffset } else { if (targetIndex.current === totalElements - 1) { targetIndex.current = 0 + targetOffset[dir] = -(totalElements + 2) * step + // 执行动画到下一帧 + offset.value = withTiming(targetOffset, { + duration: easeDuration + }) + const initOffset = { x: 0, y: 0 } + initOffset[dir] = -step * 2 + // 将开始位置设置为真正的位置 + setTimeout(() => { + offset.value = initOffset + start.value = initOffset + }, easeDuration) } else { targetIndex.current = targetIndex.current + 1 + targetOffset[dir] = -(targetIndex.current + 2) * step + // 执行动画到下一帧 + offset.value = withTiming(targetOffset, { + duration: easeDuration, + easing: easeMap[easeingFunc] + }) + start.value = targetOffset } - targetOffset[dir] = -(targetIndex.current + 2) * step console.log('---------createAutoPlay----2-', targetIndex, targetOffset) } - offset.value = targetOffset - start.value = targetOffset }, props.interval || 500) } useEffect(() => { - if (props.autoplay) { - !Number.isNaN(+step) && createAutoPlay() + if (props.autoplay && !Number.isNaN(+step)) { + if (isAutoFirst.current) { + isAutoFirst.current = false + const targetOffset = getInitOffset() + offset.value = targetOffset + start.value = targetOffset + } + createAutoPlay() } else { - const targetOffset = getInitIndex() - console.log('---------useEffect---',targetOffset) + const targetOffset = getInitOffset() offset.value = targetOffset start.value = targetOffset } }, [props.autoplay, props.current, state.width, state.height]) - function getInitIndex () { + function getInitOffset () { if (Number.isNaN(+step)) return { x: 0, y: 0 } const targetOffset = { x: 0, y: 0 } if(props.circular) { @@ -322,30 +352,53 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p return targetOffset } - function getTargetPosition (e) { + function getTargetPosition (e: GestureUpdateEvent) { let targetPos = 0 + let resetPos = 0 const posView = e[dir] + // 移动的距离 + const transDistance = e[strTrans as StrTransType] // 移动的目标步长 - const moveDistance = Math.ceil(Math.abs(e[strTrans]) / step) * step + const moveDistance = Math.ceil(Math.abs(transDistance) / step) * step // 移动的目标步长之后的坐标, e[strTrans] < 0) 代表正向滚动 否则反向 - const moveTargetPos = e[strTrans] < 0 ? posView + moveDistance : posView - moveDistance + const moveTargetPos = transDistance < 0 ? posView + moveDistance : posView - moveDistance // 目标索引值 const index = Math.floor(moveTargetPos / step) + // 是否临界点 + let isCriticalItem = false if (!props.circular) { targetIndex.current = index targetPos = -targetIndex.current * step } else { - if (e[strTrans] < 0) { + // 正向滚动 + if (transDistance< 0) { const a1 = index - (totalElements + 2) const a2 = index - 2 targetIndex.current = a1 >= 0 ? a1 : a2 - targetPos = -(targetIndex.current + 2) * step + // targetPos = -(targetIndex.current + 2) * step + targetPos = -index * step + isCriticalItem = a1 >= 0 + if (isCriticalItem) { + resetPos = -(a1 + 2) * step + } } else { - targetIndex.current = index > 1 ? index - 2 : ( index === 0 ? totalElements - 2 : totalElements - 1) - targetPos = -(targetIndex.current + 2) * step + // 反向滚动 + isCriticalItem = [0, 1].includes(index) + targetIndex.current = isCriticalItem ? index + 2 : index - 2 + // targetIndex.current = index > 1 ? index - 2 : ( index === 0 ? totalElements - 2 : totalElements - 1) + // targetPos = -(targetIndex.current + 2) * step + targetPos = -index * step + if (isCriticalItem) { + resetPos = -(index + totalElements) * step + } } } return { + isCriticalItem, + resetOffset: { + x: dir === 'x' ? resetPos : 0, + y: dir === 'y' ? resetPos : 0 + }, targetOffset: { x: dir === 'x' ? targetPos : offset.value.x, y: dir === 'y' ? targetPos : offset.value.y @@ -353,20 +406,28 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } } - function canMove (e) { - if (!props.circular && (e[strTrans] < 0 && e[dir] >= (totalElements - 1) * step || e[strTrans] > 0 && e[dir] <= step)) { - return false + function canMove (e: GestureUpdateEvent) { + // 移动的距离 + const transDistance = e[strTrans as StrTransType] + if (props.circular) { + return true; + } else if (transDistance < 0) { + // 正向滚动e[strTrans] < 0 + var moveTarget = e[dir] + Math.abs(transDistance); + var posEnd = (totalElements - 1) * step; + return moveTarget <= posEnd; + } else if (transDistance > 0) { + // 反向滚动 e[dir] < step 代表第一个元素不能再滚动, e[dir] > step + return e[dir] > step && e[dir] - transDistance >=0 } else { - return true + return true; } } const animatedStyles = useAnimatedStyle(() => { if (dir === 'x') { - console.log(1111) return { transform: [{ translateX: offset.value.x }]} } else { - console.log(2222, offset.value.y) return { transform: [{ translateY: offset.value.y }]} } }) @@ -388,35 +449,42 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p if (!canMove(e)) { return } - const { targetOffset } = getTargetPosition(e) - console.log('------------onEnd1', e, targetOffset) - console.log('------------onEnd2', offset.value.x) - /* - withTiming(offset.value, { - easing: Easing.bounce, - }); - */ - offset.value = targetOffset - start.value = targetOffset + const { isCriticalItem, targetOffset, resetOffset } = getTargetPosition(e) + if (isCriticalItem) { + // 执行动画到下一帧 + offset.value = withTiming(targetOffset, { + duration: easeDuration + }) + // 动画执行完成将开始位置设置为真正的位置 + setTimeout(() => { + offset.value = resetOffset + start.value = resetOffset + }, easeDuration) + } else { + offset.value = withTiming(targetOffset, { + duration: easeDuration, + easing: easeMap[easeingFunc] + }) + start.value = targetOffset + } const eventData = getCustomEvent('change', {}, { detail: { current: targetIndex.current, source: 'touch' }, layoutRef: layoutRef }) props.bindchange && props.bindchange(eventData) props.autoplay && !Number.isNaN(+step) && createAutoPlay() }) .onFinalize(() => { }); - - return ( + + return ( - {arrPages} - {/*wrapChildren({ + {wrapChildren({ children: arrPages }, { hasVarDec, varContext: varContextRef.current, textStyle, textProps - })*/} + })} {showsPagination && renderPagination()} From ab9c8bfcfc59a25902fc533807b907e2af4820a9 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Sun, 10 Nov 2024 23:20:17 +0800 Subject: [PATCH 05/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dworklet=20&=20JS=20?= =?UTF-8?q?=E9=80=9A=E4=BF=A1=E9=97=AE=E9=A2=98=20&=20=E8=BE=B9=E7=95=8C?= =?UTF-8?q?=E5=9C=BA=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 352 ++++++++++-------- 1 file changed, 199 insertions(+), 153 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index c74ee1e09..f276786a5 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -1,6 +1,6 @@ import { View, NativeSyntheticEvent, Dimensions, LayoutChangeEvent } from 'react-native' import { GestureDetector, Gesture, GestureUpdateEvent, PanGestureHandlerEventPayload } from 'react-native-gesture-handler'; -import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing } from 'react-native-reanimated'; +import Animated, { useAnimatedStyle, useSharedValue, withTiming, withDelay, Easing, runOnJS, runOnUI, useAnimatedReaction } from 'react-native-reanimated'; import { JSX, forwardRef, useRef, useEffect, useState, ReactNode } from 'react' import useInnerProps, { getCustomEvent } from './getInnerListeners' @@ -24,6 +24,7 @@ import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } fr */ type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic' type StrTransType = 'translationX' | 'translationY' +type dirType = 'x' | 'y' interface SwiperProps { children?: ReactNode; circular?: boolean; @@ -54,14 +55,6 @@ interface SwiperProps { interface CarouseState { width: number; height: number; - index: number; - total: number; - // 设置scrollView初始的滚动坐标,contentOffset - offset: { - x: number, - y: number - }; - dir: 'x' | 'y'; } /** @@ -149,12 +142,43 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const { textStyle, innerStyle } = splitStyle(normalStyle) const { textProps } = splitProps(props) const children = Array.isArray(props.children) ? props.children.filter(child => child) : (props.children ? [props.children] : []) - const totalElements = children.length const defaultHeight = (normalStyle?.height || 150) const defaultWidth = (normalStyle?.width || width || 375) - const dir = horizontal === false ? 'y' : 'x' - // 内部存储上一次的offset值 - const autoplayTimerRef = useRef | null>(null) + const initWidth = typeof defaultWidth === 'number' ? defaultWidth - previousMargin - nextMargin : defaultWidth + const initHeight = typeof defaultHeight === 'number' ? defaultHeight - previousMargin - nextMargin : defaultHeight + + const [widthState, setWidthState] = useState(initWidth) + const [heightState, setHeightState] = useState(initHeight) + const dir = useSharedValue(horizontal === false ? 'y' : 'x') + const pstep = dir.value === 'x' ? widthState : heightState + const initStep = Number.isNaN(pstep) ? 0 : pstep + const step = useSharedValue(initStep) + + function getInitOffset () { + if (Number.isNaN(+step.value)) return { x: 0, y: 0 } + const targetOffset = { x: 0, y: 0 } + if(props.circular && totalElements.value > 1) { + const targetIndex = (props.current || 0) + 2 + targetOffset[dir.value as dirType] = -step.value * targetIndex + } else if (props.current && props.current > 0){ + targetOffset[dir.value as dirType] = -props.current * step.value + } + + return targetOffset + } + + const totalElements = useSharedValue(children.length) + const targetIndex = useSharedValue(0) + const initOffset = getInitOffset() + const offset = useSharedValue(initOffset) + const start = useSharedValue(initOffset); + const strTrans = 'translation' + dir.value.toUpperCase() + const isAutoFirst = useRef(true) + const arrPages: Array | ReactNode = renderItems() + // 定时器替代setInterval + const timestamp = useSharedValue(0); + // 是否可以开始轮播 + const canLoop = useSharedValue(true) const { // 存储layout布局信息 @@ -163,10 +187,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout }) - const [state, setState] = useState({ - width: dir === 'x' && typeof defaultWidth === 'number' ? defaultWidth - previousMargin - nextMargin : defaultWidth, - height: dir === 'y' && typeof defaultHeight === 'number' ? defaultHeight - previousMargin - nextMargin : defaultHeight - } as CarouseState) const innerProps = useInnerProps(props, { ref: nodeRef, @@ -189,36 +209,31 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p function onWrapperLayout (e: LayoutChangeEvent) { nodeRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => { layoutRef.current = { x, y, width, height, offsetLeft, offsetTop } - const isWDiff = state.width !== width - const isHDiff = state.height !== height + const isWDiff = initWidth !== width + const isHDiff = initHeight !== height if (isWDiff || isHDiff) { const changeState = { - width: isWDiff ? width : state.width, - height: isHDiff ? height : state.height + width: isWDiff ? width : widthState, + height: isHDiff ? height : heightState } - const attr = dir === 'x' ? 'width' : 'height' + const attr = dir.value === 'x' ? 'width' : 'height' changeState[attr] = changeState[attr] - previousMargin - nextMargin - state.width = changeState.width - state.height = changeState.height - // 这里setState之后,会再触发重新渲染, renderScrollView会再次触发onScrollEnd, - setState((preState) => { - return { - ...preState, - width: changeState.width, - height: changeState.height - } - }) + if (dir.value === 'x') { + setWidthState(changeState[attr]) + } else { + setHeightState(changeState[attr]) + } } }) } function renderPagination () { - if (state.total <= 1) return null + if (totalElements.value <= 1) return null const dots: Array = [] const activeDotStyle = Object.assign({ backgroundColor: activeDotColor || '#007aff' }, dotCommonStyle) const dotStyle = Object.assign({ backgroundColor: dotColor || 'rgba(0,0,0,.2)' }, dotCommonStyle) - for (let i = 0; i < state.total; i++) { - if (i === state.index) { + for (let i = 0; i < totalElements.value; i++) { + if (i === targetIndex.value) { dots.push() } else { dots.push() @@ -226,29 +241,27 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } // 这里也可以用动画实现 return ( - + {dots} ) } function renderItems () { - const { width, height } = state - const pageStyle = { width: width, height: height } + // const { width, height } = state + const pageStyle = { width: widthState, height: heightState } // pages = ["0", "1", "2", "0", "1"] const renderChild = children.slice() - if (!Number.isNaN(+step)) { - if (props.circular && totalElements > 1) { - if (totalElements === 2) { - renderChild.concat(children).concat(children) - } else { - // 最前加两个 - renderChild.unshift(children[totalElements - 1]) - renderChild.unshift(children[totalElements - 2]) - // 最后加两个 - renderChild.push(children[0]) - renderChild.push(children[1]) - } + if (props.circular && totalElements.value > 1) { + if (totalElements.value === 2) { + renderChild.concat(children).concat(children) + } else { + // 最前加两个 + renderChild.unshift(children[totalElements.value - 1]) + renderChild.unshift(children[totalElements.value - 2]) + // 最后加两个 + renderChild.push(children[0]) + renderChild.push(children[1]) } } // 1. 不支持循环 + margin 模式 @@ -256,185 +269,216 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const extraStyle = {} as { [key: string]: any } - if (i === 0 && dir === 'x' && typeof width === 'number') { + if (i === 0 && dir.value === 'x' && typeof width === 'number') { previousMargin && (extraStyle.marginLeft = previousMargin) - } else if (i === totalElements - 1 && typeof width === 'number') { + } else if (i === totalElements.value - 1 && typeof width === 'number') { nextMargin && (extraStyle.marginRight = nextMargin) } return ({child}) }) } - - const targetIndex = useRef(0) - const initOffset = getInitOffset() - const offset = useSharedValue(initOffset) - const start = useSharedValue(initOffset); - const step = dir === 'x' ? state.width : state.height - const strTrans = 'translation' + dir.toUpperCase() - const isAutoFirst = useRef(true) - const arrPages: Array | ReactNode = renderItems() + function handleEvent () { + 'worklet'; + console.log('-----------handleEvent', targetIndex.value); + /* + runOnJS(() => { + console.log('-------------22222', 1) + // const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef }) + // props.bindchange && props.bindchange(eventData) + // props.autoplay && createAutoPlay() + })() + */ + } function createAutoPlay () { - autoplayTimerRef.current && clearInterval(autoplayTimerRef.current) - autoplayTimerRef.current = setInterval(() => { - const targetOffset = { x: 0, y: 0 } - if (!props.circular) { - // 获取下一个位置的坐标, 循环到最后一个元素,直接停止 - if (targetIndex.current === totalElements - 1) { - autoplayTimerRef.current && clearTimeout(autoplayTimerRef.current) - autoplayTimerRef.current = null - return - } - targetIndex.current = targetIndex.current + 1 - targetOffset[dir] = -targetIndex.current * step - - offset.value = withTiming(targetOffset, { - duration: easeDuration, - easing: easeMap[easeingFunc] - }) + 'worklet'; + const targetOffset = { x: 0, y: 0 } + let nextIndex = targetIndex.value + if (!props.circular) { + // 获取下一个位置的坐标, 循环到最后一个元素,直接停止 + if (targetIndex.value === totalElements.value - 1) { + return + } + nextIndex += 1 + targetOffset[dir.value as dirType] = -nextIndex * step.value + offset.value = withTiming(targetOffset, { + duration: easeDuration, + easing: easeMap[easeingFunc] + }, () => { start.value = targetOffset - } else { - if (targetIndex.current === totalElements - 1) { - targetIndex.current = 0 - targetOffset[dir] = -(totalElements + 2) * step - // 执行动画到下一帧 - offset.value = withTiming(targetOffset, { - duration: easeDuration - }) + targetIndex.value = nextIndex + handleEvent() + }) + } else { + if (nextIndex === totalElements.value - 1) { + nextIndex = 0 + targetOffset[dir.value as dirType] = -(totalElements.value + 2) * step.value + // 执行动画到下一帧 + offset.value = withTiming(targetOffset, { + duration: easeDuration + }, () => { const initOffset = { x: 0, y: 0 } - initOffset[dir] = -step * 2 + initOffset[dir.value as dirType] = -step.value * 2 // 将开始位置设置为真正的位置 - setTimeout(() => { - offset.value = initOffset - start.value = initOffset - }, easeDuration) - } else { - targetIndex.current = targetIndex.current + 1 - targetOffset[dir] = -(targetIndex.current + 2) * step - // 执行动画到下一帧 - offset.value = withTiming(targetOffset, { - duration: easeDuration, - easing: easeMap[easeingFunc] - }) + offset.value = initOffset + start.value = initOffset + targetIndex.value = nextIndex + handleEvent() + }) + } else { + nextIndex = targetIndex.value + 1 + targetOffset[dir.value as dirType] = -(nextIndex + 2) * step.value + // 执行动画到下一帧 + offset.value = withTiming(targetOffset, { + duration: easeDuration, + easing: easeMap[easeingFunc] + }, () => { start.value = targetOffset - } - console.log('---------createAutoPlay----2-', targetIndex, targetOffset) + targetIndex.value = nextIndex + handleEvent() + }) } - }, props.interval || 500) + } } + useAnimatedReaction(() => timestamp.value, (newTime, preTime) => { + const canPlay = props.autoplay && newTime > 0 && newTime !== preTime && canLoop.value && totalElements.value > 1 + if (canPlay) { + createAutoPlay() + } + }) + useEffect(() => { - if (props.autoplay && !Number.isNaN(+step)) { + const stepValue = dir.value === 'x' ? widthState : heightState + if (!Number.isNaN(+stepValue)) { + step.value = stepValue + } + if (props.autoplay && !Number.isNaN(+step.value)) { if (isAutoFirst.current) { isAutoFirst.current = false const targetOffset = getInitOffset() offset.value = targetOffset start.value = targetOffset } - createAutoPlay() } else { const targetOffset = getInitOffset() offset.value = targetOffset start.value = targetOffset } - }, [props.autoplay, props.current, state.width, state.height]) + }, [props.autoplay, props.current, widthState, heightState]) - function getInitOffset () { - if (Number.isNaN(+step)) return { x: 0, y: 0 } - const targetOffset = { x: 0, y: 0 } - if(props.circular) { - const targetIndex = (props.current || 0) + 2 - targetOffset[dir] = -step * targetIndex - } else if (props.current){ - targetOffset[dir] = -props.current * step + useEffect(() => { + if (props.autoplay && totalElements.value > 1) { + const intervalTimer = props.interval || 500 + const intervalId = setInterval(() => { + timestamp.value = Date.now(); + }, intervalTimer); + return () => clearInterval(intervalId); } - return targetOffset - } + }, []); function getTargetPosition (e: GestureUpdateEvent) { + 'worklet'; let targetPos = 0 let resetPos = 0 - const posView = e[dir] + const posView = e[dir.value as dirType] // 移动的距离 const transDistance = e[strTrans as StrTransType] // 移动的目标步长 - const moveDistance = Math.ceil(Math.abs(transDistance) / step) * step + const moveDistance = Math.ceil(Math.abs(transDistance) / step.value) * step.value // 移动的目标步长之后的坐标, e[strTrans] < 0) 代表正向滚动 否则反向 const moveTargetPos = transDistance < 0 ? posView + moveDistance : posView - moveDistance // 目标索引值 - const index = Math.floor(moveTargetPos / step) + let index = Math.floor(moveTargetPos / step.value) + let realTarget = targetIndex.value // 是否临界点 let isCriticalItem = false if (!props.circular) { - targetIndex.current = index - targetPos = -targetIndex.current * step + // targetIndex.value = index + // targetPos = -targetIndex.value * step.value + realTarget = index + targetPos = -realTarget * step.value } else { // 正向滚动 if (transDistance< 0) { - const a1 = index - (totalElements + 2) + const a1 = index - (totalElements.value + 2) const a2 = index - 2 - targetIndex.current = a1 >= 0 ? a1 : a2 - // targetPos = -(targetIndex.current + 2) * step - targetPos = -index * step + // targetIndex.value = a1 >= 0 ? a1 : a2 + realTarget = a1 >= 0 ? a1 : a2 + targetPos = -index * step.value isCriticalItem = a1 >= 0 if (isCriticalItem) { - resetPos = -(a1 + 2) * step + resetPos = -(a1 + 2) * step.value } } else { // 反向滚动 isCriticalItem = [0, 1].includes(index) - targetIndex.current = isCriticalItem ? index + 2 : index - 2 - // targetIndex.current = index > 1 ? index - 2 : ( index === 0 ? totalElements - 2 : totalElements - 1) - // targetPos = -(targetIndex.current + 2) * step - targetPos = -index * step + // targetIndex.value = isCriticalItem ? index + 2 : index - 2 + // realTarget = isCriticalItem ? index + 2 : index - 2 + realTarget = isCriticalItem ? index + 1 : index - 2 + targetPos = -index * step.value if (isCriticalItem) { - resetPos = -(index + totalElements) * step + resetPos = -(index + totalElements.value) * step.value } } } return { + realTarget, isCriticalItem, resetOffset: { - x: dir === 'x' ? resetPos : 0, - y: dir === 'y' ? resetPos : 0 + x: dir.value === 'x' ? resetPos : 0, + y: dir.value === 'y' ? resetPos : 0 }, targetOffset: { - x: dir === 'x' ? targetPos : offset.value.x, - y: dir === 'y' ? targetPos : offset.value.y + x: dir.value === 'x' ? targetPos : offset.value.x, + y: dir.value === 'y' ? targetPos : offset.value.y } } } function canMove (e: GestureUpdateEvent) { + 'worklet'; // 移动的距离 const transDistance = e[strTrans as StrTransType] - if (props.circular) { + if (totalElements.value <= 1) { + return false + } else if (props.circular) { return true; } else if (transDistance < 0) { // 正向滚动e[strTrans] < 0 - var moveTarget = e[dir] + Math.abs(transDistance); - var posEnd = (totalElements - 1) * step; + var moveTarget = e[dir.value as dirType] + Math.abs(transDistance); + var posEnd = (totalElements.value - 1) * step.value; return moveTarget <= posEnd; } else if (transDistance > 0) { - // 反向滚动 e[dir] < step 代表第一个元素不能再滚动, e[dir] > step - return e[dir] > step && e[dir] - transDistance >=0 + // 反向滚动 e[dir.value] < step.value 代表第一个元素不能再滚动, e[dir.value] > step.value + return e[dir.value as dirType] > step.value && e[dir.value as dirType] - transDistance > 0 && targetIndex.value >= 1 } else { return true; } } + function resetAutoTime () { + 'worklet'; + if (props.autoplay && canLoop.value === false) { + // 再经过interval后执行动画 + timestamp.value = 0 + canLoop.value = true + } + } + const animatedStyles = useAnimatedStyle(() => { - if (dir === 'x') { + if (dir.value === 'x') { return { transform: [{ translateX: offset.value.x }]} } else { return { transform: [{ translateY: offset.value.y }]} } }) + const gesture = Gesture.Pan() - .onBegin((e) => { - autoplayTimerRef.current && clearTimeout(autoplayTimerRef.current) + .onBegin(() => { + canLoop.value = false }) .onUpdate((e) => { if (!canMove(e)) { @@ -449,34 +493,36 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p if (!canMove(e)) { return } - const { isCriticalItem, targetOffset, resetOffset } = getTargetPosition(e) + const { isCriticalItem, targetOffset, resetOffset, realTarget } = getTargetPosition(e) if (isCriticalItem) { // 执行动画到下一帧 offset.value = withTiming(targetOffset, { duration: easeDuration - }) - // 动画执行完成将开始位置设置为真正的位置 - setTimeout(() => { + }, () => { + // 动画执行完成后归位真正的offset offset.value = resetOffset start.value = resetOffset - }, easeDuration) + targetIndex.value = realTarget + resetAutoTime() + handleEvent() + }) } else { offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] + }, () => { + targetIndex.value = realTarget + start.value = targetOffset + resetAutoTime() + handleEvent() }) - start.value = targetOffset } - const eventData = getCustomEvent('change', {}, { detail: { current: targetIndex.current, source: 'touch' }, layoutRef: layoutRef }) - props.bindchange && props.bindchange(eventData) - props.autoplay && !Number.isNaN(+step) && createAutoPlay() }) .onFinalize(() => { }); - return ( - + {wrapChildren({ children: arrPages }, { From e1cb370e7c6a784d87b4e026a6303da5f0d6e028 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Mon, 11 Nov 2024 13:08:56 +0800 Subject: [PATCH 06/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=BE=B9=E7=95=8C?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index f276786a5..0cfcb7a12 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -155,13 +155,14 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const step = useSharedValue(initStep) function getInitOffset () { - if (Number.isNaN(+step.value)) return { x: 0, y: 0 } + const stepValue = dir.value === 'x' ? widthState : heightState + if (Number.isNaN(+stepValue)) return { x: 0, y: 0 } const targetOffset = { x: 0, y: 0 } if(props.circular && totalElements.value > 1) { const targetIndex = (props.current || 0) + 2 - targetOffset[dir.value as dirType] = -step.value * targetIndex + targetOffset[dir.value as dirType] = -stepValue * targetIndex } else if (props.current && props.current > 0){ - targetOffset[dir.value as dirType] = -props.current * step.value + targetOffset[dir.value as dirType] = -props.current * stepValue } return targetOffset @@ -227,6 +228,12 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }) } + const animatedPagerStyles = useAnimatedStyle(() => { + return { + + } + }) + function renderPagination () { if (totalElements.value <= 1) return null const dots: Array = [] @@ -234,17 +241,24 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const dotStyle = Object.assign({ backgroundColor: dotColor || 'rgba(0,0,0,.2)' }, dotCommonStyle) for (let i = 0; i < totalElements.value; i++) { if (i === targetIndex.value) { - dots.push() + dots.push() } else { - dots.push() + dots.push() } } // 这里也可以用动画实现 + return ( + + {dots} + + ) + /* return ( {dots} ) + */ } function renderItems () { @@ -281,14 +295,12 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p function handleEvent () { 'worklet'; console.log('-----------handleEvent', targetIndex.value); - /* - runOnJS(() => { + runOnJS((current: number) => { console.log('-------------22222', 1) - // const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef }) - // props.bindchange && props.bindchange(eventData) - // props.autoplay && createAutoPlay() - })() - */ + const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef }) + props.bindchange && props.bindchange(eventData) + props.autoplay && createAutoPlay() + })(targetIndex.value) } function createAutoPlay () { @@ -350,11 +362,12 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }) useEffect(() => { + // 这里stepValue 有时候拿不到 const stepValue = dir.value === 'x' ? widthState : heightState if (!Number.isNaN(+stepValue)) { step.value = stepValue } - if (props.autoplay && !Number.isNaN(+step.value)) { + if (props.autoplay && !Number.isNaN(+stepValue)) { if (isAutoFirst.current) { isAutoFirst.current = false const targetOffset = getInitOffset() @@ -414,9 +427,11 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } else { // 反向滚动 isCriticalItem = [0, 1].includes(index) - // targetIndex.value = isCriticalItem ? index + 2 : index - 2 - // realTarget = isCriticalItem ? index + 2 : index - 2 - realTarget = isCriticalItem ? index + 1 : index - 2 + if (isCriticalItem) { + realTarget = index === 0 ? totalElements.value - 2 : totalElements.value - 1 + } else { + realTarget = index - 2 + } targetPos = -index * step.value if (isCriticalItem) { resetPos = -(index + totalElements.value) * step.value @@ -513,6 +528,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }, () => { targetIndex.value = realTarget start.value = targetOffset + targetIndex.value = realTarget resetAutoTime() handleEvent() }) From f3c9706aad6891af42b56d85715885bfcd9197c0 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Mon, 11 Nov 2024 17:01:03 +0800 Subject: [PATCH 07/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8DrunOnJs=20=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index 0cfcb7a12..fb40d9f8f 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -292,17 +292,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }) } - function handleEvent () { - 'worklet'; - console.log('-----------handleEvent', targetIndex.value); - runOnJS((current: number) => { - console.log('-------------22222', 1) - const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef }) - props.bindchange && props.bindchange(eventData) - props.autoplay && createAutoPlay() - })(targetIndex.value) - } - function createAutoPlay () { 'worklet'; const targetOffset = { x: 0, y: 0 } @@ -320,7 +309,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }, () => { start.value = targetOffset targetIndex.value = nextIndex - handleEvent() }) } else { if (nextIndex === totalElements.value - 1) { @@ -336,7 +324,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p offset.value = initOffset start.value = initOffset targetIndex.value = nextIndex - handleEvent() }) } else { nextIndex = targetIndex.value + 1 @@ -348,12 +335,17 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }, () => { start.value = targetOffset targetIndex.value = nextIndex - handleEvent() }) } } } + function handleSwiperChange (current: number) { + const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef }) + props.bindchange && props.bindchange(eventData) + props.autoplay && createAutoPlay() + } + useAnimatedReaction(() => timestamp.value, (newTime, preTime) => { const canPlay = props.autoplay && newTime > 0 && newTime !== preTime && canLoop.value && totalElements.value > 1 if (canPlay) { @@ -361,6 +353,11 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } }) + useAnimatedReaction(() => targetIndex.value, (newIndex, preIndex) => { + // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息 + runOnJS(handleSwiperChange)(newIndex) + }) + useEffect(() => { // 这里stepValue 有时候拿不到 const stepValue = dir.value === 'x' ? widthState : heightState @@ -493,9 +490,11 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const gesture = Gesture.Pan() .onBegin(() => { + 'worklet' canLoop.value = false }) .onUpdate((e) => { + 'worklet' if (!canMove(e)) { return } @@ -505,6 +504,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }; }) .onEnd((e) => { + 'worklet' if (!canMove(e)) { return } @@ -519,7 +519,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p start.value = resetOffset targetIndex.value = realTarget resetAutoTime() - handleEvent() }) } else { offset.value = withTiming(targetOffset, { @@ -530,12 +529,9 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p start.value = targetOffset targetIndex.value = realTarget resetAutoTime() - handleEvent() }) } }) - .onFinalize(() => { - }); return ( From d5f2083f4592c5a5a96265f8514efee786fd1ce9 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Tue, 12 Nov 2024 16:13:51 +0800 Subject: [PATCH 08/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20margin=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index fb40d9f8f..2c4ee7eb9 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -139,7 +139,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p parentWidth, parentHeight }) - const { textStyle, innerStyle } = splitStyle(normalStyle) + const { textStyle } = splitStyle(normalStyle) const { textProps } = splitProps(props) const children = Array.isArray(props.children) ? props.children.filter(child => child) : (props.children ? [props.children] : []) const defaultHeight = (normalStyle?.height || 150) @@ -155,7 +155,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const step = useSharedValue(initStep) function getInitOffset () { - const stepValue = dir.value === 'x' ? widthState : heightState + const stepValue = getStepValue() if (Number.isNaN(+stepValue)) return { x: 0, y: 0 } const targetOffset = { x: 0, y: 0 } if(props.circular && totalElements.value > 1) { @@ -262,13 +262,12 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } function renderItems () { - // const { width, height } = state const pageStyle = { width: widthState, height: heightState } // pages = ["0", "1", "2", "0", "1"] - const renderChild = children.slice() + let renderChild = children.slice() if (props.circular && totalElements.value > 1) { if (totalElements.value === 2) { - renderChild.concat(children).concat(children) + renderChild = renderChild.concat(children).concat(children) } else { // 最前加两个 renderChild.unshift(children[totalElements.value - 1]) @@ -288,8 +287,12 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } else if (i === totalElements.value - 1 && typeof width === 'number') { nextMargin && (extraStyle.marginRight = nextMargin) } - return ({child}) + return ( + {child} + ) }) + + } function createAutoPlay () { @@ -343,7 +346,10 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p function handleSwiperChange (current: number) { const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef }) props.bindchange && props.bindchange(eventData) - props.autoplay && createAutoPlay() + } + + function getStepValue () { + return dir.value === 'x' ? widthState : heightState } useAnimatedReaction(() => timestamp.value, (newTime, preTime) => { @@ -360,7 +366,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p useEffect(() => { // 这里stepValue 有时候拿不到 - const stepValue = dir.value === 'x' ? widthState : heightState + const stepValue = getStepValue() if (!Number.isNaN(+stepValue)) { step.value = stepValue } @@ -375,6 +381,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const targetOffset = getInitOffset() offset.value = targetOffset start.value = targetOffset + props.current && (targetIndex.value = props.current) } }, [props.autoplay, props.current, widthState, heightState]) @@ -398,15 +405,13 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p // 移动的目标步长 const moveDistance = Math.ceil(Math.abs(transDistance) / step.value) * step.value // 移动的目标步长之后的坐标, e[strTrans] < 0) 代表正向滚动 否则反向 - const moveTargetPos = transDistance < 0 ? posView + moveDistance : posView - moveDistance + const moveTargetPos = transDistance < 0 ? posView + moveDistance - previousMargin : posView - moveDistance - previousMargin // 目标索引值 let index = Math.floor(moveTargetPos / step.value) let realTarget = targetIndex.value // 是否临界点 let isCriticalItem = false if (!props.circular) { - // targetIndex.value = index - // targetPos = -targetIndex.value * step.value realTarget = index targetPos = -realTarget * step.value } else { @@ -460,7 +465,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } else if (transDistance < 0) { // 正向滚动e[strTrans] < 0 var moveTarget = e[dir.value as dirType] + Math.abs(transDistance); - var posEnd = (totalElements.value - 1) * step.value; + var posEnd = (totalElements.value - 1) * step.value + previousMargin; return moveTarget <= posEnd; } else if (transDistance > 0) { // 反向滚动 e[dir.value] < step.value 代表第一个元素不能再滚动, e[dir.value] > step.value From 8102f164a21d7bef461c0895208e474f5f867b2e Mon Sep 17 00:00:00 2001 From: luyongfang Date: Wed, 13 Nov 2024 00:05:15 +0800 Subject: [PATCH 09/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BF=AB=E9=80=9F?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8+=E8=BD=AE=E6=92=AD=E9=97=AE=E9=A2=98=20&=20?= =?UTF-8?q?=E6=8E=92=E6=9F=A5y=E6=BB=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index 2c4ee7eb9..b28270883 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -176,10 +176,13 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const strTrans = 'translation' + dir.value.toUpperCase() const isAutoFirst = useRef(true) const arrPages: Array | ReactNode = renderItems() - // 定时器替代setInterval + // timestamp标识何时能滚动, 定时器替代setInterval const timestamp = useSharedValue(0); - // 是否可以开始轮播 + // canLoop 标识是否能滚动 const canLoop = useSharedValue(true) + // 标记可以开始loop的时间戳 + let enableTime = useSharedValue(Date.now()) + // 备注如果用runOnJs的setInterval的方式, 不晓得为什么props.autoplay的配置还会触发变化理论是个死值; const { // 存储layout布局信息 @@ -389,7 +392,12 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p if (props.autoplay && totalElements.value > 1) { const intervalTimer = props.interval || 500 const intervalId = setInterval(() => { - timestamp.value = Date.now(); + // 正常无人工干预轮播 + if (timestamp.value !== 0 && canLoop.value) { + timestamp.value = Date.now(); + } else if (timestamp.value === 0 && Date.now() - enableTime.value > intervalTimer) { + timestamp.value = Date.now(); + } }, intervalTimer); return () => clearInterval(intervalId); } @@ -411,6 +419,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p let realTarget = targetIndex.value // 是否临界点 let isCriticalItem = false + console.log('----------getTarget', posView, transDistance, moveDistance, moveTargetPos, index) if (!props.circular) { realTarget = index targetPos = -realTarget * step.value @@ -426,6 +435,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p if (isCriticalItem) { resetPos = -(a1 + 2) * step.value } + console.log('---------zheng-', a1, a2, realTarget, resetPos, isCriticalItem) } else { // 反向滚动 isCriticalItem = [0, 1].includes(index) @@ -438,6 +448,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p if (isCriticalItem) { resetPos = -(index + totalElements.value) * step.value } + console.log('----------fan', isCriticalItem, realTarget, targetPos, resetPos) } } return { @@ -481,6 +492,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p // 再经过interval后执行动画 timestamp.value = 0 canLoop.value = true + enableTime.value = Date.now() } } From 94c43d19d088b9d9bcfa9ad91d56826971d826ce Mon Sep 17 00:00:00 2001 From: luyongfang Date: Fri, 15 Nov 2024 19:03:52 +0800 Subject: [PATCH 10/26] =?UTF-8?q?swiper=20=E9=87=8D=E6=9E=84=E6=8B=96?= =?UTF-8?q?=E6=8B=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 328 +++++++++++------- 1 file changed, 202 insertions(+), 126 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index b28270883..d7387d473 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -180,9 +180,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const timestamp = useSharedValue(0); // canLoop 标识是否能滚动 const canLoop = useSharedValue(true) - // 标记可以开始loop的时间戳 - let enableTime = useSharedValue(Date.now()) - // 备注如果用runOnJs的setInterval的方式, 不晓得为什么props.autoplay的配置还会触发变化理论是个死值; + let intervalId:ReturnType const { // 存储layout布局信息 @@ -255,13 +253,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p {dots} ) - /* - return ( - - {dots} - - ) - */ } function renderItems () { @@ -355,16 +346,41 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p return dir.value === 'x' ? widthState : heightState } - useAnimatedReaction(() => timestamp.value, (newTime, preTime) => { - const canPlay = props.autoplay && newTime > 0 && newTime !== preTime && canLoop.value && totalElements.value > 1 - if (canPlay) { - createAutoPlay() + + function createIntervalHandler () { + if (props.autoplay && totalElements.value > 1 && !intervalId) { + const intervalTimer = props.interval || 500 + intervalId = setInterval(() => { + createAutoPlay() + }, intervalTimer); + isProcessingInterval = false } - }) + } + + function cancelIntervalHandler () { + clearInterval(intervalId) + // @ts-ignore + intervalId = 0 + } useAnimatedReaction(() => targetIndex.value, (newIndex, preIndex) => { // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息 - runOnJS(handleSwiperChange)(newIndex) + const isInit = !preIndex && newIndex === 0 + if (!isInit && props.bindchange) { + runOnJS(handleSwiperChange)(newIndex) + } + }) + + useAnimatedReaction(() => canLoop.value, (loopv, oldLoop) => { + // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息 + console.log('----------useAnimatedReaction-----', loopv, oldLoop, intervalId) + if (props.autoplay) { + if (!loopv) { + runOnJS(cancelIntervalHandler)() + } else if (!intervalId && oldLoop !== null){ + runOnJS(createIntervalHandler)() + } + } }) useEffect(() => { @@ -388,111 +404,95 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } }, [props.autoplay, props.current, widthState, heightState]) - useEffect(() => { - if (props.autoplay && totalElements.value > 1) { - const intervalTimer = props.interval || 500 - const intervalId = setInterval(() => { - // 正常无人工干预轮播 - if (timestamp.value !== 0 && canLoop.value) { - timestamp.value = Date.now(); - } else if (timestamp.value === 0 && Date.now() - enableTime.value > intervalTimer) { - timestamp.value = Date.now(); - } - }, intervalTimer); - return () => clearInterval(intervalId); - } - }, []); function getTargetPosition (e: GestureUpdateEvent) { 'worklet'; - let targetPos = 0 - let resetPos = 0 - const posView = e[dir.value as dirType] - // 移动的距离 - const transDistance = e[strTrans as StrTransType] - // 移动的目标步长 - const moveDistance = Math.ceil(Math.abs(transDistance) / step.value) * step.value - // 移动的目标步长之后的坐标, e[strTrans] < 0) 代表正向滚动 否则反向 - const moveTargetPos = transDistance < 0 ? posView + moveDistance - previousMargin : posView - moveDistance - previousMargin - // 目标索引值 - let index = Math.floor(moveTargetPos / step.value) - let realTarget = targetIndex.value + let resetOffsetPos = 0 + let moveToIndex = 0 + let selectedIndex = targetIndex.value // 是否临界点 let isCriticalItem = false - console.log('----------getTarget', posView, transDistance, moveDistance, moveTargetPos, index) + // 真实滚动到的偏移量坐标 + let moveToTargetPos = 0 + // 移动的距离 + const transDistance = e[strTrans as StrTransType] + // 移动的步长 + const moveStep = Math.ceil(Math.abs(transDistance) / step.value) + // 目标索引, 向下滚动>0, 索引变小, 向上滚<0 索引变大; 向左滚>0 索引变大, 向右滚<0, 索引变大 + let index = transDistance < 0 ? targetIndex.value + moveStep : targetIndex.value - moveStep + // 用户一直拖拽, 拖拽出区域松手后的位置, 若未超出区域, 按正常逻辑走, 若超出区域, 单独计算矫正 + let isCorrect = false + // 实际应该定位的索引值 if (!props.circular) { - realTarget = index - targetPos = -realTarget * step.value + selectedIndex = index + moveToTargetPos = selectedIndex * step.value } else { - // 正向滚动 - if (transDistance< 0) { - const a1 = index - (totalElements.value + 2) - const a2 = index - 2 - // targetIndex.value = a1 >= 0 ? a1 : a2 - realTarget = a1 >= 0 ? a1 : a2 - targetPos = -index * step.value - isCriticalItem = a1 >= 0 + // Y轴向下滚动, transDistance > 0, 索引变小 + // X轴向左滚动, transDistance > 0, 索引变小 + if (transDistance > 0) { + // 滚动到的位置索引 + moveToIndex = targetIndex.value + 2 - moveStep + moveToTargetPos = moveToIndex * step.value - previousMargin + isCriticalItem = [0, 1].includes(moveToIndex) if (isCriticalItem) { - resetPos = -(a1 + 2) * step.value + selectedIndex = totalElements.value - 2 + moveToIndex + resetOffsetPos = (totalElements.value + moveToIndex ) * step.value + } else { + selectedIndex = targetIndex.value - moveStep } - console.log('---------zheng-', a1, a2, realTarget, resetPos, isCriticalItem) } else { - // 反向滚动 - isCriticalItem = [0, 1].includes(index) - if (isCriticalItem) { - realTarget = index === 0 ? totalElements.value - 2 : totalElements.value - 1 - } else { - realTarget = index - 2 + // Y轴向上滚动, transDistance < 0, 索引变大 + // X轴向右滚动, transDistance < 0, 索引变大 + moveToIndex = targetIndex.value + moveStep + 2 + if (moveToIndex > totalElements.value + 4 - 1) { + moveToIndex = totalElements.value + 4 - 1 } - targetPos = -index * step.value + moveToTargetPos = moveToIndex * step.value - previousMargin + isCriticalItem = [totalElements.value + 2, totalElements.value + 3].includes(moveToIndex) if (isCriticalItem) { - resetPos = -(index + totalElements.value) * step.value + selectedIndex = moveToIndex - ( totalElements.value + 2 ) + resetOffsetPos = (selectedIndex + 2) * step.value + } else { + selectedIndex = targetIndex.value + moveStep } - console.log('----------fan', isCriticalItem, realTarget, targetPos, resetPos) + } + // 若计算出来的坐标是有问题的 + console.log('===============================0', selectedIndex) + if (selectedIndex < 0 || selectedIndex > totalElements.value -1) { + const scrollNum = Math.ceil(Math.abs(transDistance) / step.value) + selectedIndex = (targetIndex.value + scrollNum) % totalElements.value + moveToTargetPos = (selectedIndex + 2) * step.value + resetOffsetPos = moveToTargetPos + isCorrect = true + console.log('==============================1', selectedIndex, moveToTargetPos, resetOffsetPos) } } return { - realTarget, + isCorrect, + selectedIndex, isCriticalItem, resetOffset: { - x: dir.value === 'x' ? resetPos : 0, - y: dir.value === 'y' ? resetPos : 0 + x: dir.value === 'x' ? -resetOffsetPos : 0, + y: dir.value === 'y' ? -resetOffsetPos : 0 }, targetOffset: { - x: dir.value === 'x' ? targetPos : offset.value.x, - y: dir.value === 'y' ? targetPos : offset.value.y + x: dir.value === 'x' ? -moveToTargetPos : offset.value.x, + y: dir.value === 'y' ? -moveToTargetPos : offset.value.y } } } function canMove (e: GestureUpdateEvent) { 'worklet'; - // 移动的距离 - const transDistance = e[strTrans as StrTransType] - if (totalElements.value <= 1) { - return false - } else if (props.circular) { - return true; - } else if (transDistance < 0) { - // 正向滚动e[strTrans] < 0 - var moveTarget = e[dir.value as dirType] + Math.abs(transDistance); - var posEnd = (totalElements.value - 1) * step.value + previousMargin; - return moveTarget <= posEnd; - } else if (transDistance > 0) { - // 反向滚动 e[dir.value] < step.value 代表第一个元素不能再滚动, e[dir.value] > step.value - return e[dir.value as dirType] > step.value && e[dir.value as dirType] - transDistance > 0 && targetIndex.value >= 1 + if (!props.circular) { + const transDistance = e[strTrans as StrTransType] + if (transDistance < 0) { + return targetIndex.value < totalElements.value - 1 + } else { + return targetIndex.value > 0 + } } else { - return true; - } - } - - function resetAutoTime () { - 'worklet'; - if (props.autoplay && canLoop.value === false) { - // 再经过interval后执行动画 - timestamp.value = 0 - canLoop.value = true - enableTime.value = Date.now() + return true } } @@ -504,66 +504,142 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } }) + function reachBoundary (e: GestureUpdateEvent) { + 'worklet'; + // 移动的距离 + const transDistance = e[strTrans as StrTransType] + let isBoundary = false + let resetOffset = 0 + let moveToTargetPos = 0 + let selectedIndex = 0 + // Y轴向下滚动, transDistance > 0, 索引变小 + // X轴向左滚动, transDistance > 0, 索引变小 + const currentOffset = offset.value[dir.value] + const itemLength = step.value * totalElements.value + + if (transDistance > 0) { + // 用户一直拖拽 translateXY一直变大, offset的值也一直变大, 这个时候应该loop + const moveStep = Math.floor(transDistance / itemLength) + 2 + + if (currentOffset < 0 && step.value > Math.abs(currentOffset)) { + isBoundary = true + moveToTargetPos = (totalElements.value - 1 + 2 ) * step.value + resetOffset = itemLength * moveStep + selectedIndex = totalElements.value - 1 + } + } else if (transDistance < 0) { + // const moveStep = Math.ceil(Math.abs(transDistance) / itemLength) - 2 + const endOffset = (totalElements.value + 3) * step.value + console.log('-----------------------aaaaaaa', endOffset, currentOffset) + // -----------------aaaaaaa 80 -32 + if (currentOffset < 0 && currentOffset < -endOffset) { + isBoundary = true + selectedIndex = 0 + resetOffset = step.value + moveToTargetPos = 2 * step.value + } + } + return { + isBoundary, + moveToTargetPos: { + x: dir.value === 'x' ? -moveToTargetPos : 0, + y: dir.value === 'y' ? -moveToTargetPos : 0 + }, + resetOffset: { + x: dir.value === 'x' ? -resetOffset : 0, + y: dir.value === 'y' ? -resetOffset : 0 + }, + selectedIndex + } + } + + const updateProcessing = useSharedValue(false) const gesture = Gesture.Pan() - .onBegin(() => { + .onBegin((e) => { 'worklet' canLoop.value = false }) .onUpdate((e) => { 'worklet' - if (!canMove(e)) { + if (!props.circular && !canMove(e)) { return } - offset.value = { - x: e.translationX + start.value.x, - y: e.translationY + start.value.y, - }; + // 处理用户拖拽到临界点的场景 + const { isBoundary, resetOffset, moveToTargetPos, selectedIndex } = reachBoundary(e) + if (isBoundary) { + start.value = resetOffset + offset.value = moveToTargetPos + // targetIndex.value = selectedIndex todo 不更新 + updateProcessing.value = true + console.log('--------------------------onUpdate-0-', start.value[dir.value], offset.value[dir.value], targetIndex.value) + } else { + offset.value = { + x: e.translationX + start.value.x, + y: e.translationY + start.value.y, + }; + updateProcessing.value = false + } }) .onEnd((e) => { 'worklet' - if (!canMove(e)) { + if (props.circular && !canMove(e)) { + return + } + const { isCriticalItem, targetOffset, resetOffset, selectedIndex, isCorrect } = getTargetPosition(e) + if (isCorrect) { + targetIndex.value = selectedIndex + start.value = resetOffset + offset.value = targetOffset return } - const { isCriticalItem, targetOffset, resetOffset, realTarget } = getTargetPosition(e) if (isCriticalItem) { - // 执行动画到下一帧 + console.log('---------------------------onEnd-0---', targetOffset, resetOffset, selectedIndex) offset.value = withTiming(targetOffset, { - duration: easeDuration + duration: easeDuration, + easing: easeMap[easeingFunc] }, () => { - // 动画执行完成后归位真正的offset - offset.value = resetOffset + targetIndex.value = selectedIndex start.value = resetOffset - targetIndex.value = realTarget - resetAutoTime() + offset.value = resetOffset + canLoop.value = true }) } else { + console.log('---------------------------onEnd-1---', targetOffset, resetOffset, selectedIndex) offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] }, () => { - targetIndex.value = realTarget + targetIndex.value = selectedIndex start.value = targetOffset - targetIndex.value = realTarget - resetAutoTime() + offset.value = targetOffset + canLoop.value = true }) } }) - return ( - - - {wrapChildren({ - children: arrPages - }, { - hasVarDec, - varContext: varContextRef.current, - textStyle, - textProps - })} - - - {showsPagination && renderPagination()} - ) + + function renderSwiper () { + return ( + + {wrapChildren({ + children: arrPages + }, { + hasVarDec, + varContext: varContextRef.current, + textStyle, + textProps + })} + + {showsPagination && renderPagination()} + ) + } + if (totalElements.value === 1) { + return renderSwiper() + } else { + return ( + {renderSwiper()} + ) + } }) _SwiperWrapper.displayName = 'mpx-swiper' From 3fc91c1e4cf3f34b1f4c9c8e8443283bf263b2ef Mon Sep 17 00:00:00 2001 From: luyongfang Date: Mon, 18 Nov 2024 10:44:26 +0800 Subject: [PATCH 11/26] swiper loop --- .../runtime/components/react/mpx-swiper.tsx | 262 ++++++++---------- 1 file changed, 119 insertions(+), 143 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index d7387d473..1cc6bbaf8 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -1,11 +1,12 @@ import { View, NativeSyntheticEvent, Dimensions, LayoutChangeEvent } from 'react-native' import { GestureDetector, Gesture, GestureUpdateEvent, PanGestureHandlerEventPayload } from 'react-native-gesture-handler'; -import Animated, { useAnimatedStyle, useSharedValue, withTiming, withDelay, Easing, runOnJS, runOnUI, useAnimatedReaction } from 'react-native-reanimated'; +import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, interpolateColor } from 'react-native-reanimated'; -import { JSX, forwardRef, useRef, useEffect, useState, ReactNode } from 'react' +import { JSX, forwardRef, useRef, useEffect, useState, ReactNode, useCallback, useMemo } from 'react' import useInnerProps, { getCustomEvent } from './getInnerListeners' import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数 import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from './utils' +import { createFaces } from './swiperFaces' /** * ✔ indicator-dots * ✔ indicator-color @@ -17,13 +18,15 @@ import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } fr * ✔ circular * ✔ vertical * ✘ display-multiple-items - * ✘ previous-margin - * ✘ next-margin + * ✔ previous-margin + * ✔ next-margin * ✔ easing-function ="easeOutCubic" * ✘ snap-to-edge */ type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic' type StrTransType = 'translationX' | 'translationY' +type StrAbsoType = 'absoluteX' | 'absoluteY' + type dirType = 'x' | 'y' interface SwiperProps { children?: ReactNode; @@ -52,11 +55,6 @@ interface SwiperProps { bindchange?: (event: NativeSyntheticEvent | unknown) => void; } -interface CarouseState { - width: number; - height: number; -} - /** * 默认的Style类型 */ @@ -92,6 +90,10 @@ const dotCommonStyle = { marginTop: 3, marginBottom: 3 } +// 默认前后补位的元素个数 +const patchElementNum = 2 +// preMargin 和 nextMargin +const visibleCount = 3 const easeMap = { 'default': Easing.ease, @@ -146,7 +148,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const defaultWidth = (normalStyle?.width || width || 375) const initWidth = typeof defaultWidth === 'number' ? defaultWidth - previousMargin - nextMargin : defaultWidth const initHeight = typeof defaultHeight === 'number' ? defaultHeight - previousMargin - nextMargin : defaultHeight - const [widthState, setWidthState] = useState(initWidth) const [heightState, setHeightState] = useState(initHeight) const dir = useSharedValue(horizontal === false ? 'y' : 'x') @@ -154,6 +155,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const initStep = Number.isNaN(pstep) ? 0 : pstep const step = useSharedValue(initStep) + function getInitOffset () { const stepValue = getStepValue() if (Number.isNaN(+stepValue)) return { x: 0, y: 0 } @@ -173,15 +175,15 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const initOffset = getInitOffset() const offset = useSharedValue(initOffset) const start = useSharedValue(initOffset); - const strTrans = 'translation' + dir.value.toUpperCase() + const strTrans = 'translation' + dir.value.toUpperCase() as StrTransType + const strAbso = 'absolute' + dir.value.toUpperCase() as StrAbsoType const isAutoFirst = useRef(true) const arrPages: Array | ReactNode = renderItems() - // timestamp标识何时能滚动, 定时器替代setInterval - const timestamp = useSharedValue(0); // canLoop 标识是否能滚动 - const canLoop = useSharedValue(true) + const canLoop = useSharedValue(!!props.autoplay) + // 记录用户点击时绝对定位坐标 + const preRelativePos = useSharedValue(0) let intervalId:ReturnType - const { // 存储layout布局信息 layoutRef, @@ -229,25 +231,25 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }) } - const animatedPagerStyles = useAnimatedStyle(() => { - return { - - } - }) - function renderPagination () { if (totalElements.value <= 1) return null const dots: Array = [] - const activeDotStyle = Object.assign({ backgroundColor: activeDotColor || '#007aff' }, dotCommonStyle) - const dotStyle = Object.assign({ backgroundColor: dotColor || 'rgba(0,0,0,.2)' }, dotCommonStyle) for (let i = 0; i < totalElements.value; i++) { - if (i === targetIndex.value) { - dots.push() - } else { - dots.push() - } + const dotStyle = useAnimatedStyle(() => { + const activeColor = activeDotColor || '#007aff' + const unActionColor = dotColor || 'rgba(0,0,0,.2)' + return { + backgroundColor: i === targetIndex.value ? activeColor : unActionColor + } + }) + dots.push( + ) } - // 这里也可以用动画实现 return ( {dots} @@ -272,18 +274,24 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } } // 1. 不支持循环 + margin 模式 - return renderChild.map((child, i) => { + return renderChild.map((child, index) => { + // 可能会有变大变小 + // const { scaleXY } = getTransform(index, targetIndex.value) const extraStyle = {} as { [key: string]: any } - if (i === 0 && dir.value === 'x' && typeof width === 'number') { + if (index === 0 && dir.value === 'x' && typeof width === 'number') { previousMargin && (extraStyle.marginLeft = previousMargin) - } else if (i === totalElements.value - 1 && typeof width === 'number') { + } else if (index === totalElements.value - 1 && typeof width === 'number') { nextMargin && (extraStyle.marginRight = nextMargin) } - return ( + return ( {child} - ) + ) }) @@ -294,8 +302,9 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const targetOffset = { x: 0, y: 0 } let nextIndex = targetIndex.value if (!props.circular) { - // 获取下一个位置的坐标, 循环到最后一个元素,直接停止 + // 获取下一个位置的坐标, 循环到最后一个元素,直接停止, 取消定时器 if (targetIndex.value === totalElements.value - 1) { + canLoop.value = false return } nextIndex += 1 @@ -308,6 +317,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p targetIndex.value = nextIndex }) } else { + // 默认向右, 向下 if (nextIndex === totalElements.value - 1) { nextIndex = 0 targetOffset[dir.value as dirType] = -(totalElements.value + 2) * step.value @@ -348,17 +358,21 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p function createIntervalHandler () { - if (props.autoplay && totalElements.value > 1 && !intervalId) { - const intervalTimer = props.interval || 500 + const intervalTimer = props.interval || 500 + if (props.autoplay && totalElements.value > 1 && !intervalId && canLoop.value) { intervalId = setInterval(() => { - createAutoPlay() + // canLoop变化比较快的情况下, 会触发重复执行 + if (canLoop.value) { + createAutoPlay() + } else { + cancelIntervalHandler() + } }, intervalTimer); - isProcessingInterval = false } } function cancelIntervalHandler () { - clearInterval(intervalId) + intervalId && clearInterval(intervalId) // @ts-ignore intervalId = 0 } @@ -369,19 +383,18 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p if (!isInit && props.bindchange) { runOnJS(handleSwiperChange)(newIndex) } - }) + }, [targetIndex.value]) useAnimatedReaction(() => canLoop.value, (loopv, oldLoop) => { // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息 - console.log('----------useAnimatedReaction-----', loopv, oldLoop, intervalId) if (props.autoplay) { if (!loopv) { runOnJS(cancelIntervalHandler)() - } else if (!intervalId && oldLoop !== null){ + } else if (!intervalId && oldLoop !== true){ runOnJS(createIntervalHandler)() } } - }) + }, [canLoop.value]) useEffect(() => { // 这里stepValue 有时候拿不到 @@ -398,9 +411,20 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } } else { const targetOffset = getInitOffset() - offset.value = targetOffset - start.value = targetOffset - props.current && (targetIndex.value = props.current) + if (props.current !== undefined && (props.current !== targetIndex.value || props.current === 0 && targetIndex.value > 0)) { + targetIndex.value = props.current + offset.value = withTiming(targetOffset, { + duration: easeDuration, + easing: easeMap[easeingFunc] + }, () => { + offset.value = targetOffset + start.value = targetOffset + }) + } else { + offset.value = targetOffset + start.value = targetOffset + props.current && (targetIndex.value = props.current) + } } }, [props.autoplay, props.current, widthState, heightState]) @@ -408,67 +432,39 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p function getTargetPosition (e: GestureUpdateEvent) { 'worklet'; let resetOffsetPos = 0 - let moveToIndex = 0 let selectedIndex = targetIndex.value // 是否临界点 let isCriticalItem = false // 真实滚动到的偏移量坐标 let moveToTargetPos = 0 // 移动的距离 - const transDistance = e[strTrans as StrTransType] - // 移动的步长 - const moveStep = Math.ceil(Math.abs(transDistance) / step.value) - // 目标索引, 向下滚动>0, 索引变小, 向上滚<0 索引变大; 向左滚>0 索引变大, 向右滚<0, 索引变大 - let index = transDistance < 0 ? targetIndex.value + moveStep : targetIndex.value - moveStep - // 用户一直拖拽, 拖拽出区域松手后的位置, 若未超出区域, 按正常逻辑走, 若超出区域, 单独计算矫正 - let isCorrect = false + const transDistance = e[strTrans] + // 当前的位置 + const currentOffset = offset.value[dir.value as dirType] + const currentIndex = Math.abs(currentOffset) / step.value + let moveToIndex = transDistance < 0 ? Math.ceil(currentIndex) : Math.floor(currentIndex) // 实际应该定位的索引值 if (!props.circular) { - selectedIndex = index + selectedIndex = moveToIndex moveToTargetPos = selectedIndex * step.value } else { - // Y轴向下滚动, transDistance > 0, 索引变小 - // X轴向左滚动, transDistance > 0, 索引变小 - if (transDistance > 0) { - // 滚动到的位置索引 - moveToIndex = targetIndex.value + 2 - moveStep - moveToTargetPos = moveToIndex * step.value - previousMargin - isCriticalItem = [0, 1].includes(moveToIndex) - if (isCriticalItem) { - selectedIndex = totalElements.value - 2 + moveToIndex - resetOffsetPos = (totalElements.value + moveToIndex ) * step.value - } else { - selectedIndex = targetIndex.value - moveStep - } + if (moveToIndex >= totalElements.value + patchElementNum) { + selectedIndex = moveToIndex - (totalElements.value + patchElementNum) + resetOffsetPos = (selectedIndex + patchElementNum) * step.value + moveToTargetPos = moveToIndex * step.value + isCriticalItem = true + } else if (moveToIndex <= patchElementNum - 1) { + selectedIndex = moveToIndex === 0 ? totalElements.value - 2 : totalElements.value - 1 + resetOffsetPos = (selectedIndex + patchElementNum) * step.value + moveToTargetPos = moveToIndex * step.value + isCriticalItem = true } else { - // Y轴向上滚动, transDistance < 0, 索引变大 - // X轴向右滚动, transDistance < 0, 索引变大 - moveToIndex = targetIndex.value + moveStep + 2 - if (moveToIndex > totalElements.value + 4 - 1) { - moveToIndex = totalElements.value + 4 - 1 - } - moveToTargetPos = moveToIndex * step.value - previousMargin - isCriticalItem = [totalElements.value + 2, totalElements.value + 3].includes(moveToIndex) - if (isCriticalItem) { - selectedIndex = moveToIndex - ( totalElements.value + 2 ) - resetOffsetPos = (selectedIndex + 2) * step.value - } else { - selectedIndex = targetIndex.value + moveStep - } - } - // 若计算出来的坐标是有问题的 - console.log('===============================0', selectedIndex) - if (selectedIndex < 0 || selectedIndex > totalElements.value -1) { - const scrollNum = Math.ceil(Math.abs(transDistance) / step.value) - selectedIndex = (targetIndex.value + scrollNum) % totalElements.value - moveToTargetPos = (selectedIndex + 2) * step.value - resetOffsetPos = moveToTargetPos - isCorrect = true - console.log('==============================1', selectedIndex, moveToTargetPos, resetOffsetPos) + selectedIndex = moveToIndex - 2 + moveToTargetPos = moveToIndex * step.value } + } return { - isCorrect, selectedIndex, isCriticalItem, resetOffset: { @@ -485,7 +481,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p function canMove (e: GestureUpdateEvent) { 'worklet'; if (!props.circular) { - const transDistance = e[strTrans as StrTransType] + const transDistance = e[strTrans] if (transDistance < 0) { return targetIndex.value < totalElements.value - 1 } else { @@ -507,36 +503,28 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p function reachBoundary (e: GestureUpdateEvent) { 'worklet'; // 移动的距离 - const transDistance = e[strTrans as StrTransType] + const transDistance = e[strTrans] + const elementsLength = step.value * totalElements.value let isBoundary = false let resetOffset = 0 let moveToTargetPos = 0 - let selectedIndex = 0 - // Y轴向下滚动, transDistance > 0, 索引变小 - // X轴向左滚动, transDistance > 0, 索引变小 - const currentOffset = offset.value[dir.value] - const itemLength = step.value * totalElements.value - - if (transDistance > 0) { - // 用户一直拖拽 translateXY一直变大, offset的值也一直变大, 这个时候应该loop - const moveStep = Math.floor(transDistance / itemLength) + 2 - - if (currentOffset < 0 && step.value > Math.abs(currentOffset)) { + // Y轴向下滚动, transDistance > 0 + // X轴向左滚动, transDistance > 0 + const currentOffset = offset.value[dir.value as dirType] + const moveStep = Math.ceil((e[strAbso] - preRelativePos.value) / elementsLength) + if (transDistance < 0) { + const posEnd = (totalElements.value + patchElementNum + 1) * step.value + if (currentOffset < -posEnd) { isBoundary = true - moveToTargetPos = (totalElements.value - 1 + 2 ) * step.value - resetOffset = itemLength * moveStep - selectedIndex = totalElements.value - 1 + moveToTargetPos = (totalElements.value + patchElementNum) * step.value + resetOffset = moveStep * elementsLength } - } else if (transDistance < 0) { - // const moveStep = Math.ceil(Math.abs(transDistance) / itemLength) - 2 - const endOffset = (totalElements.value + 3) * step.value - console.log('-----------------------aaaaaaa', endOffset, currentOffset) - // -----------------aaaaaaa 80 -32 - if (currentOffset < 0 && currentOffset < -endOffset) { + } else if (transDistance > 0) { + const posEnd = (patchElementNum - 1) * step.value + if (currentOffset > -posEnd) { isBoundary = true - selectedIndex = 0 - resetOffset = step.value - moveToTargetPos = 2 * step.value + resetOffset = moveStep * elementsLength + step.value + moveToTargetPos = (patchElementNum - 1) * step.value } } return { @@ -548,16 +536,14 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p resetOffset: { x: dir.value === 'x' ? -resetOffset : 0, y: dir.value === 'y' ? -resetOffset : 0 - }, - selectedIndex + } } } - const updateProcessing = useSharedValue(false) - - const gesture = Gesture.Pan() + const gestureHandler = Gesture.Pan() .onBegin((e) => { 'worklet' + preRelativePos.value = e[strAbso] canLoop.value = false }) .onUpdate((e) => { @@ -566,19 +552,16 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p return } // 处理用户拖拽到临界点的场景 - const { isBoundary, resetOffset, moveToTargetPos, selectedIndex } = reachBoundary(e) - if (isBoundary) { + const { isBoundary, resetOffset, moveToTargetPos } = reachBoundary(e) + if (isBoundary && props.circular) { start.value = resetOffset offset.value = moveToTargetPos - // targetIndex.value = selectedIndex todo 不更新 - updateProcessing.value = true - console.log('--------------------------onUpdate-0-', start.value[dir.value], offset.value[dir.value], targetIndex.value) } else { + const moveDistance = e[strAbso] - preRelativePos.value offset.value = { - x: e.translationX + start.value.x, - y: e.translationY + start.value.y, + x: moveDistance + start.value.x, + y: moveDistance + start.value.y, }; - updateProcessing.value = false } }) .onEnd((e) => { @@ -586,15 +569,8 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p if (props.circular && !canMove(e)) { return } - const { isCriticalItem, targetOffset, resetOffset, selectedIndex, isCorrect } = getTargetPosition(e) - if (isCorrect) { - targetIndex.value = selectedIndex - start.value = resetOffset - offset.value = targetOffset - return - } + const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(e) if (isCriticalItem) { - console.log('---------------------------onEnd-0---', targetOffset, resetOffset, selectedIndex) offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] @@ -605,7 +581,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p canLoop.value = true }) } else { - console.log('---------------------------onEnd-1---', targetOffset, resetOffset, selectedIndex) offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] @@ -619,7 +594,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }) function renderSwiper () { - return ( + return ( {wrapChildren({ children: arrPages @@ -633,10 +608,11 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p {showsPagination && renderPagination()} ) } + if (totalElements.value === 1) { return renderSwiper() } else { - return ( + return ( {renderSwiper()} ) } From be7b1a938112eeb2eab41475d0c37dbcbb609fc8 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Mon, 18 Nov 2024 11:31:40 +0800 Subject: [PATCH 12/26] fix bug --- .../webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index 1cc6bbaf8..201e29679 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -566,7 +566,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p }) .onEnd((e) => { 'worklet' - if (props.circular && !canMove(e)) { + if (!props.circular && !canMove(e)) { return } const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(e) From ec9cdd190c4c9b8fbf7265df0b25eb8d997e00f1 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Thu, 21 Nov 2024 10:38:14 +0800 Subject: [PATCH 13/26] =?UTF-8?q?swiper=20=E4=BF=AE=E5=A4=8D=E8=BF=9E?= =?UTF-8?q?=E7=BB=AD=E6=BB=91=E5=8A=A8&=E6=BB=91=E5=8A=A8=E4=B8=AD?= =?UTF-8?q?=E8=A7=A6=E6=91=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 230 +++++++++++++----- 1 file changed, 170 insertions(+), 60 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index 201e29679..faa9a86c5 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -1,12 +1,11 @@ import { View, NativeSyntheticEvent, Dimensions, LayoutChangeEvent } from 'react-native' import { GestureDetector, Gesture, GestureUpdateEvent, PanGestureHandlerEventPayload } from 'react-native-gesture-handler'; -import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, interpolateColor } from 'react-native-reanimated'; +import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation, interpolate } from 'react-native-reanimated'; import { JSX, forwardRef, useRef, useEffect, useState, ReactNode, useCallback, useMemo } from 'react' import useInnerProps, { getCustomEvent } from './getInnerListeners' import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数 import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from './utils' -import { createFaces } from './swiperFaces' /** * ✔ indicator-dots * ✔ indicator-color @@ -17,15 +16,16 @@ import { createFaces } from './swiperFaces' * ✔ duration * ✔ circular * ✔ vertical - * ✘ display-multiple-items * ✔ previous-margin * ✔ next-margin * ✔ easing-function ="easeOutCubic" + * ✘ display-multiple-items * ✘ snap-to-edge */ type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic' type StrTransType = 'translationX' | 'translationY' type StrAbsoType = 'absoluteX' | 'absoluteY' +type StrVelocity = 'velocityX' | 'velocityY' type dirType = 'x' | 'y' interface SwiperProps { @@ -94,9 +94,12 @@ const dotCommonStyle = { const patchElementNum = 2 // preMargin 和 nextMargin const visibleCount = 3 +const longPressRatio = 2 +const quickMoveTime = 200 +const quickMoveDuration = 200 const easeMap = { - 'default': Easing.ease, + 'default': Easing.linear, 'linear': Easing.linear, 'easeInCubic': Easing.in(Easing.cubic), 'easeOutCubic': Easing.out(Easing.cubic), @@ -177,13 +180,20 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const start = useSharedValue(initOffset); const strTrans = 'translation' + dir.value.toUpperCase() as StrTransType const strAbso = 'absolute' + dir.value.toUpperCase() as StrAbsoType + const strVelocity = 'velocity' + dir.value.toUpperCase() as StrVelocity const isAutoFirst = useRef(true) const arrPages: Array | ReactNode = renderItems() // canLoop 标识是否能滚动 const canLoop = useSharedValue(!!props.autoplay) + // 标识手指触摸和抬起 + const touchfinish = useSharedValue(false) // 记录用户点击时绝对定位坐标 const preRelativePos = useSharedValue(0) - let intervalId:ReturnType + let a = useSharedValue(0) + let intervalId = useSharedValue(0) + let timerId = useSharedValue(0) + const gestureTime = useSharedValue(0) + const customTrans = useSharedValue(0) const { // 存储layout布局信息 layoutRef, @@ -257,6 +267,19 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p ) } + const faces = [ + { index: -1, targetIndex: 0, scaleXY: 0.8}, + { index: 0, targetIndex: 1, scaleXY: 1.2 }, + { index: 1, targetIndex: 2, scaleXY: 0.8} + ] + + const getTransform = useCallback((index: number) => { + const inputRange = faces.map((f) => (index + f.index)) + return { + scaleXY: interpolate(targetIndex.value, inputRange, faces.map((x) => x.scaleXY)) + } + }, [targetIndex]) + function renderItems () { const pageStyle = { width: widthState, height: heightState } // pages = ["0", "1", "2", "0", "1"] @@ -276,7 +299,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p // 1. 不支持循环 + margin 模式 return renderChild.map((child, index) => { // 可能会有变大变小 - // const { scaleXY } = getTransform(index, targetIndex.value) + // const { scaleXY } = getTransform(index) const extraStyle = {} as { [key: string]: any } @@ -286,15 +309,12 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p nextMargin && (extraStyle.marginRight = nextMargin) } return ( {child} ) }) - - } function createAutoPlay () { @@ -356,27 +376,26 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p return dir.value === 'x' ? widthState : heightState } - - function createIntervalHandler () { - const intervalTimer = props.interval || 500 - if (props.autoplay && totalElements.value > 1 && !intervalId && canLoop.value) { - intervalId = setInterval(() => { - // canLoop变化比较快的情况下, 会触发重复执行 - if (canLoop.value) { - createAutoPlay() - } else { - cancelIntervalHandler() + function handlerInterval (loopv: boolean, oldLoop: boolean | null) { + clearInterval(intervalId.value) + if (canLoop.value && oldLoop !== true) { + const intervalTimer = props.interval || 500 + clearTimeout(timerId.value) + timerId.value = setTimeout(() => { + if (props.autoplay && totalElements.value > 1 && canLoop.value) { + intervalId.value = setInterval(() => { + // canLoop变化比较快的情况下 + if (canLoop.value) { + createAutoPlay() + } else { + clearInterval(intervalId.value) + } + }, intervalTimer); } - }, intervalTimer); + }, intervalTimer) } } - function cancelIntervalHandler () { - intervalId && clearInterval(intervalId) - // @ts-ignore - intervalId = 0 - } - useAnimatedReaction(() => targetIndex.value, (newIndex, preIndex) => { // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息 const isInit = !preIndex && newIndex === 0 @@ -388,11 +407,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p useAnimatedReaction(() => canLoop.value, (loopv, oldLoop) => { // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息 if (props.autoplay) { - if (!loopv) { - runOnJS(cancelIntervalHandler)() - } else if (!intervalId && oldLoop !== true){ - runOnJS(createIntervalHandler)() - } + runOnJS(handlerInterval)(loopv, oldLoop) } }, [canLoop.value]) @@ -500,32 +515,114 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } }) + function handleEnd (e: GestureUpdateEvent) { + 'worklet'; + const subtractTime = Date.now() - gestureTime.value + const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(e) + // 用户快速连续操作小于100ms, 快速切换有些生硬,调整下时间 + const durationTime = subtractTime <= quickMoveTime ? quickMoveDuration : easeDuration + if (isCriticalItem) { + offset.value = withTiming(targetOffset, { + duration: durationTime, + easing: easeMap[easeingFunc] + }, () => { + targetIndex.value = selectedIndex + if (touchfinish.value !== false) { + start.value = resetOffset + offset.value = resetOffset + canLoop.value = true + } + }) + } else { + offset.value = withTiming(targetOffset, { + duration: durationTime, + easing: easeMap[easeingFunc] + }, () => { + targetIndex.value = selectedIndex + if (touchfinish.value !== false) { + start.value = targetOffset + offset.value = targetOffset + canLoop.value = true + } + }) + } + } + + function handleBack () { + 'worklet'; + // 向右trans > 0, 向左trans < 0 + const posScrollRight = -(patchElementNum + targetIndex.value) * step.value + const posScrollLeft = -(patchElementNum + targetIndex.value) * step.value + const pos = customTrans.value < 0 ? posScrollRight : posScrollLeft + const targetOffset = { + x: dir.value === 'x' ? pos : 0, + y: dir.value === 'y' ? pos : 0 + } + offset.value = withTiming(targetOffset, { + duration: easeDuration, + easing: easeMap[easeingFunc] + }, () => { + if (touchfinish.value !== false) { + start.value =targetOffset + offset.value = targetOffset + canLoop.value = true + } + }) + } + + function handleLongPress (e: GestureUpdateEvent) { + 'worklet'; + const currentOffset = Math.abs(offset.value[dir.value as dirType]) + const half = currentOffset % step.value > step.value / 2 + // 向右trans > 0, 向左trans < 0 + const isExceedHalf = customTrans.value < 0 ? half : !half + if (isExceedHalf) { + handleEnd(e) + } else { + handleBack() + } + } + function reachBoundary (e: GestureUpdateEvent) { 'worklet'; // 移动的距离 const transDistance = e[strTrans] + const moveDistance = e[strAbso] - preRelativePos.value const elementsLength = step.value * totalElements.value let isBoundary = false let resetOffset = 0 let moveToTargetPos = 0 - // Y轴向下滚动, transDistance > 0 + // Y轴向下滚动, transDistance > 0, 向上滚动 < 0 // X轴向左滚动, transDistance > 0 const currentOffset = offset.value[dir.value as dirType] - const moveStep = Math.ceil((e[strAbso] - preRelativePos.value) / elementsLength) + const moveStep = Math.ceil(moveDistance / elementsLength) if (transDistance < 0) { const posEnd = (totalElements.value + patchElementNum + 1) * step.value - if (currentOffset < -posEnd) { + const posReverseEnd = (patchElementNum - 1) * step.value + if (currentOffset < -posEnd + step.value && currentOffset + posEnd > -2) { isBoundary = true moveToTargetPos = (totalElements.value + patchElementNum) * step.value - resetOffset = moveStep * elementsLength + resetOffset = Math.abs(moveStep) === 0 ? patchElementNum * step.value + moveDistance : moveStep * elementsLength + } + if (currentOffset > -posReverseEnd) { + isBoundary = true + moveToTargetPos = (patchElementNum + totalElements.value - 1) * step.value + resetOffset = moveStep * elementsLength } } else if (transDistance > 0) { const posEnd = (patchElementNum - 1) * step.value + const posReverseEnd = (patchElementNum + totalElements.value) * step.value if (currentOffset > -posEnd) { isBoundary = true - resetOffset = moveStep * elementsLength + step.value + resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? moveDistance : 0) moveToTargetPos = (patchElementNum - 1) * step.value } + if (currentOffset < -posReverseEnd) { + isBoundary = true + resetOffset = moveStep * elementsLength + patchElementNum * step.value + moveToTargetPos = posReverseEnd + } + } return { isBoundary, @@ -543,21 +640,45 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const gestureHandler = Gesture.Pan() .onBegin((e) => { 'worklet' + touchfinish.value = false + start.value = { + x: dir.value === 'x' ? offset.value.x : 0, + y: dir.value === 'y' ? offset.value.y : 0 + } + cancelAnimation(offset) preRelativePos.value = e[strAbso] canLoop.value = false + gestureTime.value = Date.now() + }) + .onTouchesUp((e) => { + 'worklet' + cancelAnimation(offset) + touchfinish.value = true + const touchEventData = e.changedTouches[0] + // 只点击不会触发onEnd事件 + if (Math.ceil(preRelativePos.value) === Math.ceil(touchEventData[strAbso])) { + const transObj: { [key in StrTransType]: number } = { + translationX: 0, + translationY: 0 + }; + transObj[strTrans] = customTrans.value + handleLongPress(transObj as GestureUpdateEvent) + } }) .onUpdate((e) => { 'worklet' + cancelAnimation(offset) if (!props.circular && !canMove(e)) { return } - // 处理用户拖拽到临界点的场景 + const moveDistance = e[strAbso] - preRelativePos.value + customTrans.value = moveDistance + // 处理用户一直拖拽到临界点的场景, 不会执行onEnd const { isBoundary, resetOffset, moveToTargetPos } = reachBoundary(e) - if (isBoundary && props.circular) { + if (isBoundary && props.circular && !touchfinish.value) { start.value = resetOffset offset.value = moveToTargetPos } else { - const moveDistance = e[strAbso] - preRelativePos.value offset.value = { x: moveDistance + start.value.x, y: moveDistance + start.value.y, @@ -569,27 +690,16 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p if (!props.circular && !canMove(e)) { return } - const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(e) - if (isCriticalItem) { - offset.value = withTiming(targetOffset, { - duration: easeDuration, - easing: easeMap[easeingFunc] - }, () => { - targetIndex.value = selectedIndex - start.value = resetOffset - offset.value = resetOffset - canLoop.value = true - }) + const { isBoundary, resetOffset: reachReset, moveToTargetPos } = reachBoundary(e) + if (props.circular && isBoundary) { + start.value = reachReset + offset.value = moveToTargetPos + return + } + if (Math.abs(e[strVelocity]) < longPressRatio) { + handleLongPress(e) } else { - offset.value = withTiming(targetOffset, { - duration: easeDuration, - easing: easeMap[easeingFunc] - }, () => { - targetIndex.value = selectedIndex - start.value = targetOffset - offset.value = targetOffset - canLoop.value = true - }) + handleEnd(e) } }) From ae983cde8db93a4195212bd0d928df23c063082b Mon Sep 17 00:00:00 2001 From: luyongfang Date: Thu, 21 Nov 2024 14:05:21 +0800 Subject: [PATCH 14/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8Deslint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 53 ++++++------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index faa9a86c5..2f6c47ee0 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -92,9 +92,7 @@ const dotCommonStyle = { } // 默认前后补位的元素个数 const patchElementNum = 2 -// preMargin 和 nextMargin -const visibleCount = 3 -const longPressRatio = 2 +const longPressRatio = 100 const quickMoveTime = 200 const quickMoveDuration = 200 @@ -189,9 +187,8 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p const touchfinish = useSharedValue(false) // 记录用户点击时绝对定位坐标 const preRelativePos = useSharedValue(0) - let a = useSharedValue(0) - let intervalId = useSharedValue(0) - let timerId = useSharedValue(0) + const intervalId = useRef(0 as number | ReturnType) + const timerId = useRef(0 as number | ReturnType); const gestureTime = useSharedValue(0) const customTrans = useSharedValue(0) const { @@ -267,22 +264,8 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p ) } - const faces = [ - { index: -1, targetIndex: 0, scaleXY: 0.8}, - { index: 0, targetIndex: 1, scaleXY: 1.2 }, - { index: 1, targetIndex: 2, scaleXY: 0.8} - ] - - const getTransform = useCallback((index: number) => { - const inputRange = faces.map((f) => (index + f.index)) - return { - scaleXY: interpolate(targetIndex.value, inputRange, faces.map((x) => x.scaleXY)) - } - }, [targetIndex]) - function renderItems () { const pageStyle = { width: widthState, height: heightState } - // pages = ["0", "1", "2", "0", "1"] let renderChild = children.slice() if (props.circular && totalElements.value > 1) { if (totalElements.value === 2) { @@ -296,21 +279,19 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p renderChild.push(children[1]) } } - // 1. 不支持循环 + margin 模式 return renderChild.map((child, index) => { - // 可能会有变大变小 - // const { scaleXY } = getTransform(index) - const extraStyle = {} as { - [key: string]: any - } + const extraStyle = {} as { [key: string]: any } if (index === 0 && dir.value === 'x' && typeof width === 'number') { previousMargin && (extraStyle.marginLeft = previousMargin) } else if (index === totalElements.value - 1 && typeof width === 'number') { nextMargin && (extraStyle.marginRight = nextMargin) } + return ( {child} ) @@ -377,22 +358,22 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p } function handlerInterval (loopv: boolean, oldLoop: boolean | null) { - clearInterval(intervalId.value) + clearInterval(intervalId.current) if (canLoop.value && oldLoop !== true) { const intervalTimer = props.interval || 500 - clearTimeout(timerId.value) - timerId.value = setTimeout(() => { + clearTimeout(timerId.current) + timerId.current = setTimeout(() => { if (props.autoplay && totalElements.value > 1 && canLoop.value) { - intervalId.value = setInterval(() => { + intervalId.current = setInterval(() => { // canLoop变化比较快的情况下 if (canLoop.value) { createAutoPlay() } else { - clearInterval(intervalId.value) + clearInterval(intervalId.current) } - }, intervalTimer); + }, intervalTimer) } - }, intervalTimer) + }, 500) } } @@ -550,7 +531,7 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p function handleBack () { 'worklet'; - // 向右trans > 0, 向左trans < 0 + // 向右trans > 0, 向左trans < 0, 向右滑动的back, 向左滑动的back const posScrollRight = -(patchElementNum + targetIndex.value) * step.value const posScrollLeft = -(patchElementNum + targetIndex.value) * step.value const pos = customTrans.value < 0 ? posScrollRight : posScrollLeft From 8063477c19ff332bfe47ec53c25a706aaf37322b Mon Sep 17 00:00:00 2001 From: luyongfang Date: Thu, 21 Nov 2024 14:23:46 +0800 Subject: [PATCH 15/26] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-picker-view-column.tsx | 6 +++--- .../lib/runtime/components/react/mpx-picker-view.tsx | 6 +++--- .../lib/runtime/components/react/mpx-swiper.tsx | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index c972e0869..4bff033ab 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -23,7 +23,7 @@ interface ColumnProps { const defaultItemHeight = 36 // 每个Column 都有个外层的高度, 内部的元素高度 // 默认的高度 -const _PickerViewColumn = forwardRef, ColumnProps>((props: ColumnProps, ref) => { +const PickerViewColumn = forwardRef, ColumnProps>((props: ColumnProps, ref) => { const { children, selectedIndex, onColumnLayoutChange, onSelectChange, getInnerLayout, style, wrapperStyle, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props // PickerViewColumn const { @@ -153,5 +153,5 @@ const _PickerViewColumn = forwardRef, ) }) -_PickerViewColumn.displayName = 'mpx-picker-view-column' -export default _PickerViewColumn \ No newline at end of file +PickerViewColumn.displayName = 'MpxPickerViewColumn' +export default PickerViewColumn \ No newline at end of file diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index 670a6b0e8..595067379 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -60,7 +60,7 @@ const styles: { [key: string]: Object } = { zIndex: 100 } } -const _PickerView = forwardRef, PickerViewProps>((props: PickerViewProps, ref) => { +const PickerView = forwardRef, PickerViewProps>((props: PickerViewProps, ref) => { const { children, value = [], bindchange, style, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props // indicatorStyle 需要转换为rn的style // 微信设置到pick-view上上设置的normalStyle如border等需要转换成RN的style然后进行透传 @@ -216,6 +216,6 @@ const _PickerView = forwardRef, PickerViewProp ) }) -_PickerView.displayName = 'mpx-picker-view' +PickerView.displayName = 'MpxPickerView' -export default _PickerView \ No newline at end of file +export default PickerView \ No newline at end of file diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index 2f6c47ee0..2f4c8976a 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -104,7 +104,7 @@ const easeMap = { 'easeInOutCubic': Easing.inOut(Easing.cubic) } -const _SwiperWrapper = forwardRef, SwiperProps>((props: SwiperProps, ref): JSX.Element => { +const SwiperWrapper = forwardRef, SwiperProps>((props: SwiperProps, ref): JSX.Element => { const { 'indicator-dots': showsPagination, 'indicator-color': dotColor = 'rgba(0, 0, 0, .3)', @@ -708,6 +708,6 @@ const _SwiperWrapper = forwardRef, SwiperProps>((p ) } }) -_SwiperWrapper.displayName = 'mpx-swiper' +SwiperWrapper.displayName = 'MpxSwiperWrapper' -export default _SwiperWrapper +export default SwiperWrapper From 63ca5e37823c60c9585b63c366830bec999e9477 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Thu, 21 Nov 2024 14:28:22 +0800 Subject: [PATCH 16/26] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=8B=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index 2f4c8976a..63f27a0dd 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -488,14 +488,6 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } } - const animatedStyles = useAnimatedStyle(() => { - if (dir.value === 'x') { - return { transform: [{ translateX: offset.value.x }]} - } else { - return { transform: [{ translateY: offset.value.y }]} - } - }) - function handleEnd (e: GestureUpdateEvent) { 'worklet'; const subtractTime = Date.now() - gestureTime.value @@ -683,7 +675,15 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr handleEnd(e) } }) - + + const animatedStyles = useAnimatedStyle(() => { + if (dir.value === 'x') { + return { transform: [{ translateX: offset.value.x }]} + } else { + return { transform: [{ translateY: offset.value.y }]} + } + }) + function renderSwiper () { return ( From 5d640eed2b66e0faa9ae20ed550c91709fde3793 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Thu, 21 Nov 2024 14:31:04 +0800 Subject: [PATCH 17/26] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=8B=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/runtime/components/react/mpx-swiper.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index 63f27a0dd..e561ee898 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -186,7 +186,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr // 标识手指触摸和抬起 const touchfinish = useSharedValue(false) // 记录用户点击时绝对定位坐标 - const preRelativePos = useSharedValue(0) + const preAbsolutePos = useSharedValue(0) const intervalId = useRef(0 as number | ReturnType) const timerId = useRef(0 as number | ReturnType); const gestureTime = useSharedValue(0) @@ -560,7 +560,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr 'worklet'; // 移动的距离 const transDistance = e[strTrans] - const moveDistance = e[strAbso] - preRelativePos.value + const moveDistance = e[strAbso] - preAbsolutePos.value const elementsLength = step.value * totalElements.value let isBoundary = false let resetOffset = 0 @@ -619,7 +619,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr y: dir.value === 'y' ? offset.value.y : 0 } cancelAnimation(offset) - preRelativePos.value = e[strAbso] + preAbsolutePos.value = e[strAbso] canLoop.value = false gestureTime.value = Date.now() }) @@ -629,7 +629,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr touchfinish.value = true const touchEventData = e.changedTouches[0] // 只点击不会触发onEnd事件 - if (Math.ceil(preRelativePos.value) === Math.ceil(touchEventData[strAbso])) { + if (Math.ceil(preAbsolutePos.value) === Math.ceil(touchEventData[strAbso])) { const transObj: { [key in StrTransType]: number } = { translationX: 0, translationY: 0 @@ -644,7 +644,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr if (!props.circular && !canMove(e)) { return } - const moveDistance = e[strAbso] - preRelativePos.value + const moveDistance = e[strAbso] - preAbsolutePos.value customTrans.value = moveDistance // 处理用户一直拖拽到临界点的场景, 不会执行onEnd const { isBoundary, resetOffset, moveToTargetPos } = reachBoundary(e) From 10566df79760d201d6263d2dab69aaa00ced82c8 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Thu, 21 Nov 2024 15:02:19 +0800 Subject: [PATCH 18/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dlint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../react/mpx-picker-view-column.tsx | 2 +- .../components/react/mpx-picker-view.tsx | 2 +- .../runtime/components/react/mpx-swiper.tsx | 86 +++++++++---------- 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 4bff033ab..04a04ba12 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -154,4 +154,4 @@ const PickerViewColumn = forwardRef, }) PickerViewColumn.displayName = 'MpxPickerViewColumn' -export default PickerViewColumn \ No newline at end of file +export default PickerViewColumn diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index 595067379..f61058ff6 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -218,4 +218,4 @@ const PickerView = forwardRef, PickerViewProps PickerView.displayName = 'MpxPickerView' -export default PickerView \ No newline at end of file +export default PickerView diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index e561ee898..be360d237 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -1,8 +1,8 @@ import { View, NativeSyntheticEvent, Dimensions, LayoutChangeEvent } from 'react-native' -import { GestureDetector, Gesture, GestureUpdateEvent, PanGestureHandlerEventPayload } from 'react-native-gesture-handler'; -import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation, interpolate } from 'react-native-reanimated'; +import { GestureDetector, Gesture, GestureUpdateEvent, PanGestureHandlerEventPayload } from 'react-native-gesture-handler' +import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation, interpolate } from 'react-native-reanimated' -import { JSX, forwardRef, useRef, useEffect, useState, ReactNode, useCallback, useMemo } from 'react' +import { JSX, forwardRef, useRef, useEffect, useState, ReactNode } from 'react' import useInnerProps, { getCustomEvent } from './getInnerListeners' import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数 import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from './utils' @@ -78,6 +78,11 @@ const styles: { [key: string]: Object } = { flex: 1, justifyContent: 'center', alignItems: 'center' + }, + swiper: { + overflow: 'scroll', + display: 'flex', + justifyContent: 'flex-start' } } @@ -97,11 +102,11 @@ const quickMoveTime = 200 const quickMoveDuration = 200 const easeMap = { - 'default': Easing.linear, - 'linear': Easing.linear, - 'easeInCubic': Easing.in(Easing.cubic), - 'easeOutCubic': Easing.out(Easing.cubic), - 'easeInOutCubic': Easing.inOut(Easing.cubic) + default: Easing.linear, + linear: Easing.linear, + easeInCubic: Easing.in(Easing.cubic), + easeOutCubic: Easing.out(Easing.cubic), + easeInOutCubic: Easing.inOut(Easing.cubic) } const SwiperWrapper = forwardRef, SwiperProps>((props: SwiperProps, ref): JSX.Element => { @@ -119,7 +124,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const previousMargin = props['previous-margin'] ? parseInt(props['previous-margin']) : 0 const nextMargin = props['next-margin'] ? parseInt(props['next-margin']) : 0 const easeingFunc = props['easing-function'] || 'default' - const easeDuration = props['duration'] || 500 + const easeDuration = props.duration || 500 const horizontal = props.vertical !== undefined ? !props.vertical : true const nodeRef = useRef(null) @@ -152,22 +157,20 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const [widthState, setWidthState] = useState(initWidth) const [heightState, setHeightState] = useState(initHeight) const dir = useSharedValue(horizontal === false ? 'y' : 'x') - const pstep = dir.value === 'x' ? widthState : heightState + const pstep = dir.value === 'x' ? widthState : heightState const initStep = Number.isNaN(pstep) ? 0 : pstep const step = useSharedValue(initStep) - function getInitOffset () { const stepValue = getStepValue() if (Number.isNaN(+stepValue)) return { x: 0, y: 0 } const targetOffset = { x: 0, y: 0 } - if(props.circular && totalElements.value > 1) { + if (props.circular && totalElements.value > 1) { const targetIndex = (props.current || 0) + 2 targetOffset[dir.value as dirType] = -stepValue * targetIndex - } else if (props.current && props.current > 0){ + } else if (props.current && props.current > 0) { targetOffset[dir.value as dirType] = -props.current * stepValue } - return targetOffset } @@ -175,7 +178,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const targetIndex = useSharedValue(0) const initOffset = getInitOffset() const offset = useSharedValue(initOffset) - const start = useSharedValue(initOffset); + const start = useSharedValue(initOffset) const strTrans = 'translation' + dir.value.toUpperCase() as StrTransType const strAbso = 'absolute' + dir.value.toUpperCase() as StrAbsoType const strVelocity = 'velocity' + dir.value.toUpperCase() as StrVelocity @@ -188,7 +191,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr // 记录用户点击时绝对定位坐标 const preAbsolutePos = useSharedValue(0) const intervalId = useRef(0 as number | ReturnType) - const timerId = useRef(0 as number | ReturnType); + const timerId = useRef(0 as number | ReturnType) const gestureTime = useSharedValue(0) const customTrans = useSharedValue(0) const { @@ -198,9 +201,8 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout }) - const innerProps = useInnerProps(props, { - ref: nodeRef, + ref: nodeRef }, [ 'style', 'indicator-dots', @@ -255,7 +257,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr dotStyle ]} key={i}> - ) + ) } return ( @@ -286,11 +288,10 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } else if (index === totalElements.value - 1 && typeof width === 'number') { nextMargin && (extraStyle.marginRight = nextMargin) } - return ( {child} @@ -299,7 +300,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } function createAutoPlay () { - 'worklet'; + 'worklet' const targetOffset = { x: 0, y: 0 } let nextIndex = targetIndex.value if (!props.circular) { @@ -354,7 +355,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } function getStepValue () { - return dir.value === 'x' ? widthState : heightState + return dir.value === 'x' ? widthState : heightState } function handlerInterval (loopv: boolean, oldLoop: boolean | null) { @@ -407,7 +408,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } } else { const targetOffset = getInitOffset() - if (props.current !== undefined && (props.current !== targetIndex.value || props.current === 0 && targetIndex.value > 0)) { + if (props.current !== undefined && (props.current !== targetIndex.value || (props.current === 0 && targetIndex.value > 0))) { targetIndex.value = props.current offset.value = withTiming(targetOffset, { duration: easeDuration, @@ -424,9 +425,8 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } }, [props.autoplay, props.current, widthState, heightState]) - function getTargetPosition (e: GestureUpdateEvent) { - 'worklet'; + 'worklet' let resetOffsetPos = 0 let selectedIndex = targetIndex.value // 是否临界点 @@ -438,7 +438,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr // 当前的位置 const currentOffset = offset.value[dir.value as dirType] const currentIndex = Math.abs(currentOffset) / step.value - let moveToIndex = transDistance < 0 ? Math.ceil(currentIndex) : Math.floor(currentIndex) + const moveToIndex = transDistance < 0 ? Math.ceil(currentIndex) : Math.floor(currentIndex) // 实际应该定位的索引值 if (!props.circular) { selectedIndex = moveToIndex @@ -458,7 +458,6 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr selectedIndex = moveToIndex - 2 moveToTargetPos = moveToIndex * step.value } - } return { selectedIndex, @@ -475,7 +474,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } function canMove (e: GestureUpdateEvent) { - 'worklet'; + 'worklet' if (!props.circular) { const transDistance = e[strTrans] if (transDistance < 0) { @@ -489,7 +488,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } function handleEnd (e: GestureUpdateEvent) { - 'worklet'; + 'worklet' const subtractTime = Date.now() - gestureTime.value const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(e) // 用户快速连续操作小于100ms, 快速切换有些生硬,调整下时间 @@ -522,7 +521,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } function handleBack () { - 'worklet'; + 'worklet' // 向右trans > 0, 向左trans < 0, 向右滑动的back, 向左滑动的back const posScrollRight = -(patchElementNum + targetIndex.value) * step.value const posScrollLeft = -(patchElementNum + targetIndex.value) * step.value @@ -536,7 +535,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr easing: easeMap[easeingFunc] }, () => { if (touchfinish.value !== false) { - start.value =targetOffset + start.value = targetOffset offset.value = targetOffset canLoop.value = true } @@ -544,7 +543,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } function handleLongPress (e: GestureUpdateEvent) { - 'worklet'; + 'worklet' const currentOffset = Math.abs(offset.value[dir.value as dirType]) const half = currentOffset % step.value > step.value / 2 // 向右trans > 0, 向左trans < 0 @@ -557,7 +556,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } function reachBoundary (e: GestureUpdateEvent) { - 'worklet'; + 'worklet' // 移动的距离 const transDistance = e[strTrans] const moveDistance = e[strAbso] - preAbsolutePos.value @@ -580,7 +579,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr if (currentOffset > -posReverseEnd) { isBoundary = true moveToTargetPos = (patchElementNum + totalElements.value - 1) * step.value - resetOffset = moveStep * elementsLength + resetOffset = moveStep * elementsLength } } else if (transDistance > 0) { const posEnd = (patchElementNum - 1) * step.value @@ -595,7 +594,6 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr resetOffset = moveStep * elementsLength + patchElementNum * step.value moveToTargetPos = posReverseEnd } - } return { isBoundary, @@ -633,7 +631,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const transObj: { [key in StrTransType]: number } = { translationX: 0, translationY: 0 - }; + } transObj[strTrans] = customTrans.value handleLongPress(transObj as GestureUpdateEvent) } @@ -650,12 +648,12 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const { isBoundary, resetOffset, moveToTargetPos } = reachBoundary(e) if (isBoundary && props.circular && !touchfinish.value) { start.value = resetOffset - offset.value = moveToTargetPos + offset.value = moveToTargetPos } else { offset.value = { x: moveDistance + start.value.x, - y: moveDistance + start.value.y, - }; + y: moveDistance + start.value.y + } } }) .onEnd((e) => { @@ -666,7 +664,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const { isBoundary, resetOffset: reachReset, moveToTargetPos } = reachBoundary(e) if (props.circular && isBoundary) { start.value = reachReset - offset.value = moveToTargetPos + offset.value = moveToTargetPos return } if (Math.abs(e[strVelocity]) < longPressRatio) { @@ -678,14 +676,14 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const animatedStyles = useAnimatedStyle(() => { if (dir.value === 'x') { - return { transform: [{ translateX: offset.value.x }]} + return { transform: [{ translateX: offset.value.x }] } } else { - return { transform: [{ translateY: offset.value.y }]} + return { transform: [{ translateY: offset.value.y }] } } }) function renderSwiper () { - return ( + return ( {wrapChildren({ children: arrPages From a3d0ceaa6b2d844c74f35d1bfb7ec28fd354d7eb Mon Sep 17 00:00:00 2001 From: luyongfang Date: Mon, 25 Nov 2024 11:26:58 +0800 Subject: [PATCH 19/26] comment update --- .../runtime/components/react/mpx-swiper.tsx | 385 ++++++++---------- 1 file changed, 173 insertions(+), 212 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index be360d237..497ee870c 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -1,8 +1,8 @@ import { View, NativeSyntheticEvent, Dimensions, LayoutChangeEvent } from 'react-native' -import { GestureDetector, Gesture, GestureUpdateEvent, PanGestureHandlerEventPayload } from 'react-native-gesture-handler' -import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation, interpolate } from 'react-native-reanimated' +import { GestureDetector, Gesture } from 'react-native-gesture-handler' +import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation } from 'react-native-reanimated' -import { JSX, forwardRef, useRef, useEffect, useState, ReactNode } from 'react' +import React, { JSX, forwardRef, useRef, useEffect, useState, ReactNode, ReactElement } from 'react' import useInnerProps, { getCustomEvent } from './getInnerListeners' import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数 import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from './utils' @@ -26,6 +26,9 @@ type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInO type StrTransType = 'translationX' | 'translationY' type StrAbsoType = 'absoluteX' | 'absoluteY' type StrVelocity = 'velocityX' | 'velocityY' +type EventDataType = { + translation: number +} type dirType = 'x' | 'y' interface SwiperProps { @@ -96,10 +99,8 @@ const dotCommonStyle = { marginBottom: 3 } // 默认前后补位的元素个数 -const patchElementNum = 2 +const patchElementNum = 1 const longPressRatio = 100 -const quickMoveTime = 200 -const quickMoveDuration = 200 const easeMap = { default: Easing.linear, @@ -158,42 +159,31 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const [heightState, setHeightState] = useState(initHeight) const dir = useSharedValue(horizontal === false ? 'y' : 'x') const pstep = dir.value === 'x' ? widthState : heightState - const initStep = Number.isNaN(pstep) ? 0 : pstep + const initStep = isNaN(pstep) ? 0 : pstep + // 每个元素的宽度 or 高度 const step = useSharedValue(initStep) - - function getInitOffset () { - const stepValue = getStepValue() - if (Number.isNaN(+stepValue)) return { x: 0, y: 0 } - const targetOffset = { x: 0, y: 0 } - if (props.circular && totalElements.value > 1) { - const targetIndex = (props.current || 0) + 2 - targetOffset[dir.value as dirType] = -stepValue * targetIndex - } else if (props.current && props.current > 0) { - targetOffset[dir.value as dirType] = -props.current * stepValue - } - return targetOffset - } - const totalElements = useSharedValue(children.length) - const targetIndex = useSharedValue(0) - const initOffset = getInitOffset() - const offset = useSharedValue(initOffset) - const start = useSharedValue(initOffset) + // 记录选中元素的索引值 + const targetIndex = useSharedValue(props.current || 0) + // 记录元素的偏移量 + const offset = useSharedValue({ x: 0, y: 0 }) const strTrans = 'translation' + dir.value.toUpperCase() as StrTransType const strAbso = 'absolute' + dir.value.toUpperCase() as StrAbsoType const strVelocity = 'velocity' + dir.value.toUpperCase() as StrVelocity const isAutoFirst = useRef(true) const arrPages: Array | ReactNode = renderItems() - // canLoop 标识是否能滚动 - const canLoop = useSharedValue(!!props.autoplay) - // 标识手指触摸和抬起 + // autoplay的状态下是否被暂停 + const paused = useRef(false) + // 标识手指触摸和抬起, 起点在onBegin const touchfinish = useSharedValue(false) + // 用户是否触发了move事件,起点在onStart, 触发move事件才会执行onEnd, 1. 移动一定会触发onStart, onTouchesMove, onEnd 2. 点击未进行操作, 会触发onTouchsUp + const isTriggerStart = useSharedValue(false) // 记录用户点击时绝对定位坐标 const preAbsolutePos = useSharedValue(0) - const intervalId = useRef(0 as number | ReturnType) const timerId = useRef(0 as number | ReturnType) - const gestureTime = useSharedValue(0) + // 用户点击未移动状态下,记录用户上一次操作的transtion 的 direction const customTrans = useSharedValue(0) + const intervalTimer = props.interval || 500 const { // 存储layout布局信息 layoutRef, @@ -220,24 +210,11 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr ], { layoutRef: layoutRef }) function onWrapperLayout (e: LayoutChangeEvent) { - nodeRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => { - layoutRef.current = { x, y, width, height, offsetLeft, offsetTop } - const isWDiff = initWidth !== width - const isHDiff = initHeight !== height - if (isWDiff || isHDiff) { - const changeState = { - width: isWDiff ? width : widthState, - height: isHDiff ? height : heightState - } - const attr = dir.value === 'x' ? 'width' : 'height' - changeState[attr] = changeState[attr] - previousMargin - nextMargin - if (dir.value === 'x') { - setWidthState(changeState[attr]) - } else { - setHeightState(changeState[attr]) - } - } - }) + const { width, height } = e.nativeEvent.layout + const realWidth = dir.value === 'x' ? width - previousMargin - nextMargin : width + const realHeight = dir.value === 'y' ? height - previousMargin - nextMargin : height + setWidthState(realWidth) + setHeightState(realHeight) } function renderPagination () { @@ -270,23 +247,23 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const pageStyle = { width: widthState, height: heightState } let renderChild = children.slice() if (props.circular && totalElements.value > 1) { - if (totalElements.value === 2) { - renderChild = renderChild.concat(children).concat(children) - } else { - // 最前加两个 - renderChild.unshift(children[totalElements.value - 1]) - renderChild.unshift(children[totalElements.value - 2]) - // 最后加两个 - renderChild.push(children[0]) - renderChild.push(children[1]) - } + // 最前面加最后一个元素 + const lastChild = React.cloneElement(children[totalElements.value - 1] as ReactElement) + // 最后面加第一个元素 + const firstChild = React.cloneElement(children[0] as ReactElement) + renderChild = [lastChild].concat(renderChild).concat(firstChild) } return renderChild.map((child, index) => { const extraStyle = {} as { [key: string]: any } - if (index === 0 && dir.value === 'x' && typeof width === 'number') { - previousMargin && (extraStyle.marginLeft = previousMargin) - } else if (index === totalElements.value - 1 && typeof width === 'number') { - nextMargin && (extraStyle.marginRight = nextMargin) + const xCondition = dir.value === 'x' && typeof widthState === 'number' + const yCondition = dir.value === 'y' && typeof heightState === 'number' + if (index === 0 && (xCondition || yCondition)) { + previousMargin && dir.value === 'x' && (extraStyle.marginLeft = previousMargin) + previousMargin && dir.value === 'y' && (extraStyle.marginTop = previousMargin) + } + if (index === totalElements.value - 1 && (xCondition || yCondition)) { + nextMargin && dir.value === 'x' && (extraStyle.marginRight = nextMargin) + nextMargin && dir.value === 'y' && (extraStyle.marginBottom = nextMargin) } return (, SwiperProps>((pr if (!props.circular) { // 获取下一个位置的坐标, 循环到最后一个元素,直接停止, 取消定时器 if (targetIndex.value === totalElements.value - 1) { - canLoop.value = false + runOnJS(stopLoop)() return } nextIndex += 1 @@ -315,34 +292,33 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr duration: easeDuration, easing: easeMap[easeingFunc] }, () => { - start.value = targetOffset + offset.value = targetOffset targetIndex.value = nextIndex }) } else { // 默认向右, 向下 if (nextIndex === totalElements.value - 1) { nextIndex = 0 - targetOffset[dir.value as dirType] = -(totalElements.value + 2) * step.value + targetOffset[dir.value as dirType] = -(totalElements.value + patchElementNum) * step.value // 执行动画到下一帧 offset.value = withTiming(targetOffset, { duration: easeDuration }, () => { const initOffset = { x: 0, y: 0 } - initOffset[dir.value as dirType] = -step.value * 2 + initOffset[dir.value as dirType] = -step.value * patchElementNum // 将开始位置设置为真正的位置 offset.value = initOffset - start.value = initOffset targetIndex.value = nextIndex }) } else { nextIndex = targetIndex.value + 1 - targetOffset[dir.value as dirType] = -(nextIndex + 2) * step.value + targetOffset[dir.value as dirType] = -(nextIndex + patchElementNum) * step.value // 执行动画到下一帧 offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] }, () => { - start.value = targetOffset + offset.value = targetOffset targetIndex.value = nextIndex }) } @@ -358,23 +334,37 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr return dir.value === 'x' ? widthState : heightState } - function handlerInterval (loopv: boolean, oldLoop: boolean | null) { - clearInterval(intervalId.current) - if (canLoop.value && oldLoop !== true) { - const intervalTimer = props.interval || 500 - clearTimeout(timerId.current) + function getInitOffset () { + const stepValue = getStepValue() + if (isNaN(+stepValue)) return { x: 0, y: 0 } + const targetOffset = { x: 0, y: 0 } + if (props.circular && totalElements.value > 1) { + const targetIndex = (props.current || 0) + patchElementNum + targetOffset[dir.value as dirType] = -stepValue * targetIndex + } else if (props.current && props.current > 0) { + targetOffset[dir.value as dirType] = -props.current * stepValue + } + return targetOffset + } + + function loop () { + if (!paused.current) { timerId.current = setTimeout(() => { - if (props.autoplay && totalElements.value > 1 && canLoop.value) { - intervalId.current = setInterval(() => { - // canLoop变化比较快的情况下 - if (canLoop.value) { - createAutoPlay() - } else { - clearInterval(intervalId.current) - } - }, intervalTimer) - } - }, 500) + createAutoPlay() + loop() + }, intervalTimer) + } + } + + function stopLoop () { + paused.current = true + clearTimeout(timerId.current) + } + + function resumeLoop () { + if (props.autoplay && paused.current) { + paused.current = false + loop() } } @@ -384,61 +374,47 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr if (!isInit && props.bindchange) { runOnJS(handleSwiperChange)(newIndex) } - }, [targetIndex.value]) - - useAnimatedReaction(() => canLoop.value, (loopv, oldLoop) => { - // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息 - if (props.autoplay) { - runOnJS(handlerInterval)(loopv, oldLoop) - } - }, [canLoop.value]) + }) useEffect(() => { // 这里stepValue 有时候拿不到 const stepValue = getStepValue() - if (!Number.isNaN(+stepValue)) { - step.value = stepValue + if (isNaN(+stepValue)) { + return } - if (props.autoplay && !Number.isNaN(+stepValue)) { - if (isAutoFirst.current) { - isAutoFirst.current = false - const targetOffset = getInitOffset() - offset.value = targetOffset - start.value = targetOffset - } - } else { - const targetOffset = getInitOffset() - if (props.current !== undefined && (props.current !== targetIndex.value || (props.current === 0 && targetIndex.value > 0))) { - targetIndex.value = props.current - offset.value = withTiming(targetOffset, { - duration: easeDuration, - easing: easeMap[easeingFunc] - }, () => { - offset.value = targetOffset - start.value = targetOffset - }) - } else { + step.value = stepValue + const targetOffset = getInitOffset() + if (props.current !== undefined && (props.current !== targetIndex.value || (props.current === 0 && targetIndex.value > 0))) { + targetIndex.value = props.current + offset.value = withTiming(targetOffset, { + duration: easeDuration, + easing: easeMap[easeingFunc] + }, () => { offset.value = targetOffset - start.value = targetOffset - props.current && (targetIndex.value = props.current) - } + isAutoFirst.current = false + props.autoplay && isAutoFirst.current && loop() + }) + } else if (isAutoFirst.current) { + isAutoFirst.current = false + offset.value = targetOffset + props.autoplay && loop() } - }, [props.autoplay, props.current, widthState, heightState]) + }, [props.current, widthState, heightState]) - function getTargetPosition (e: GestureUpdateEvent) { + function getTargetPosition (eventData: EventDataType) { 'worklet' + // 移动的距离 + const { translation } = eventData let resetOffsetPos = 0 let selectedIndex = targetIndex.value // 是否临界点 let isCriticalItem = false // 真实滚动到的偏移量坐标 let moveToTargetPos = 0 - // 移动的距离 - const transDistance = e[strTrans] // 当前的位置 const currentOffset = offset.value[dir.value as dirType] const currentIndex = Math.abs(currentOffset) / step.value - const moveToIndex = transDistance < 0 ? Math.ceil(currentIndex) : Math.floor(currentIndex) + const moveToIndex = translation < 0 ? Math.ceil(currentIndex) : Math.floor(currentIndex) // 实际应该定位的索引值 if (!props.circular) { selectedIndex = moveToIndex @@ -450,12 +426,12 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr moveToTargetPos = moveToIndex * step.value isCriticalItem = true } else if (moveToIndex <= patchElementNum - 1) { - selectedIndex = moveToIndex === 0 ? totalElements.value - 2 : totalElements.value - 1 + selectedIndex = moveToIndex === 0 ? totalElements.value - patchElementNum : totalElements.value - 1 resetOffsetPos = (selectedIndex + patchElementNum) * step.value moveToTargetPos = moveToIndex * step.value isCriticalItem = true } else { - selectedIndex = moveToIndex - 2 + selectedIndex = moveToIndex - patchElementNum moveToTargetPos = moveToIndex * step.value } } @@ -473,134 +449,122 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } } - function canMove (e: GestureUpdateEvent) { + function canMove (eventData: EventDataType) { 'worklet' + const { translation } = eventData if (!props.circular) { - const transDistance = e[strTrans] - if (transDistance < 0) { + if (translation < 0) { return targetIndex.value < totalElements.value - 1 } else { - return targetIndex.value > 0 + return targetIndex.value >= 0 } } else { return true } } - function handleEnd (e: GestureUpdateEvent) { + function handleEnd (eventData: EventDataType) { 'worklet' - const subtractTime = Date.now() - gestureTime.value - const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(e) - // 用户快速连续操作小于100ms, 快速切换有些生硬,调整下时间 - const durationTime = subtractTime <= quickMoveTime ? quickMoveDuration : easeDuration + const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(eventData) if (isCriticalItem) { offset.value = withTiming(targetOffset, { - duration: durationTime, + duration: easeDuration, easing: easeMap[easeingFunc] }, () => { - targetIndex.value = selectedIndex if (touchfinish.value !== false) { - start.value = resetOffset + targetIndex.value = selectedIndex offset.value = resetOffset - canLoop.value = true + runOnJS(resumeLoop)() } }) } else { offset.value = withTiming(targetOffset, { - duration: durationTime, + duration: easeDuration, easing: easeMap[easeingFunc] }, () => { - targetIndex.value = selectedIndex if (touchfinish.value !== false) { - start.value = targetOffset + targetIndex.value = selectedIndex offset.value = targetOffset - canLoop.value = true + runOnJS(resumeLoop)() } }) } } - function handleBack () { + function handleBack (eventData: EventDataType) { 'worklet' - // 向右trans > 0, 向左trans < 0, 向右滑动的back, 向左滑动的back - const posScrollRight = -(patchElementNum + targetIndex.value) * step.value - const posScrollLeft = -(patchElementNum + targetIndex.value) * step.value - const pos = customTrans.value < 0 ? posScrollRight : posScrollLeft + const { translation } = eventData + // 向右滑动的back:trans < 0, 向左滑动的back: trans < 0 + const currentOffset = Math.abs(offset.value[dir.value as dirType]) + const curIndex = currentOffset / step.value + const moveToIndex = (translation < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElementNum + const pos = (moveToIndex + patchElementNum) * step.value const targetOffset = { - x: dir.value === 'x' ? pos : 0, - y: dir.value === 'y' ? pos : 0 + x: dir.value === 'x' ? -pos : 0, + y: dir.value === 'y' ? -pos : 0 } offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] }, () => { if (touchfinish.value !== false) { - start.value = targetOffset offset.value = targetOffset - canLoop.value = true + targetIndex.value = moveToIndex + runOnJS(resumeLoop)() } }) } - function handleLongPress (e: GestureUpdateEvent) { + function handleLongPress (eventData: EventDataType) { 'worklet' + const { translation } = eventData const currentOffset = Math.abs(offset.value[dir.value as dirType]) const half = currentOffset % step.value > step.value / 2 - // 向右trans > 0, 向左trans < 0 - const isExceedHalf = customTrans.value < 0 ? half : !half + // 向右trans < 0, 向左trans > 0 + const isExceedHalf = translation < 0 ? half : !half if (isExceedHalf) { - handleEnd(e) + handleEnd(eventData) } else { - handleBack() + handleBack(eventData) } } - function reachBoundary (e: GestureUpdateEvent) { + function reachBoundary (eventData: EventDataType) { 'worklet' // 移动的距离 - const transDistance = e[strTrans] - const moveDistance = e[strAbso] - preAbsolutePos.value + const { translation } = eventData const elementsLength = step.value * totalElements.value + let isBoundary = false let resetOffset = 0 - let moveToTargetPos = 0 - // Y轴向下滚动, transDistance > 0, 向上滚动 < 0 - // X轴向左滚动, transDistance > 0 + // Y轴向下滚动, transDistance > 0, 向上滚动 < 0 X轴向左滚动, transDistance > 0 const currentOffset = offset.value[dir.value as dirType] - const moveStep = Math.ceil(moveDistance / elementsLength) - if (transDistance < 0) { + const moveStep = Math.ceil(translation / elementsLength) + if (translation < 0) { const posEnd = (totalElements.value + patchElementNum + 1) * step.value const posReverseEnd = (patchElementNum - 1) * step.value if (currentOffset < -posEnd + step.value && currentOffset + posEnd > -2) { isBoundary = true - moveToTargetPos = (totalElements.value + patchElementNum) * step.value - resetOffset = Math.abs(moveStep) === 0 ? patchElementNum * step.value + moveDistance : moveStep * elementsLength + resetOffset = Math.abs(moveStep) === 0 ? patchElementNum * step.value + translation : moveStep * elementsLength } if (currentOffset > -posReverseEnd) { isBoundary = true - moveToTargetPos = (patchElementNum + totalElements.value - 1) * step.value resetOffset = moveStep * elementsLength } - } else if (transDistance > 0) { + } else if (translation > 0) { const posEnd = (patchElementNum - 1) * step.value const posReverseEnd = (patchElementNum + totalElements.value) * step.value if (currentOffset > -posEnd) { isBoundary = true - resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? moveDistance : 0) - moveToTargetPos = (patchElementNum - 1) * step.value + resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? translation : 0) } if (currentOffset < -posReverseEnd) { isBoundary = true resetOffset = moveStep * elementsLength + patchElementNum * step.value - moveToTargetPos = posReverseEnd } } return { isBoundary, - moveToTargetPos: { - x: dir.value === 'x' ? -moveToTargetPos : 0, - y: dir.value === 'y' ? -moveToTargetPos : 0 - }, resetOffset: { x: dir.value === 'x' ? -resetOffset : 0, y: dir.value === 'y' ? -resetOffset : 0 @@ -612,65 +576,62 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr .onBegin((e) => { 'worklet' touchfinish.value = false - start.value = { - x: dir.value === 'x' ? offset.value.x : 0, - y: dir.value === 'y' ? offset.value.y : 0 - } cancelAnimation(offset) + runOnJS(stopLoop)() preAbsolutePos.value = e[strAbso] - canLoop.value = false - gestureTime.value = Date.now() }) - .onTouchesUp((e) => { + .onStart(() => { 'worklet' - cancelAnimation(offset) - touchfinish.value = true - const touchEventData = e.changedTouches[0] - // 只点击不会触发onEnd事件 - if (Math.ceil(preAbsolutePos.value) === Math.ceil(touchEventData[strAbso])) { - const transObj: { [key in StrTransType]: number } = { - translationX: 0, - translationY: 0 - } - transObj[strTrans] = customTrans.value - handleLongPress(transObj as GestureUpdateEvent) - } + isTriggerStart.value = true }) - .onUpdate((e) => { + .onTouchesMove((e) => { 'worklet' - cancelAnimation(offset) - if (!props.circular && !canMove(e)) { - return - } - const moveDistance = e[strAbso] - preAbsolutePos.value + const touchEventData = e.changedTouches[0] + const moveDistance = touchEventData[strAbso] - preAbsolutePos.value customTrans.value = moveDistance + const eventData = { + translation: moveDistance + } // 处理用户一直拖拽到临界点的场景, 不会执行onEnd - const { isBoundary, resetOffset, moveToTargetPos } = reachBoundary(e) + if (!props.circular && !canMove(eventData)) { + return + } + const { isBoundary, resetOffset } = reachBoundary(eventData) if (isBoundary && props.circular && !touchfinish.value) { - start.value = resetOffset - offset.value = moveToTargetPos + offset.value = resetOffset } else { offset.value = { - x: moveDistance + start.value.x, - y: moveDistance + start.value.y + x: moveDistance + offset.value.x, + y: moveDistance + offset.value.y } } + preAbsolutePos.value = touchEventData[strAbso] + }) + .onTouchesUp((e) => { + 'worklet' + // 未触发移动不会触发onStart事件, touches事件拿到的数据只有absoluteX 和 x, 无法判断方向 + if (!isTriggerStart.value) { + const eventData = { + translation: customTrans.value + } + handleLongPress(eventData) + } + isTriggerStart.value = false + touchfinish.value = true }) .onEnd((e) => { 'worklet' - if (!props.circular && !canMove(e)) { - return + const moveDistance = e[strAbso] - preAbsolutePos.value + const eventData = { + translation: e[strTrans] } - const { isBoundary, resetOffset: reachReset, moveToTargetPos } = reachBoundary(e) - if (props.circular && isBoundary) { - start.value = reachReset - offset.value = moveToTargetPos + if (!props.circular && !canMove(eventData) && isTriggerStart.value) { return } if (Math.abs(e[strVelocity]) < longPressRatio) { - handleLongPress(e) + handleLongPress(eventData) } else { - handleEnd(e) + handleEnd(eventData) } }) From a7e51008e59d8a804668374a53e87636f0c92b77 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Mon, 25 Nov 2024 11:48:30 +0800 Subject: [PATCH 20/26] delete worklet --- .../webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index 497ee870c..f5ea0f8c6 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -277,7 +277,6 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } function createAutoPlay () { - 'worklet' const targetOffset = { x: 0, y: 0 } let nextIndex = targetIndex.value if (!props.circular) { From 4f62f1f27e9d14ed8fde0be622f1ec3a6fc6396b Mon Sep 17 00:00:00 2001 From: luyongfang Date: Mon, 25 Nov 2024 12:59:32 +0800 Subject: [PATCH 21/26] add children change --- .../lib/runtime/components/react/mpx-swiper.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index f5ea0f8c6..e0f2a9423 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -400,6 +400,12 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } }, [props.current, widthState, heightState]) + useEffect(() => { + if (totalElements.value !== children.length) { + totalElements.value = children.length + } + }, [props.children]) + function getTargetPosition (eventData: EventDataType) { 'worklet' // 移动的距离 From 752ac6e623ce596efeb60078f7904c5ebcbddfb4 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Tue, 26 Nov 2024 17:02:01 +0800 Subject: [PATCH 22/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8DcanMove=20&=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0dot=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 70 +++++++++++++------ 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index e0f2a9423..a4e0c3173 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -1,6 +1,6 @@ import { View, NativeSyntheticEvent, Dimensions, LayoutChangeEvent } 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 Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation, interpolate } from 'react-native-reanimated' import React, { JSX, forwardRef, useRef, useEffect, useState, ReactNode, ReactElement } from 'react' import useInnerProps, { getCustomEvent } from './getInnerListeners' @@ -82,6 +82,11 @@ const styles: { [key: string]: Object } = { justifyContent: 'center', alignItems: 'center' }, + pagerWrapper: { + position: 'absolute', + flexDirection: 'row', + alignSelf: 'center', + }, swiper: { overflow: 'scroll', display: 'flex', @@ -96,7 +101,11 @@ const dotCommonStyle = { marginLeft: 3, marginRight: 3, marginTop: 3, - marginBottom: 3 + marginBottom: 3, + zIndex: 98 +} +const activeDotStyle = { + zIndex: 99 } // 默认前后补位的元素个数 const patchElementNum = 1 @@ -217,30 +226,45 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr setHeightState(realHeight) } + const dotAnimatedStyle = useAnimatedStyle(() => { + const step = dir.value === 'x' ? widthState : heightState + const moveStep = dotCommonStyle.width + dotCommonStyle.marginLeft + dotCommonStyle.marginRight + if (isNaN(+step)) return {} + const inputRange = [-step, 0, step] + const outputRange = [-moveStep, 0, moveStep] + return { + transform: [{ + translateX: interpolate(-offset.value[dir.value as dirType], inputRange, outputRange) + }] + } + }) + function renderPagination () { - if (totalElements.value <= 1) return null + const stepValue = getStepValue() + if (totalElements.value <= 1 || isNaN(+stepValue)) return null + const activeColor = activeDotColor || '#007aff' + const unActionColor = dotColor || 'rgba(0,0,0,.2)' + // 正常渲染所有dots const dots: Array = [] for (let i = 0; i < totalElements.value; i++) { - const dotStyle = useAnimatedStyle(() => { - const activeColor = activeDotColor || '#007aff' - const unActionColor = dotColor || 'rgba(0,0,0,.2)' - return { - backgroundColor: i === targetIndex.value ? activeColor : unActionColor - } - }) - dots.push( - ) + dots.push() } return ( - {dots} + + + {dots} - ) + ) } function renderItems () { @@ -330,6 +354,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } function getStepValue () { + 'worklet' return dir.value === 'x' ? widthState : heightState } @@ -457,11 +482,13 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr function canMove (eventData: EventDataType) { 'worklet' const { translation } = eventData + const stepValue = getStepValue() + const currentOffset = Math.abs(offset.value[dir.value as dirType]) if (!props.circular) { if (translation < 0) { - return targetIndex.value < totalElements.value - 1 + return currentOffset + Math.abs(translation) < stepValue * (totalElements.value - 1) } else { - return targetIndex.value >= 0 + return currentOffset - Math.abs(translation) > 0 } } else { return true @@ -626,7 +653,6 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr }) .onEnd((e) => { 'worklet' - const moveDistance = e[strAbso] - preAbsolutePos.value const eventData = { translation: e[strTrans] } From 017fb99f1f68b4d3754d3274630201af911ea26f Mon Sep 17 00:00:00 2001 From: luyongfang Date: Tue, 26 Nov 2024 21:14:29 +0800 Subject: [PATCH 23/26] =?UTF-8?q?=E4=BC=98=E5=8C=96CR=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 93 ++++++++----------- 1 file changed, 39 insertions(+), 54 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index a4e0c3173..1f7f73826 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -85,7 +85,7 @@ const styles: { [key: string]: Object } = { pagerWrapper: { position: 'absolute', flexDirection: 'row', - alignSelf: 'center', + alignSelf: 'center' }, swiper: { overflow: 'scroll', @@ -175,11 +175,10 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr // 记录选中元素的索引值 const targetIndex = useSharedValue(props.current || 0) // 记录元素的偏移量 - const offset = useSharedValue({ x: 0, y: 0 }) + const offset = useSharedValue(0) const strTrans = 'translation' + dir.value.toUpperCase() as StrTransType const strAbso = 'absolute' + dir.value.toUpperCase() as StrAbsoType const strVelocity = 'velocity' + dir.value.toUpperCase() as StrVelocity - const isAutoFirst = useRef(true) const arrPages: Array | ReactNode = renderItems() // autoplay的状态下是否被暂停 const paused = useRef(false) @@ -193,6 +192,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr // 用户点击未移动状态下,记录用户上一次操作的transtion 的 direction const customTrans = useSharedValue(0) const intervalTimer = props.interval || 500 + totalElements.value = children.length const { // 存储layout布局信息 layoutRef, @@ -234,7 +234,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const outputRange = [-moveStep, 0, moveStep] return { transform: [{ - translateX: interpolate(-offset.value[dir.value as dirType], inputRange, outputRange) + translateX: interpolate(-offset.value, inputRange, outputRange) }] } }) @@ -301,16 +301,16 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } function createAutoPlay () { - const targetOffset = { x: 0, y: 0 } + let targetOffset = 0 let nextIndex = targetIndex.value if (!props.circular) { // 获取下一个位置的坐标, 循环到最后一个元素,直接停止, 取消定时器 if (targetIndex.value === totalElements.value - 1) { - runOnJS(stopLoop)() + pauseLoop() return } nextIndex += 1 - targetOffset[dir.value as dirType] = -nextIndex * step.value + targetOffset = -nextIndex * step.value offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] @@ -322,20 +322,19 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr // 默认向右, 向下 if (nextIndex === totalElements.value - 1) { nextIndex = 0 - targetOffset[dir.value as dirType] = -(totalElements.value + patchElementNum) * step.value + targetOffset = -(totalElements.value + patchElementNum) * step.value // 执行动画到下一帧 offset.value = withTiming(targetOffset, { duration: easeDuration }, () => { - const initOffset = { x: 0, y: 0 } - initOffset[dir.value as dirType] = -step.value * patchElementNum + const initOffset = -step.value * patchElementNum // 将开始位置设置为真正的位置 offset.value = initOffset targetIndex.value = nextIndex }) } else { nextIndex = targetIndex.value + 1 - targetOffset[dir.value as dirType] = -(nextIndex + patchElementNum) * step.value + targetOffset = -(nextIndex + patchElementNum) * step.value // 执行动画到下一帧 offset.value = withTiming(targetOffset, { duration: easeDuration, @@ -360,13 +359,13 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr function getInitOffset () { const stepValue = getStepValue() - if (isNaN(+stepValue)) return { x: 0, y: 0 } - const targetOffset = { x: 0, y: 0 } + if (isNaN(+stepValue)) return 0 + let targetOffset = 0 if (props.circular && totalElements.value > 1) { const targetIndex = (props.current || 0) + patchElementNum - targetOffset[dir.value as dirType] = -stepValue * targetIndex + targetOffset = -stepValue * targetIndex } else if (props.current && props.current > 0) { - targetOffset[dir.value as dirType] = -props.current * stepValue + targetOffset = -props.current * stepValue } return targetOffset } @@ -380,7 +379,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } } - function stopLoop () { + function pauseLoop () { paused.current = true clearTimeout(timerId.current) } @@ -415,21 +414,23 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr easing: easeMap[easeingFunc] }, () => { offset.value = targetOffset - isAutoFirst.current = false - props.autoplay && isAutoFirst.current && loop() }) - } else if (isAutoFirst.current) { - isAutoFirst.current = false - offset.value = targetOffset - props.autoplay && loop() } }, [props.current, widthState, heightState]) useEffect(() => { - if (totalElements.value !== children.length) { - totalElements.value = children.length + const stepValue = getStepValue() + if (isNaN(+stepValue)) { + return } - }, [props.children]) + const targetOffset = getInitOffset() + if (props.autoplay) { + offset.value = targetOffset + loop() + } else { + pauseLoop() + } + }, [props.autoplay, widthState, heightState]) function getTargetPosition (eventData: EventDataType) { 'worklet' @@ -442,7 +443,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr // 真实滚动到的偏移量坐标 let moveToTargetPos = 0 // 当前的位置 - const currentOffset = offset.value[dir.value as dirType] + const currentOffset = offset.value const currentIndex = Math.abs(currentOffset) / step.value const moveToIndex = translation < 0 ? Math.ceil(currentIndex) : Math.floor(currentIndex) // 实际应该定位的索引值 @@ -468,14 +469,8 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr return { selectedIndex, isCriticalItem, - resetOffset: { - x: dir.value === 'x' ? -resetOffsetPos : 0, - y: dir.value === 'y' ? -resetOffsetPos : 0 - }, - targetOffset: { - x: dir.value === 'x' ? -moveToTargetPos : offset.value.x, - y: dir.value === 'y' ? -moveToTargetPos : offset.value.y - } + resetOffset: -resetOffsetPos, + targetOffset: -moveToTargetPos } } @@ -483,7 +478,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr 'worklet' const { translation } = eventData const stepValue = getStepValue() - const currentOffset = Math.abs(offset.value[dir.value as dirType]) + const currentOffset = Math.abs(offset.value) if (!props.circular) { if (translation < 0) { return currentOffset + Math.abs(translation) < stepValue * (totalElements.value - 1) @@ -527,14 +522,10 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr 'worklet' const { translation } = eventData // 向右滑动的back:trans < 0, 向左滑动的back: trans < 0 - const currentOffset = Math.abs(offset.value[dir.value as dirType]) + const currentOffset = Math.abs(offset.value) const curIndex = currentOffset / step.value const moveToIndex = (translation < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElementNum - const pos = (moveToIndex + patchElementNum) * step.value - const targetOffset = { - x: dir.value === 'x' ? -pos : 0, - y: dir.value === 'y' ? -pos : 0 - } + const targetOffset = (moveToIndex + patchElementNum) * step.value offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] @@ -550,7 +541,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr function handleLongPress (eventData: EventDataType) { 'worklet' const { translation } = eventData - const currentOffset = Math.abs(offset.value[dir.value as dirType]) + const currentOffset = Math.abs(offset.value) const half = currentOffset % step.value > step.value / 2 // 向右trans < 0, 向左trans > 0 const isExceedHalf = translation < 0 ? half : !half @@ -570,7 +561,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr let isBoundary = false let resetOffset = 0 // Y轴向下滚动, transDistance > 0, 向上滚动 < 0 X轴向左滚动, transDistance > 0 - const currentOffset = offset.value[dir.value as dirType] + const currentOffset = offset.value const moveStep = Math.ceil(translation / elementsLength) if (translation < 0) { const posEnd = (totalElements.value + patchElementNum + 1) * step.value @@ -597,10 +588,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } return { isBoundary, - resetOffset: { - x: dir.value === 'x' ? -resetOffset : 0, - y: dir.value === 'y' ? -resetOffset : 0 - } + resetOffset: -resetOffset } } @@ -609,7 +597,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr 'worklet' touchfinish.value = false cancelAnimation(offset) - runOnJS(stopLoop)() + runOnJS(pauseLoop)() preAbsolutePos.value = e[strAbso] }) .onStart(() => { @@ -632,10 +620,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr if (isBoundary && props.circular && !touchfinish.value) { offset.value = resetOffset } else { - offset.value = { - x: moveDistance + offset.value.x, - y: moveDistance + offset.value.y - } + offset.value = moveDistance + offset.value } preAbsolutePos.value = touchEventData[strAbso] }) @@ -668,9 +653,9 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const animatedStyles = useAnimatedStyle(() => { if (dir.value === 'x') { - return { transform: [{ translateX: offset.value.x }] } + return { transform: [{ translateX: offset.value }] } } else { - return { transform: [{ translateY: offset.value.y }] } + return { transform: [{ translateY: offset.value }] } } }) From 4e23d54c71e9cf1062f7fe4c31d380239e90e1d4 Mon Sep 17 00:00:00 2001 From: luyongfang Date: Fri, 29 Nov 2024 13:07:22 +0800 Subject: [PATCH 24/26] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=BE=B9=E7=95=8C?= =?UTF-8?q?=E5=9C=BA=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runtime/components/react/mpx-swiper.tsx | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index 1f7f73826..df31a52ae 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -1,6 +1,6 @@ import { View, NativeSyntheticEvent, Dimensions, LayoutChangeEvent } from 'react-native' import { GestureDetector, Gesture } from 'react-native-gesture-handler' -import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation, interpolate } from 'react-native-reanimated' +import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation, interpolateColor, interpolate, Extrapolation } from 'react-native-reanimated' import React, { JSX, forwardRef, useRef, useEffect, useState, ReactNode, ReactElement } from 'react' import useInnerProps, { getCustomEvent } from './getInnerListeners' @@ -85,7 +85,8 @@ const styles: { [key: string]: Object } = { pagerWrapper: { position: 'absolute', flexDirection: 'row', - alignSelf: 'center' + justifyContent: 'center', + alignItems: 'center' }, swiper: { overflow: 'scroll', @@ -173,7 +174,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const step = useSharedValue(initStep) const totalElements = useSharedValue(children.length) // 记录选中元素的索引值 - const targetIndex = useSharedValue(props.current || 0) + const targetIndex = useSharedValue(0) // 记录元素的偏移量 const offset = useSharedValue(0) const strTrans = 'translation' + dir.value.toUpperCase() as StrTransType @@ -228,17 +229,15 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const dotAnimatedStyle = useAnimatedStyle(() => { const step = dir.value === 'x' ? widthState : heightState - const moveStep = dotCommonStyle.width + dotCommonStyle.marginLeft + dotCommonStyle.marginRight if (isNaN(+step)) return {} - const inputRange = [-step, 0, step] - const outputRange = [-moveStep, 0, moveStep] + const dotStep = dotCommonStyle.width + dotCommonStyle.marginRight + dotCommonStyle.marginLeft return { transform: [{ - translateX: interpolate(-offset.value, inputRange, outputRange) + translateX: targetIndex.value * dotStep }] } }) - + function renderPagination () { const stepValue = getStepValue() if (totalElements.value <= 1 || isNaN(+stepValue)) return null @@ -257,7 +256,8 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr activeDotStyle, { backgroundColor: activeColor, - position: 'absolute' + position: 'absolute', + left: 0 }, dotAnimatedStyle ]} @@ -415,6 +415,8 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr }, () => { offset.value = targetOffset }) + } else { + offset.value = targetOffset } }, [props.current, widthState, heightState]) @@ -425,11 +427,13 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr } const targetOffset = getInitOffset() if (props.autoplay) { + pauseLoop() offset.value = targetOffset - loop() + resumeLoop() } else { pauseLoop() } + return () => { pauseLoop() } }, [props.autoplay, widthState, heightState]) function getTargetPosition (eventData: EventDataType) { @@ -525,7 +529,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const currentOffset = Math.abs(offset.value) const curIndex = currentOffset / step.value const moveToIndex = (translation < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElementNum - const targetOffset = (moveToIndex + patchElementNum) * step.value + const targetOffset = -(moveToIndex + patchElementNum) * step.value offset.value = withTiming(targetOffset, { duration: easeDuration, easing: easeMap[easeingFunc] @@ -545,7 +549,9 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr const half = currentOffset % step.value > step.value / 2 // 向右trans < 0, 向左trans > 0 const isExceedHalf = translation < 0 ? half : !half - if (isExceedHalf) { + if (+translation === 0) { + runOnJS(resumeLoop)() + } else if (isExceedHalf) { handleEnd(eventData) } else { handleBack(eventData) @@ -566,7 +572,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr if (translation < 0) { const posEnd = (totalElements.value + patchElementNum + 1) * step.value const posReverseEnd = (patchElementNum - 1) * step.value - if (currentOffset < -posEnd + step.value && currentOffset + posEnd > -2) { + if (currentOffset < -posEnd + step.value) { isBoundary = true resetOffset = Math.abs(moveStep) === 0 ? patchElementNum * step.value + translation : moveStep * elementsLength } From 2dd23501912f5beca64b7c5649246efb50e433ec Mon Sep 17 00:00:00 2001 From: luyongfang Date: Tue, 3 Dec 2024 14:23:15 +0800 Subject: [PATCH 25/26] fix eslint --- .../webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index df31a52ae..b393244bc 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -237,7 +237,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr }] } }) - + function renderPagination () { const stepValue = getStepValue() if (totalElements.value <= 1 || isNaN(+stepValue)) return null From 006729c9e103d871bd16e3b97d9a3e03737b834d Mon Sep 17 00:00:00 2001 From: luyongfang Date: Tue, 10 Dec 2024 12:37:57 +0800 Subject: [PATCH 26/26] fix change --- .../webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx index b393244bc..05ec7f777 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-swiper.tsx @@ -394,7 +394,7 @@ const SwiperWrapper = forwardRef, SwiperProps>((pr useAnimatedReaction(() => targetIndex.value, (newIndex, preIndex) => { // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息 const isInit = !preIndex && newIndex === 0 - if (!isInit && props.bindchange) { + if (!isInit && props.current !== newIndex && props.bindchange) { runOnJS(handleSwiperChange)(newIndex) } })