@mpxjs/webpack-plugin 2.9.70 → 2.9.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/lib/platform/template/wx/component-config/movable-view.js +8 -1
  2. package/lib/platform/template/wx/component-config/picker-view.js +1 -5
  3. package/lib/platform/template/wx/component-config/scroll-view.js +1 -1
  4. package/lib/platform/template/wx/index.js +0 -4
  5. package/lib/runtime/components/react/context.ts +8 -0
  6. package/lib/runtime/components/react/dist/context.js +2 -0
  7. package/lib/runtime/components/react/dist/getInnerListeners.js +34 -31
  8. package/lib/runtime/components/react/dist/mpx-button.jsx +16 -44
  9. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +93 -58
  10. package/lib/runtime/components/react/dist/mpx-navigator.jsx +1 -1
  11. package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +35 -0
  12. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +151 -127
  13. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +38 -34
  14. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -11
  15. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +11 -4
  16. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +31 -8
  17. package/lib/runtime/components/react/dist/mpx-swiper.jsx +670 -0
  18. package/lib/runtime/components/react/dist/mpx-view.jsx +15 -53
  19. package/lib/runtime/components/react/dist/pickerFaces.js +7 -6
  20. package/lib/runtime/components/react/dist/pickerVIewContext.js +14 -0
  21. package/lib/runtime/components/react/dist/pickerViewIndicator.jsx +23 -0
  22. package/lib/runtime/components/react/dist/pickerViewMask.jsx +18 -0
  23. package/lib/runtime/components/react/dist/useAnimationHooks.js +20 -2
  24. package/lib/runtime/components/react/dist/utils.jsx +74 -11
  25. package/lib/runtime/components/react/getInnerListeners.ts +43 -32
  26. package/lib/runtime/components/react/mpx-button.tsx +20 -57
  27. package/lib/runtime/components/react/mpx-movable-view.tsx +119 -74
  28. package/lib/runtime/components/react/mpx-navigator.tsx +1 -1
  29. package/lib/runtime/components/react/mpx-picker-view-column-item.tsx +76 -0
  30. package/lib/runtime/components/react/mpx-picker-view-column.tsx +206 -183
  31. package/lib/runtime/components/react/mpx-picker-view.tsx +49 -48
  32. package/lib/runtime/components/react/mpx-rich-text/index.tsx +12 -18
  33. package/lib/runtime/components/react/mpx-scroll-view.tsx +21 -10
  34. package/lib/runtime/components/react/mpx-swiper-item.tsx +45 -11
  35. package/lib/runtime/components/react/mpx-swiper.tsx +742 -0
  36. package/lib/runtime/components/react/mpx-view.tsx +18 -65
  37. package/lib/runtime/components/react/pickerFaces.ts +10 -7
  38. package/lib/runtime/components/react/pickerVIewContext.ts +27 -0
  39. package/lib/runtime/components/react/pickerViewIndicator.tsx +34 -0
  40. package/lib/runtime/components/react/pickerViewMask.tsx +30 -0
  41. package/lib/runtime/components/react/types/{getInnerListeners.ts → getInnerListeners.d.ts} +4 -5
  42. package/lib/runtime/components/react/types/global.d.ts +10 -0
  43. package/lib/runtime/components/react/useAnimationHooks.ts +24 -3
  44. package/lib/runtime/components/react/utils.tsx +85 -12
  45. package/lib/runtime/components/web/mpx-checkbox.vue +1 -1
  46. package/lib/runtime/components/web/mpx-picker-view-column.vue +9 -4
  47. package/lib/template-compiler/compiler.js +61 -13
  48. package/lib/wxss/loader.js +15 -2
  49. package/package.json +3 -3
  50. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +0 -480
  51. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +0 -68
  52. package/lib/runtime/components/react/dist/mpx-swiper/type.js +0 -1
  53. package/lib/runtime/components/react/dist/pickerOverlay.jsx +0 -21
  54. package/lib/runtime/components/react/dist/types/common.js +0 -1
  55. package/lib/runtime/components/react/dist/types/getInnerListeners.js +0 -1
  56. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +0 -527
  57. package/lib/runtime/components/react/mpx-swiper/index.tsx +0 -80
  58. package/lib/runtime/components/react/mpx-swiper/type.ts +0 -87
  59. package/lib/runtime/components/react/pickerOverlay.tsx +0 -32
  60. /package/lib/runtime/components/react/types/{common.ts → common.d.ts} +0 -0
@@ -0,0 +1,742 @@
1
+ import { View, NativeSyntheticEvent, LayoutChangeEvent } from 'react-native'
2
+ import { GestureDetector, Gesture } from 'react-native-gesture-handler'
3
+ import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation } from 'react-native-reanimated'
4
+
5
+ import React, { JSX, forwardRef, useRef, useEffect, ReactNode, ReactElement, useMemo } from 'react'
6
+ import useInnerProps, { getCustomEvent } from './getInnerListeners'
7
+ import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数
8
+ import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from './utils'
9
+ import { SwiperContext } from './context'
10
+ /**
11
+ * ✔ indicator-dots
12
+ * ✔ indicator-color
13
+ * ✔ indicator-active-color
14
+ * ✔ autoplay
15
+ * ✔ current
16
+ * ✔ interval
17
+ * ✔ duration
18
+ * ✔ circular
19
+ * ✔ vertical
20
+ * ✔ previous-margin
21
+ * ✔ next-margin
22
+ * ✔ easing-function ="easeOutCubic"
23
+ * ✘ display-multiple-items
24
+ * ✘ snap-to-edge
25
+ */
26
+ type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic'
27
+ type StrAbsoType = 'absoluteX' | 'absoluteY'
28
+ type EventDataType = {
29
+ translation: number
30
+ }
31
+
32
+ interface SwiperProps {
33
+ children?: ReactNode;
34
+ circular?: boolean;
35
+ current?: number;
36
+ interval?: number;
37
+ autoplay?: boolean;
38
+ // scrollView 只有安卓可以设
39
+ duration?: number;
40
+ // 滑动过程中元素是否scale变化
41
+ scale?: boolean;
42
+ 'indicator-dots'?: boolean;
43
+ 'indicator-color'?: string;
44
+ 'indicator-active-color'?: string;
45
+ vertical?: boolean;
46
+ style: {
47
+ [key: string]: any
48
+ };
49
+ 'easing-function'?: EaseType;
50
+ 'previous-margin'?: string;
51
+ 'next-margin'?: string;
52
+ 'enable-offset'?: boolean;
53
+ 'enable-var': boolean;
54
+ 'parent-font-size'?: number;
55
+ 'parent-width'?: number;
56
+ 'parent-height'?: number;
57
+ 'external-var-context'?: Record<string, any>;
58
+ bindchange?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void;
59
+ }
60
+
61
+ /**
62
+ * 默认的Style类型
63
+ */
64
+ const styles: { [key: string]: Object } = {
65
+ pagination_x: {
66
+ position: 'absolute',
67
+ bottom: 25,
68
+ left: 0,
69
+ right: 0,
70
+ flexDirection: 'row',
71
+ flex: 1,
72
+ justifyContent: 'center',
73
+ alignItems: 'center'
74
+ },
75
+ pagination_y: {
76
+ position: 'absolute',
77
+ right: 15,
78
+ top: 0,
79
+ bottom: 0,
80
+ flexDirection: 'column',
81
+ flex: 1,
82
+ justifyContent: 'center',
83
+ alignItems: 'center'
84
+ },
85
+ pagerWrapperx: {
86
+ position: 'absolute',
87
+ flexDirection: 'row',
88
+ justifyContent: 'center',
89
+ alignItems: 'center'
90
+ },
91
+ pagerWrappery: {
92
+ position: 'absolute',
93
+ flexDirection: 'column',
94
+ justifyContent: 'center',
95
+ alignItems: 'center'
96
+ },
97
+ swiper: {
98
+ overflow: 'scroll',
99
+ display: 'flex',
100
+ justifyContent: 'flex-start'
101
+ }
102
+ }
103
+
104
+ const dotCommonStyle = {
105
+ width: 8,
106
+ height: 8,
107
+ borderRadius: 4,
108
+ marginLeft: 3,
109
+ marginRight: 3,
110
+ marginTop: 3,
111
+ marginBottom: 3,
112
+ zIndex: 98
113
+ }
114
+ const activeDotStyle = {
115
+ zIndex: 99
116
+ }
117
+ const longPressRatio = 100
118
+
119
+ const easeMap = {
120
+ default: Easing.linear,
121
+ linear: Easing.linear,
122
+ easeInCubic: Easing.in(Easing.cubic),
123
+ easeOutCubic: Easing.out(Easing.cubic),
124
+ easeInOutCubic: Easing.inOut(Easing.cubic)
125
+ }
126
+
127
+ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((props: SwiperProps, ref): JSX.Element => {
128
+ const {
129
+ 'indicator-dots': showsPagination,
130
+ 'indicator-color': dotColor = 'rgba(0, 0, 0, .3)',
131
+ 'indicator-active-color': activeDotColor = '#000000',
132
+ 'enable-var': enableVar = false,
133
+ 'parent-font-size': parentFontSize,
134
+ 'parent-width': parentWidth,
135
+ 'parent-height': parentHeight,
136
+ 'external-var-context': externalVarContext,
137
+ style = {},
138
+ autoplay = false,
139
+ circular = false
140
+ } = props
141
+ const easeingFunc = props['easing-function'] || 'default'
142
+ const easeDuration = props.duration || 500
143
+ const horizontal = props.vertical !== undefined ? !props.vertical : true
144
+ const nodeRef = useRef<View>(null)
145
+ useNodesRef<View, SwiperProps>(props, ref, nodeRef, {})
146
+ // 计算transfrom之类的
147
+ const {
148
+ normalStyle,
149
+ hasVarDec,
150
+ varContextRef,
151
+ hasSelfPercent,
152
+ setWidth,
153
+ setHeight
154
+ } = useTransformStyle(style, {
155
+ enableVar,
156
+ externalVarContext,
157
+ parentFontSize,
158
+ parentWidth,
159
+ parentHeight
160
+ })
161
+ const { textStyle } = splitStyle(normalStyle)
162
+ const { textProps } = splitProps(props)
163
+ const preMargin = props['previous-margin'] ? global.__formatValue(props['previous-margin']) as number : 0
164
+ const nextMargin = props['next-margin'] ? global.__formatValue(props['next-margin']) as number : 0
165
+ const preMarginShared = useSharedValue(preMargin)
166
+ const nextMarginShared = useSharedValue(nextMargin)
167
+ const autoplayShared = useSharedValue(autoplay)
168
+ // 默认前后补位的元素个数
169
+ const patchElmNum = circular ? (preMargin ? 2 : 1) : 0
170
+ const patchElmNumShared = useSharedValue(patchElmNum)
171
+ const circularShared = useSharedValue(circular)
172
+ const children = Array.isArray(props.children) ? props.children.filter(child => child) : (props.children ? [props.children] : [])
173
+ // 对有变化的变量,在worklet中只能使用sharedValue变量,useRef不能更新
174
+ const childrenLength = useSharedValue(children.length)
175
+ const initWidth = typeof normalStyle?.width === 'number' ? normalStyle.width - preMargin - nextMargin : normalStyle.width
176
+ const initHeight = typeof normalStyle?.height === 'number' ? normalStyle.height - preMargin - nextMargin : normalStyle.height
177
+ const dir = horizontal === false ? 'y' : 'x'
178
+ const pstep = dir === 'x' ? initWidth : initHeight
179
+ const initStep: number = isNaN(pstep) ? 0 : pstep
180
+ // 每个元素的宽度 or 高度,有固定值直接初始化无则0
181
+ const step = useSharedValue(initStep)
182
+ // 记录选中元素的索引值
183
+ const currentIndex = useSharedValue(props.current || 0)
184
+ // const initOffset = getOffset(props.current || 0, initStep)
185
+ // 记录元素的偏移量
186
+ const offset = useSharedValue(getOffset(props.current || 0, initStep))
187
+ const strAbso = 'absolute' + dir.toUpperCase() as StrAbsoType
188
+ // 标识手指触摸和抬起, 起点在onBegin
189
+ const touchfinish = useSharedValue(true)
190
+ // 记录上一帧的绝对定位坐标
191
+ const preAbsolutePos = useSharedValue(0)
192
+ // 记录从onBegin 到 onTouchesUp 时移动的距离
193
+ const moveTranstion = useSharedValue(0)
194
+ // 记录从onBegin 到 onTouchesUp 的时间
195
+ const moveTime = useSharedValue(0)
196
+ const timerId = useRef(0 as number | ReturnType<typeof setTimeout>)
197
+ const intervalTimer = props.interval || 500
198
+ const {
199
+ // 存储layout布局信息
200
+ layoutRef,
201
+ layoutProps,
202
+ layoutStyle
203
+ } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout })
204
+ const innerProps = useInnerProps(props, {
205
+ ref: nodeRef
206
+ }, [
207
+ 'style',
208
+ 'indicator-dots',
209
+ 'indicator-color',
210
+ 'indicator-active-color',
211
+ 'previous-margin',
212
+ 'vertical',
213
+ 'previous-margin',
214
+ 'next-margin',
215
+ 'easing-function',
216
+ 'autoplay',
217
+ 'circular',
218
+ 'interval',
219
+ 'easing-function'
220
+ ], { layoutRef: layoutRef })
221
+
222
+ function onWrapperLayout (e: LayoutChangeEvent) {
223
+ const { width, height } = e.nativeEvent.layout
224
+ const realWidth = dir === 'x' ? width - preMargin - nextMargin : width
225
+ const realHeight = dir === 'y' ? height - preMargin - nextMargin : height
226
+ const iStep = dir === 'x' ? realWidth : realHeight
227
+ if (iStep !== step.value) {
228
+ step.value = iStep
229
+ updateCurrent(props.current || 0, iStep)
230
+ updateAutoplay()
231
+ }
232
+ }
233
+
234
+ const dotAnimatedStyle = useAnimatedStyle(() => {
235
+ if (!step.value) return {}
236
+ const dotStep = dotCommonStyle.width + dotCommonStyle.marginRight + dotCommonStyle.marginLeft
237
+ if (dir === 'x') {
238
+ return { transform: [{ translateX: currentIndex.value * dotStep }] }
239
+ } else {
240
+ return { transform: [{ translateY: currentIndex.value * dotStep }] }
241
+ }
242
+ })
243
+
244
+ function renderPagination () {
245
+ if (children.length <= 1) return null
246
+ const activeColor = activeDotColor || '#007aff'
247
+ const unActionColor = dotColor || 'rgba(0,0,0,.2)'
248
+ // 正常渲染所有dots
249
+ const dots: Array<ReactNode> = []
250
+ for (let i = 0; i < children.length; i++) {
251
+ dots.push(<View style={[dotCommonStyle, { backgroundColor: unActionColor }]} key={i}></View>)
252
+ }
253
+ return (
254
+ <View pointerEvents="none" style = {styles['pagination_' + dir]}>
255
+ <View style = {[styles['pagerWrapper' + dir]]}>
256
+ <Animated.View style={[
257
+ dotCommonStyle,
258
+ activeDotStyle,
259
+ {
260
+ backgroundColor: activeColor,
261
+ position: 'absolute',
262
+ left: 0,
263
+ top: 0
264
+ },
265
+ dotAnimatedStyle
266
+ ]}
267
+ />
268
+ {dots}
269
+ </View>
270
+ </View>)
271
+ }
272
+
273
+ function renderItems () {
274
+ const intLen = children.length
275
+ let renderChild = children.slice()
276
+ if (circular && intLen > 1) {
277
+ // 最前面加最后一个元素
278
+ const lastChild = React.cloneElement(children[intLen - 1] as ReactElement, { key: 'clone0' })
279
+ // 最后面加第一个元素
280
+ const firstChild = React.cloneElement(children[0] as ReactElement, { key: 'clone1' })
281
+ if (preMargin) {
282
+ const lastChild1 = React.cloneElement(children[intLen - 2] as ReactElement, { key: 'clone2' })
283
+ const firstChild1 = React.cloneElement(children[1] as ReactElement, { key: 'clone3' })
284
+ renderChild = [lastChild1, lastChild].concat(renderChild).concat([firstChild, firstChild1])
285
+ } else {
286
+ renderChild = [lastChild].concat(renderChild).concat([firstChild])
287
+ }
288
+ }
289
+ const arrChildren = renderChild.map((child, index) => {
290
+ const extraStyle = {} as { [key: string]: any }
291
+ if (index === 0 && !circular) {
292
+ preMargin && dir === 'x' && (extraStyle.marginLeft = preMargin)
293
+ preMargin && dir === 'y' && (extraStyle.marginTop = preMargin)
294
+ }
295
+ if (index === intLen - 1 && !circular) {
296
+ nextMargin && dir === 'x' && (extraStyle.marginRight = nextMargin)
297
+ nextMargin && dir === 'y' && (extraStyle.marginBottom = nextMargin)
298
+ }
299
+ // 业务swiper-item自己生成key,内部添加的元素自定义key
300
+ const newChild = React.cloneElement(child, {
301
+ itemIndex: index,
302
+ customStyle: extraStyle
303
+ })
304
+ return newChild
305
+ })
306
+ const contextValue = {
307
+ offset,
308
+ step,
309
+ scale: props.scale,
310
+ dir
311
+ }
312
+ return (<SwiperContext.Provider value={contextValue}>{arrChildren}</SwiperContext.Provider>)
313
+ }
314
+
315
+ const { loop, pauseLoop, resumeLoop } = useMemo(() => {
316
+ function createAutoPlay () {
317
+ if (!step.value) return
318
+ let targetOffset = 0
319
+ let nextIndex = currentIndex.value
320
+ if (!circularShared.value) {
321
+ // 获取下一个位置的坐标, 循环到最后一个元素,直接停止, 取消定时器
322
+ if (currentIndex.value === childrenLength.value - 1) {
323
+ pauseLoop()
324
+ return
325
+ }
326
+ nextIndex += 1
327
+ // targetOffset = -nextIndex * step.value - preMarginShared.value
328
+ targetOffset = -nextIndex * step.value
329
+ offset.value = withTiming(targetOffset, {
330
+ duration: easeDuration,
331
+ easing: easeMap[easeingFunc]
332
+ }, () => {
333
+ currentIndex.value = nextIndex
334
+ runOnJS(loop)()
335
+ })
336
+ } else {
337
+ // 默认向右, 向下
338
+ if (nextIndex === childrenLength.value - 1) {
339
+ nextIndex = 0
340
+ targetOffset = -(childrenLength.value + patchElmNumShared.value) * step.value + preMarginShared.value
341
+ // 执行动画到下一帧
342
+ offset.value = withTiming(targetOffset, {
343
+ duration: easeDuration
344
+ }, () => {
345
+ const initOffset = -step.value * patchElmNumShared.value + preMarginShared.value
346
+ // 将开始位置设置为真正的位置
347
+ offset.value = initOffset
348
+ currentIndex.value = nextIndex
349
+ runOnJS(loop)()
350
+ })
351
+ } else {
352
+ nextIndex = currentIndex.value + 1
353
+ targetOffset = -(nextIndex + patchElmNumShared.value) * step.value + preMarginShared.value
354
+ // 执行动画到下一帧
355
+ offset.value = withTiming(targetOffset, {
356
+ duration: easeDuration,
357
+ easing: easeMap[easeingFunc]
358
+ }, () => {
359
+ currentIndex.value = nextIndex
360
+ runOnJS(loop)()
361
+ })
362
+ }
363
+ }
364
+ }
365
+
366
+ // loop在JS线程中调用,createAutoPlay + useEffect中
367
+ function loop () {
368
+ timerId.current && clearTimeout(timerId.current)
369
+ timerId.current = setTimeout(createAutoPlay, intervalTimer)
370
+ }
371
+
372
+ function pauseLoop () {
373
+ timerId.current && clearTimeout(timerId.current)
374
+ }
375
+ // resumeLoop在worklet中调用
376
+ function resumeLoop () {
377
+ if (autoplayShared.value && childrenLength.value > 1) {
378
+ loop()
379
+ }
380
+ }
381
+ return {
382
+ loop,
383
+ pauseLoop,
384
+ resumeLoop
385
+ }
386
+ }, [])
387
+
388
+ function handleSwiperChange (current: number) {
389
+ if (props.current !== currentIndex.value) {
390
+ const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef })
391
+ props.bindchange && props.bindchange(eventData)
392
+ }
393
+ }
394
+
395
+ function getOffset (index:number, stepValue: number) {
396
+ if (!stepValue) return 0
397
+ let targetOffset = 0
398
+ if (circular && children.length > 1) {
399
+ const targetIndex = index + patchElmNum
400
+ targetOffset = -(stepValue * targetIndex - preMargin)
401
+ } else {
402
+ targetOffset = -index * stepValue
403
+ }
404
+ return targetOffset
405
+ }
406
+
407
+ function updateCurrent (index:number, stepValue: number) {
408
+ const targetOffset = getOffset(index || 0, stepValue)
409
+ if (targetOffset !== offset.value) {
410
+ // 内部基于props.current!==currentIndex.value决定是否使用动画及更新currentIndex.value
411
+ if (props.current !== undefined && props.current !== currentIndex.value) {
412
+ offset.value = withTiming(targetOffset, {
413
+ duration: easeDuration,
414
+ easing: easeMap[easeingFunc]
415
+ }, () => {
416
+ currentIndex.value = props.current || 0
417
+ })
418
+ } else {
419
+ offset.value = targetOffset
420
+ }
421
+ }
422
+ }
423
+ function updateAutoplay () {
424
+ if (autoplay && children.length > 1) {
425
+ loop()
426
+ } else {
427
+ pauseLoop()
428
+ }
429
+ }
430
+ // 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画
431
+ useAnimatedReaction(() => currentIndex.value, (newIndex: number, preIndex: number) => {
432
+ // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
433
+ if (newIndex !== preIndex && props.bindchange) {
434
+ runOnJS(handleSwiperChange)(newIndex)
435
+ }
436
+ })
437
+
438
+ useEffect(() => {
439
+ let patchStep = 0
440
+ if (preMargin !== preMarginShared.value) {
441
+ patchStep += preMargin - preMarginShared.value
442
+ }
443
+ if (nextMargin !== nextMarginShared.value) {
444
+ patchStep += nextMargin - nextMarginShared.value
445
+ }
446
+ preMarginShared.value = preMargin
447
+ nextMarginShared.value = nextMargin
448
+ const newStep = step.value - patchStep
449
+ if (step.value !== newStep) {
450
+ step.value = newStep
451
+ offset.value = getOffset(currentIndex.value, newStep)
452
+ }
453
+ }, [preMargin, nextMargin])
454
+
455
+ useEffect(() => {
456
+ childrenLength.value = children.length
457
+ if (children.length - 1 < currentIndex.value) {
458
+ pauseLoop()
459
+ currentIndex.value = 0
460
+ offset.value = getOffset(0, step.value)
461
+ if (autoplay && children.length > 1) {
462
+ loop()
463
+ }
464
+ }
465
+ }, [children.length])
466
+
467
+ useEffect(() => {
468
+ updateCurrent(props.current || 0, step.value)
469
+ }, [props.current])
470
+
471
+ useEffect(() => {
472
+ autoplayShared.value = autoplay
473
+ updateAutoplay()
474
+ return () => {
475
+ if (autoplay) {
476
+ pauseLoop()
477
+ }
478
+ }
479
+ }, [autoplay])
480
+
481
+ useEffect(() => {
482
+ if (circular !== circularShared.value) {
483
+ circularShared.value = circular
484
+ patchElmNumShared.value = circular ? (preMargin ? 2 : 1) : 0
485
+ offset.value = getOffset(currentIndex.value, step.value)
486
+ }
487
+ }, [circular, preMargin])
488
+
489
+ const { gestureHandler } = useMemo(() => {
490
+ function getTargetPosition (eventData: EventDataType) {
491
+ 'worklet'
492
+ // 移动的距离
493
+ const { translation } = eventData
494
+ let resetOffsetPos = 0
495
+ let selectedIndex = currentIndex.value
496
+ // 是否临界点
497
+ let isCriticalItem = false
498
+ // 真实滚动到的偏移量坐标
499
+ let moveToTargetPos = 0
500
+ const currentOffset = translation < 0 ? offset.value - preMarginShared.value : offset.value + preMarginShared.value
501
+ const computedIndex = Math.abs(currentOffset) / step.value
502
+ const moveToIndex = translation < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex)
503
+ // 实际应该定位的索引值
504
+ if (!circularShared.value) {
505
+ selectedIndex = moveToIndex
506
+ moveToTargetPos = selectedIndex * step.value
507
+ } else {
508
+ if (moveToIndex >= childrenLength.value + patchElmNumShared.value) {
509
+ selectedIndex = moveToIndex - (childrenLength.value + patchElmNumShared.value)
510
+ resetOffsetPos = (selectedIndex + patchElmNumShared.value) * step.value - preMarginShared.value
511
+ moveToTargetPos = moveToIndex * step.value - preMarginShared.value
512
+ isCriticalItem = true
513
+ } else if (moveToIndex <= patchElmNumShared.value - 1) {
514
+ selectedIndex = moveToIndex === 0 ? childrenLength.value - patchElmNumShared.value : childrenLength.value - 1
515
+ resetOffsetPos = (selectedIndex + patchElmNumShared.value) * step.value - preMarginShared.value
516
+ moveToTargetPos = moveToIndex * step.value - preMarginShared.value
517
+ isCriticalItem = true
518
+ } else {
519
+ selectedIndex = moveToIndex - patchElmNumShared.value
520
+ moveToTargetPos = moveToIndex * step.value - preMarginShared.value
521
+ }
522
+ }
523
+ return {
524
+ selectedIndex,
525
+ isCriticalItem,
526
+ resetOffset: -resetOffsetPos,
527
+ targetOffset: -moveToTargetPos
528
+ }
529
+ }
530
+ function canMove (eventData: EventDataType) {
531
+ 'worklet'
532
+ const { translation } = eventData
533
+ const currentOffset = Math.abs(offset.value)
534
+ if (!circularShared.value) {
535
+ if (translation < 0) {
536
+ return currentOffset < step.value * (childrenLength.value - 1)
537
+ } else {
538
+ return currentOffset > 0
539
+ }
540
+ } else {
541
+ return true
542
+ }
543
+ }
544
+ function handleEnd (eventData: EventDataType) {
545
+ 'worklet'
546
+ const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(eventData)
547
+ if (isCriticalItem) {
548
+ offset.value = withTiming(targetOffset, {
549
+ duration: easeDuration,
550
+ easing: easeMap[easeingFunc]
551
+ }, () => {
552
+ if (touchfinish.value !== false) {
553
+ currentIndex.value = selectedIndex
554
+ offset.value = resetOffset
555
+ runOnJS(resumeLoop)()
556
+ }
557
+ })
558
+ } else {
559
+ offset.value = withTiming(targetOffset, {
560
+ duration: easeDuration,
561
+ easing: easeMap[easeingFunc]
562
+ }, () => {
563
+ if (touchfinish.value !== false) {
564
+ currentIndex.value = selectedIndex
565
+ runOnJS(resumeLoop)()
566
+ }
567
+ })
568
+ }
569
+ }
570
+ function handleBack (eventData: EventDataType) {
571
+ 'worklet'
572
+ const { translation } = eventData
573
+ // 向右滑动的back:trans < 0, 向左滑动的back: trans < 0
574
+ let currentOffset = Math.abs(offset.value)
575
+ if (circularShared.value) {
576
+ currentOffset += translation < 0 ? preMarginShared.value : -preMarginShared.value
577
+ }
578
+ const curIndex = currentOffset / step.value
579
+ const moveToIndex = (translation < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElmNumShared.value
580
+ const targetOffset = -(moveToIndex + patchElmNumShared.value) * step.value + (circularShared.value ? preMarginShared.value : 0)
581
+ offset.value = withTiming(targetOffset, {
582
+ duration: easeDuration,
583
+ easing: easeMap[easeingFunc]
584
+ }, () => {
585
+ if (touchfinish.value !== false) {
586
+ currentIndex.value = moveToIndex
587
+ runOnJS(resumeLoop)()
588
+ }
589
+ })
590
+ }
591
+ function handleLongPress () {
592
+ 'worklet'
593
+ const currentOffset = Math.abs(offset.value)
594
+ let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value
595
+ if (circularShared.value) {
596
+ preOffset -= preMarginShared.value
597
+ }
598
+ // 正常事件中拿到的transition值(正向滑动<0,倒着滑>0)
599
+ const diffOffset = preOffset - currentOffset
600
+ const half = Math.abs(diffOffset) > step.value / 2
601
+ if (+diffOffset === 0) {
602
+ runOnJS(resumeLoop)()
603
+ } else if (half) {
604
+ handleEnd({ translation: diffOffset })
605
+ } else {
606
+ handleBack({ translation: diffOffset })
607
+ }
608
+ }
609
+ function reachBoundary (eventData: EventDataType) {
610
+ 'worklet'
611
+ // 移动的距离
612
+ const { translation } = eventData
613
+ const elementsLength = step.value * childrenLength.value
614
+ let isBoundary = false
615
+ let resetOffset = 0
616
+ // Y轴向下滚动, transDistance > 0, 向上滚动 < 0 X轴向左滚动, transDistance > 0
617
+ const currentOffset = offset.value
618
+ const moveStep = Math.ceil(translation / elementsLength)
619
+ if (translation < 0) {
620
+ const posEnd = (childrenLength.value + patchElmNumShared.value + 1) * step.value
621
+ const posReverseEnd = (patchElmNumShared.value - 1) * step.value
622
+ if (currentOffset < -posEnd + step.value) {
623
+ isBoundary = true
624
+ resetOffset = Math.abs(moveStep) === 0 ? patchElmNumShared.value * step.value + translation : moveStep * elementsLength
625
+ }
626
+ if (currentOffset > -posReverseEnd) {
627
+ isBoundary = true
628
+ resetOffset = moveStep * elementsLength
629
+ }
630
+ } else if (translation > 0) {
631
+ const posEnd = (patchElmNumShared.value - 1) * step.value
632
+ const posReverseEnd = (patchElmNumShared.value + childrenLength.value) * step.value
633
+ if (currentOffset > -posEnd) {
634
+ isBoundary = true
635
+ resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? translation : 0)
636
+ }
637
+ if (currentOffset < -posReverseEnd) {
638
+ isBoundary = true
639
+ resetOffset = moveStep * elementsLength + patchElmNumShared.value * step.value
640
+ }
641
+ }
642
+ return {
643
+ isBoundary,
644
+ resetOffset: -resetOffset
645
+ }
646
+ }
647
+ const gesturePan = Gesture.Pan()
648
+ .onBegin((e) => {
649
+ 'worklet'
650
+ if (!step.value) return
651
+ touchfinish.value = false
652
+ cancelAnimation(offset)
653
+ runOnJS(pauseLoop)()
654
+ preAbsolutePos.value = e[strAbso]
655
+ moveTranstion.value = e[strAbso]
656
+ moveTime.value = new Date().getTime()
657
+ })
658
+ .onTouchesMove((e) => {
659
+ 'worklet'
660
+ if (touchfinish.value) return
661
+ const touchEventData = e.changedTouches[0]
662
+ const moveDistance = touchEventData[strAbso] - preAbsolutePos.value
663
+ const eventData = {
664
+ translation: moveDistance
665
+ }
666
+ // 处理用户一直拖拽到临界点的场景, 不会执行onEnd
667
+ if (!circularShared.value && !canMove(eventData)) {
668
+ return
669
+ }
670
+ const { isBoundary, resetOffset } = reachBoundary(eventData)
671
+ if (isBoundary && circularShared.value) {
672
+ offset.value = resetOffset
673
+ } else {
674
+ offset.value = moveDistance + offset.value
675
+ }
676
+ preAbsolutePos.value = touchEventData[strAbso]
677
+ })
678
+ .onTouchesUp((e) => {
679
+ 'worklet'
680
+ if (touchfinish.value) return
681
+ const touchEventData = e.changedTouches[0]
682
+ const moveDistance = touchEventData[strAbso] - moveTranstion.value
683
+ touchfinish.value = true
684
+ const eventData = {
685
+ translation: moveDistance
686
+ }
687
+ // 用户手指按下起来, 需要计算正确的位置, 比如在滑动过程中突然按下然后起来,需要计算到正确的位置
688
+ if (!circularShared.value && !canMove(eventData)) {
689
+ return
690
+ }
691
+ const strVelocity = moveDistance / (new Date().getTime() - moveTime.value) * 1000
692
+ if (Math.abs(strVelocity) < longPressRatio) {
693
+ handleLongPress()
694
+ } else {
695
+ handleEnd(eventData)
696
+ }
697
+ })
698
+ return {
699
+ gestureHandler: gesturePan
700
+ }
701
+ }, [])
702
+
703
+ const animatedStyles = useAnimatedStyle(() => {
704
+ if (dir === 'x') {
705
+ return { transform: [{ translateX: offset.value }], opacity: step.value > 0 ? 1 : 0 }
706
+ } else {
707
+ return { transform: [{ translateY: offset.value }], opacity: step.value > 0 ? 1 : 0 }
708
+ }
709
+ })
710
+
711
+ function renderSwiper () {
712
+ const arrPages: Array<ReactNode> | ReactNode = renderItems()
713
+ return (<View style={[normalStyle, layoutStyle, styles.swiper]} {...layoutProps} {...innerProps}>
714
+ <Animated.View style={[{
715
+ flexDirection: dir === 'x' ? 'row' : 'column',
716
+ width: '100%',
717
+ height: '100%'
718
+ }, animatedStyles]}>
719
+ {wrapChildren({
720
+ children: arrPages
721
+ }, {
722
+ hasVarDec,
723
+ varContext: varContextRef.current,
724
+ textStyle,
725
+ textProps
726
+ })}
727
+ </Animated.View>
728
+ {showsPagination && renderPagination()}
729
+ </View>)
730
+ }
731
+
732
+ if (children.length === 1) {
733
+ return renderSwiper()
734
+ } else {
735
+ return (<GestureDetector gesture={gestureHandler}>
736
+ {renderSwiper()}
737
+ </GestureDetector>)
738
+ }
739
+ })
740
+ SwiperWrapper.displayName = 'MpxSwiperWrapper'
741
+
742
+ export default SwiperWrapper