@mpxjs/webpack-plugin 2.9.69-beta.4 → 2.9.69-beta.5

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.
@@ -1,8 +1,8 @@
1
1
  import { View, NativeSyntheticEvent, LayoutChangeEvent } from 'react-native'
2
2
  import { GestureDetector, Gesture } from 'react-native-gesture-handler'
3
- import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, runOnUI, useAnimatedReaction, cancelAnimation } from 'react-native-reanimated'
3
+ import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation } from 'react-native-reanimated'
4
4
 
5
- import React, { JSX, forwardRef, useRef, useEffect, ReactNode, ReactElement } from 'react'
5
+ import React, { JSX, forwardRef, useRef, useEffect, ReactNode, ReactElement, useCallback, useMemo } from 'react'
6
6
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
7
7
  import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数
8
8
  import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from './utils'
@@ -24,9 +24,7 @@ import { SwiperContext } from './context'
24
24
  * ✘ snap-to-edge
25
25
  */
26
26
  type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic'
27
- type StrTransType = 'translationX' | 'translationY'
28
27
  type StrAbsoType = 'absoluteX' | 'absoluteY'
29
- type StrVelocity = 'velocityX' | 'velocityY'
30
28
  type EventDataType = {
31
29
  translation: number
32
30
  }
@@ -136,18 +134,15 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
136
134
  'parent-width': parentWidth,
137
135
  'parent-height': parentHeight,
138
136
  'external-var-context': externalVarContext,
139
- style = {}
137
+ style = {},
138
+ autoplay,
139
+ circular
140
140
  } = props
141
- const previousMargin = props['previous-margin'] ? parseInt(props['previous-margin']) : 0
142
- const nextMargin = props['next-margin'] ? parseInt(props['next-margin']) : 0
143
141
  const easeingFunc = props['easing-function'] || 'default'
144
142
  const easeDuration = props.duration || 500
145
143
  const horizontal = props.vertical !== undefined ? !props.vertical : true
146
- // 默认前后补位的元素个数
147
- const patchElementNum = props.circular ? (previousMargin ? 2 : 1) : 0
148
144
  const nodeRef = useRef<View>(null)
149
145
  useNodesRef<View, SwiperProps>(props, ref, nodeRef, {})
150
-
151
146
  // 计算transfrom之类的
152
147
  const {
153
148
  normalStyle,
@@ -165,21 +160,31 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
165
160
  })
166
161
  const { textStyle } = splitStyle(normalStyle)
167
162
  const { textProps } = splitProps(props)
163
+ const preMargin = props['previous-margin'] ? parseInt(props['previous-margin']) : 0
164
+ const nextMargin = props['next-margin'] ? parseInt(props['next-margin']) : 0
165
+ const previousMarginShared = useSharedValue(preMargin)
166
+ const nextMarginShared = useSharedValue(nextMargin)
167
+ const autoplayShared = useSharedValue(autoplay || false)
168
+ // 默认前后补位的元素个数
169
+ const patchElmNum = circular ? (preMargin ? 2 : 1) : 0
170
+ const patchElmNumShared = useSharedValue(patchElmNum)
171
+ const circularShared = useSharedValue(circular || false)
168
172
  const children = Array.isArray(props.children) ? props.children.filter(child => child) : (props.children ? [props.children] : [])
169
- const initWidth = typeof normalStyle?.width === 'number' ? normalStyle.width - previousMargin - nextMargin : normalStyle.width
170
- const initHeight = typeof normalStyle?.height === 'number' ? normalStyle.height - previousMargin - nextMargin : normalStyle.height
171
- const dir = useSharedValue(horizontal === false ? 'y' : 'x')
172
- const pstep = dir.value === 'x' ? initWidth : initHeight
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
173
179
  const initStep: number = isNaN(pstep) ? 0 : pstep
174
- // 每个元素的宽度 or 高度
180
+ // 每个元素的宽度 or 高度,有固定值直接初始化无则0
175
181
  const step = useSharedValue(initStep)
176
- const totalElements = useSharedValue(children.length)
177
182
  // 记录选中元素的索引值
178
- const currentIndex = useSharedValue(0)
183
+ const currentIndex = useSharedValue(props.current || 0)
184
+ // const initOffset = getOffset(props.current || 0, initStep)
179
185
  // 记录元素的偏移量
180
- const offset = useSharedValue(0)
181
- const strAbso = 'absolute' + dir.value.toUpperCase() as StrAbsoType
182
- const arrPages: Array<ReactNode> | ReactNode = renderItems()
186
+ const offset = useSharedValue(getOffset(props.current || 0, initStep))
187
+ const strAbso = 'absolute' + dir.toUpperCase() as StrAbsoType
183
188
  // 标识手指触摸和抬起, 起点在onBegin
184
189
  const touchfinish = useSharedValue(true)
185
190
  // 记录上一帧的绝对定位坐标
@@ -189,17 +194,13 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
189
194
  // 记录从onBegin 到 onTouchesUp 的时间
190
195
  const moveTime = useSharedValue(0)
191
196
  const timerId = useRef(0 as number | ReturnType<typeof setTimeout>)
192
- // 用户点击未移动状态下,记录用户上一次操作的transtion 的 direction
193
- const customTrans = useSharedValue(0)
194
197
  const intervalTimer = props.interval || 500
195
- totalElements.value = children.length
196
198
  const {
197
199
  // 存储layout布局信息
198
200
  layoutRef,
199
201
  layoutProps,
200
202
  layoutStyle
201
203
  } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout })
202
-
203
204
  const innerProps = useInnerProps(props, {
204
205
  ref: nodeRef
205
206
  }, [
@@ -220,22 +221,20 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
220
221
 
221
222
  function onWrapperLayout (e: LayoutChangeEvent) {
222
223
  const { width, height } = e.nativeEvent.layout
223
- const realWidth = dir.value === 'x' ? width - previousMargin - nextMargin : width
224
- const realHeight = dir.value === 'y' ? height - previousMargin - nextMargin : height
225
- step.value = dir.value === 'x' ? realWidth : realHeight
226
- if (touchfinish.value) {
227
- runOnUI(() => {
228
- offset.value = getOffset(currentIndex.value)
229
- })()
230
- pauseLoop()
231
- resumeLoop()
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()
232
231
  }
233
232
  }
234
233
 
235
234
  const dotAnimatedStyle = useAnimatedStyle(() => {
236
235
  if (!step.value) return {}
237
236
  const dotStep = dotCommonStyle.width + dotCommonStyle.marginRight + dotCommonStyle.marginLeft
238
- if (dir.value === 'x') {
237
+ if (dir === 'x') {
239
238
  return { transform: [{ translateX: currentIndex.value * dotStep }] }
240
239
  } else {
241
240
  return { transform: [{ translateY: currentIndex.value * dotStep }] }
@@ -243,17 +242,17 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
243
242
  })
244
243
 
245
244
  function renderPagination () {
246
- if (totalElements.value <= 1) return null
245
+ if (children.length <= 1) return null
247
246
  const activeColor = activeDotColor || '#007aff'
248
247
  const unActionColor = dotColor || 'rgba(0,0,0,.2)'
249
248
  // 正常渲染所有dots
250
249
  const dots: Array<ReactNode> = []
251
- for (let i = 0; i < totalElements.value; i++) {
250
+ for (let i = 0; i < children.length; i++) {
252
251
  dots.push(<View style={[dotCommonStyle, { backgroundColor: unActionColor }]} key={i}></View>)
253
252
  }
254
253
  return (
255
- <View pointerEvents="none" style = {styles['pagination_' + dir.value]}>
256
- <View style = {[styles['pagerWrapper' + dir.value]]}>
254
+ <View pointerEvents="none" style = {styles['pagination_' + dir]}>
255
+ <View style = {[styles['pagerWrapper' + dir]]}>
257
256
  <Animated.View style={[
258
257
  dotCommonStyle,
259
258
  activeDotStyle,
@@ -272,85 +271,61 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
272
271
  }
273
272
 
274
273
  function renderItems () {
275
- const itemAnimatedStyles = useAnimatedStyle(() => {
276
- return dir.value === 'x' ? { width: step.value, height: '100%' } : { width: '100%', height: step.value }
277
- })
274
+ const intLen = children.length
278
275
  let renderChild = children.slice()
279
- if (props.circular && totalElements.value > 1) {
276
+ if (circular && intLen > 1) {
280
277
  // 最前面加最后一个元素
281
- const lastChild = React.cloneElement(children[totalElements.value - 1] as ReactElement)
278
+ const lastChild = React.cloneElement(children[intLen - 1] as ReactElement, { key: 'clone0' })
282
279
  // 最后面加第一个元素
283
- const firstChild = React.cloneElement(children[0] as ReactElement)
284
- if (previousMargin) {
285
- const lastChild1 = React.cloneElement(children[totalElements.value - 2] as ReactElement)
286
- const firstChild1 = React.cloneElement(children[1] as ReactElement)
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' })
287
284
  renderChild = [lastChild1, lastChild].concat(renderChild).concat([firstChild, firstChild1])
288
285
  } else {
289
286
  renderChild = [lastChild].concat(renderChild).concat([firstChild])
290
287
  }
291
288
  }
292
- const arrChilds = renderChild.map((child, index) => {
289
+ const arrChildren = renderChild.map((child, index) => {
293
290
  const extraStyle = {} as { [key: string]: any }
294
- if (index === 0 && !props.circular) {
295
- previousMargin && dir.value === 'x' && (extraStyle.marginLeft = previousMargin)
296
- previousMargin && dir.value === 'y' && (extraStyle.marginTop = previousMargin)
291
+ if (index === 0 && !circular) {
292
+ preMargin && dir === 'x' && (extraStyle.marginLeft = preMargin)
293
+ preMargin && dir === 'y' && (extraStyle.marginTop = preMargin)
297
294
  }
298
- if (index === totalElements.value - 1 && !props.circular) {
299
- nextMargin && dir.value === 'x' && (extraStyle.marginRight = nextMargin)
300
- nextMargin && dir.value === 'y' && (extraStyle.marginBottom = nextMargin)
295
+ if (index === intLen - 1 && !circular) {
296
+ nextMargin && dir === 'x' && (extraStyle.marginRight = nextMargin)
297
+ nextMargin && dir === 'y' && (extraStyle.marginBottom = nextMargin)
301
298
  }
299
+ // 业务swiper-item自己生成key,内部添加的元素自定义key
302
300
  const newChild = React.cloneElement(child, {
303
301
  itemIndex: index,
304
- customStyle: [itemAnimatedStyles, extraStyle],
305
- key: 'page' + index
302
+ customStyle: extraStyle
306
303
  })
307
304
  return newChild
308
305
  })
309
306
  const contextValue = {
310
307
  offset,
311
308
  step,
312
- scale: props.scale
309
+ scale: props.scale,
310
+ dir
313
311
  }
314
- return (<SwiperContext.Provider value={contextValue}>{arrChilds}</SwiperContext.Provider>)
312
+ return (<SwiperContext.Provider value={contextValue}>{arrChildren}</SwiperContext.Provider>)
315
313
  }
316
314
 
317
- function createAutoPlay () {
318
- let targetOffset = 0
319
- let nextIndex = currentIndex.value
320
- if (!props.circular) {
321
- // 获取下一个位置的坐标, 循环到最后一个元素,直接停止, 取消定时器
322
- if (currentIndex.value === totalElements.value - 1) {
323
- pauseLoop()
324
- return
325
- }
326
- nextIndex += 1
327
- targetOffset = -nextIndex * step.value - previousMargin
328
- offset.value = withTiming(targetOffset, {
329
- duration: easeDuration,
330
- easing: easeMap[easeingFunc]
331
- }, () => {
332
- currentIndex.value = nextIndex
333
- runOnJS(loop)()
334
- })
335
- } else {
336
- // 默认向右, 向下
337
- if (nextIndex === totalElements.value - 1) {
338
- nextIndex = 0
339
- targetOffset = -(totalElements.value + patchElementNum) * step.value + previousMargin
340
- // 执行动画到下一帧
341
- offset.value = withTiming(targetOffset, {
342
- duration: easeDuration
343
- }, () => {
344
- const initOffset = -step.value * patchElementNum + previousMargin
345
- // 将开始位置设置为真正的位置
346
- offset.value = initOffset
347
- currentIndex.value = nextIndex
348
- runOnJS(loop)()
349
- })
350
- } else {
351
- nextIndex = currentIndex.value + 1
352
- targetOffset = -(nextIndex + patchElementNum) * step.value + previousMargin
353
- // 执行动画到下一帧
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 - previousMarginShared.value
328
+ targetOffset = -nextIndex * step.value
354
329
  offset.value = withTiming(targetOffset, {
355
330
  duration: easeDuration,
356
331
  easing: easeMap[easeingFunc]
@@ -358,9 +333,57 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
358
333
  currentIndex.value = nextIndex
359
334
  runOnJS(loop)()
360
335
  })
336
+ } else {
337
+ // 默认向右, 向下
338
+ if (nextIndex === childrenLength.value - 1) {
339
+ nextIndex = 0
340
+ targetOffset = -(childrenLength.value + patchElmNumShared.value) * step.value + previousMarginShared.value
341
+ // 执行动画到下一帧
342
+ offset.value = withTiming(targetOffset, {
343
+ duration: easeDuration
344
+ }, () => {
345
+ const initOffset = -step.value * patchElmNumShared.value + previousMarginShared.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 + previousMarginShared.value
354
+ // 执行动画到下一帧
355
+ offset.value = withTiming(targetOffset, {
356
+ duration: easeDuration,
357
+ easing: easeMap[easeingFunc]
358
+ }, () => {
359
+ currentIndex.value = nextIndex
360
+ runOnJS(loop)()
361
+ })
362
+ }
361
363
  }
362
364
  }
363
- }
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
+ }, [])
364
387
 
365
388
  function handleSwiperChange (current: number) {
366
389
  if (props.current !== currentIndex.value) {
@@ -369,294 +392,317 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
369
392
  }
370
393
  }
371
394
 
372
- function getOffset (index?: number) {
373
- 'worklet'
374
- if (!step.value) return 0
395
+ function getOffset (index:number, stepValue: number) {
396
+ if (!stepValue) return 0
375
397
  let targetOffset = 0
376
- if (props.circular && totalElements.value > 1) {
377
- const targetIndex = (index || props.current || 0) + patchElementNum
378
- targetOffset = -(step.value * targetIndex - previousMargin)
398
+ if (circular && children.length > 1) {
399
+ const targetIndex = index + patchElmNum
400
+ targetOffset = -(stepValue * targetIndex - preMargin)
379
401
  } else {
380
- targetOffset = -(index || props?.current || 0) * step.value
402
+ targetOffset = -index * stepValue
381
403
  }
382
404
  return targetOffset
383
405
  }
384
406
 
385
- function loop () {
386
- timerId.current = setTimeout(() => {
387
- createAutoPlay()
388
- }, intervalTimer)
389
- }
390
-
391
- function pauseLoop () {
392
- timerId.current && clearTimeout(timerId.current)
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
+ }
393
422
  }
394
-
395
- function resumeLoop () {
396
- if (props.autoplay) {
423
+ function updateAutoplay () {
424
+ if (autoplay && children.length > 1) {
397
425
  loop()
426
+ } else {
427
+ pauseLoop()
398
428
  }
399
429
  }
400
-
401
- useAnimatedReaction(() => currentIndex.value, (newIndex, preIndex) => {
430
+ // 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画
431
+ useAnimatedReaction(() => currentIndex.value, (newIndex: number, preIndex: number) => {
402
432
  // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
403
- const isInit = !preIndex && newIndex === 0
404
- if (!isInit && newIndex !== preIndex && props.bindchange) {
433
+ if (newIndex !== preIndex && props.bindchange) {
405
434
  runOnJS(handleSwiperChange)(newIndex)
406
435
  }
407
436
  })
437
+
408
438
  useEffect(() => {
409
- if (!step.value) {
410
- return
439
+ let patchStep = 0
440
+ if (preMargin !== previousMarginShared.value) {
441
+ patchStep += preMargin - previousMarginShared.value
411
442
  }
412
- const targetOffset = getOffset()
413
- if (props.current !== undefined && props.current !== currentIndex.value) {
414
- offset.value = withTiming(targetOffset, {
415
- duration: easeDuration,
416
- easing: easeMap[easeingFunc]
417
- }, () => {
418
- currentIndex.value = props.current || 0
419
- })
443
+ if (nextMargin !== nextMarginShared.value) {
444
+ patchStep += nextMargin - nextMarginShared.value
420
445
  }
421
- }, [props.current])
446
+ previousMarginShared.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])
422
454
 
423
455
  useEffect(() => {
424
- if (!step.value) {
425
- return
426
- }
427
- if (props.autoplay) {
428
- resumeLoop()
429
- } else {
456
+ childrenLength.value = children.length
457
+ if (children.length - 1 < currentIndex.value) {
430
458
  pauseLoop()
459
+ currentIndex.value = 0
460
+ offset.value = getOffset(0, step.value)
461
+ if (autoplay && children.length > 1) {
462
+ loop()
463
+ }
431
464
  }
465
+ }, [children.length])
466
+
467
+ useEffect(() => {
468
+ updateCurrent(props.current || 0, step.value)
469
+ }, [props.current])
470
+
471
+ useEffect(() => {
472
+ autoplayShared.value = autoplay || false
473
+ updateAutoplay()
432
474
  return () => {
433
- if (props.autoplay) {
475
+ if (autoplay) {
434
476
  pauseLoop()
435
477
  }
436
478
  }
437
- }, [props.autoplay])
479
+ }, [autoplay])
438
480
 
439
- function getTargetPosition (eventData: EventDataType) {
440
- 'worklet'
441
- // 移动的距离
442
- const { translation } = eventData
443
- let resetOffsetPos = 0
444
- let selectedIndex = currentIndex.value
445
- // 是否临界点
446
- let isCriticalItem = false
447
- // 真实滚动到的偏移量坐标
448
- let moveToTargetPos = 0
449
- const currentOffset = translation < 0 ? offset.value - previousMargin : offset.value + previousMargin
450
- const computedIndex = Math.abs(currentOffset) / step.value
451
- const moveToIndex = translation < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex)
452
- // 实际应该定位的索引值
453
- if (!props.circular) {
454
- selectedIndex = moveToIndex
455
- moveToTargetPos = selectedIndex * step.value
456
- } else {
457
- if (moveToIndex >= totalElements.value + patchElementNum) {
458
- selectedIndex = moveToIndex - (totalElements.value + patchElementNum)
459
- resetOffsetPos = (selectedIndex + patchElementNum) * step.value - previousMargin
460
- moveToTargetPos = moveToIndex * step.value - previousMargin
461
- isCriticalItem = true
462
- } else if (moveToIndex <= patchElementNum - 1) {
463
- selectedIndex = moveToIndex === 0 ? totalElements.value - patchElementNum : totalElements.value - 1
464
- resetOffsetPos = (selectedIndex + patchElementNum) * step.value - previousMargin
465
- moveToTargetPos = moveToIndex * step.value - previousMargin
466
- isCriticalItem = true
481
+ useEffect(() => {
482
+ if (circular !== circularShared.value) {
483
+ circularShared.value = circular || false
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 - previousMarginShared.value : offset.value + previousMarginShared.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
467
507
  } else {
468
- selectedIndex = moveToIndex - patchElementNum
469
- moveToTargetPos = moveToIndex * step.value - previousMargin
508
+ if (moveToIndex >= childrenLength.value + patchElmNumShared.value) {
509
+ selectedIndex = moveToIndex - (childrenLength.value + patchElmNumShared.value)
510
+ resetOffsetPos = (selectedIndex + patchElmNumShared.value) * step.value - previousMarginShared.value
511
+ moveToTargetPos = moveToIndex * step.value - previousMarginShared.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 - previousMarginShared.value
516
+ moveToTargetPos = moveToIndex * step.value - previousMarginShared.value
517
+ isCriticalItem = true
518
+ } else {
519
+ selectedIndex = moveToIndex - patchElmNumShared.value
520
+ moveToTargetPos = moveToIndex * step.value - previousMarginShared.value
521
+ }
522
+ }
523
+ return {
524
+ selectedIndex,
525
+ isCriticalItem,
526
+ resetOffset: -resetOffsetPos,
527
+ targetOffset: -moveToTargetPos
470
528
  }
471
529
  }
472
- return {
473
- selectedIndex,
474
- isCriticalItem,
475
- resetOffset: -resetOffsetPos,
476
- targetOffset: -moveToTargetPos
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
+ }
477
543
  }
478
- }
479
-
480
- function canMove (eventData: EventDataType) {
481
- 'worklet'
482
- const { translation } = eventData
483
- const currentOffset = Math.abs(offset.value)
484
- if (!props.circular) {
485
- if (translation < 0) {
486
- return currentOffset < step.value * (totalElements.value - 1)
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
+ })
487
558
  } else {
488
- return currentOffset > 0
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
+ })
489
568
  }
490
- } else {
491
- return true
492
569
  }
493
- }
494
-
495
- function handleEnd (eventData: EventDataType) {
496
- 'worklet'
497
- const { isCriticalItem, targetOffset, resetOffset, selectedIndex } = getTargetPosition(eventData)
498
- if (isCriticalItem) {
499
- offset.value = withTiming(targetOffset, {
500
- duration: easeDuration,
501
- easing: easeMap[easeingFunc]
502
- }, () => {
503
- if (touchfinish.value !== false) {
504
- currentIndex.value = selectedIndex
505
- offset.value = resetOffset
506
- runOnJS(resumeLoop)()
507
- }
508
- })
509
- } else {
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 ? previousMarginShared.value : -previousMarginShared.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 ? previousMarginShared.value : 0)
510
581
  offset.value = withTiming(targetOffset, {
511
582
  duration: easeDuration,
512
583
  easing: easeMap[easeingFunc]
513
584
  }, () => {
514
585
  if (touchfinish.value !== false) {
515
- currentIndex.value = selectedIndex
586
+ currentIndex.value = moveToIndex
516
587
  runOnJS(resumeLoop)()
517
588
  }
518
589
  })
519
590
  }
520
- }
521
-
522
- function handleBack (eventData: EventDataType) {
523
- 'worklet'
524
- const { translation } = eventData
525
- // 向右滑动的back:trans < 0, 向左滑动的back: trans < 0
526
- let currentOffset = Math.abs(offset.value)
527
- if (props.circular) {
528
- currentOffset += translation < 0 ? previousMargin : -previousMargin
529
- }
530
- const curIndex = currentOffset / step.value
531
- const moveToIndex = (translation < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElementNum
532
- const targetOffset = -(moveToIndex + patchElementNum) * step.value + (props.circular ? -previousMargin : 0)
533
- offset.value = withTiming(targetOffset, {
534
- duration: easeDuration,
535
- easing: easeMap[easeingFunc]
536
- }, () => {
537
- if (touchfinish.value !== false) {
538
- currentIndex.value = moveToIndex
539
- runOnJS(resumeLoop)()
540
- }
541
- })
542
- }
543
-
544
- function handleLongPress (eventData: EventDataType) {
545
- 'worklet'
546
- const { translation } = eventData
547
- let currentOffset = Math.abs(offset.value)
548
- if (props.circular) {
549
- currentOffset += previousMargin
550
- }
551
-
552
- const half = currentOffset % step.value > step.value / 2
553
- // 向右trans < 0, 向左trans > 0
554
- const isExceedHalf = translation < 0 ? half : !half
555
- if (+translation === 0) {
556
- runOnJS(resumeLoop)()
557
- } else if (isExceedHalf) {
558
- handleEnd(eventData)
559
- } else {
560
- handleBack(eventData)
561
- }
562
- }
563
-
564
- function reachBoundary (eventData: EventDataType) {
565
- 'worklet'
566
- // 移动的距离
567
- const { translation } = eventData
568
- const elementsLength = step.value * totalElements.value
569
-
570
- let isBoundary = false
571
- let resetOffset = 0
572
- // Y轴向下滚动, transDistance > 0, 向上滚动 < 0 X轴向左滚动, transDistance > 0
573
- const currentOffset = offset.value
574
- const moveStep = Math.ceil(translation / elementsLength)
575
- if (translation < 0) {
576
- const posEnd = (totalElements.value + patchElementNum + 1) * step.value
577
- const posReverseEnd = (patchElementNum - 1) * step.value
578
- if (currentOffset < -posEnd + step.value) {
579
- isBoundary = true
580
- resetOffset = Math.abs(moveStep) === 0 ? patchElementNum * step.value + translation : moveStep * elementsLength
581
- }
582
- if (currentOffset > -posReverseEnd) {
583
- isBoundary = true
584
- resetOffset = moveStep * elementsLength
585
- }
586
- } else if (translation > 0) {
587
- const posEnd = (patchElementNum - 1) * step.value
588
- const posReverseEnd = (patchElementNum + totalElements.value) * step.value
589
- if (currentOffset > -posEnd) {
590
- isBoundary = true
591
- resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? translation : 0)
592
- }
593
- if (currentOffset < -posReverseEnd) {
594
- isBoundary = true
595
- resetOffset = moveStep * elementsLength + patchElementNum * step.value
596
- }
597
- }
598
- return {
599
- isBoundary,
600
- resetOffset: -resetOffset
601
- }
602
- }
603
-
604
- const gestureHandler = Gesture.Pan()
605
- .onBegin((e) => {
606
- 'worklet'
607
- touchfinish.value = false
608
- cancelAnimation(offset)
609
- runOnJS(pauseLoop)()
610
- preAbsolutePos.value = e[strAbso]
611
- moveTranstion.value = e[strAbso]
612
- moveTime.value = new Date().getTime()
613
- })
614
- .onTouchesMove((e) => {
591
+ function handleLongPress () {
615
592
  'worklet'
616
- const touchEventData = e.changedTouches[0]
617
- const moveDistance = touchEventData[strAbso] - preAbsolutePos.value
618
- customTrans.value = moveDistance
619
- const eventData = {
620
- translation: moveDistance
593
+ const currentOffset = Math.abs(offset.value)
594
+ let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value
595
+ if (circularShared.value) {
596
+ preOffset -= previousMarginShared.value
621
597
  }
622
- // 处理用户一直拖拽到临界点的场景, 不会执行onEnd
623
- if (!props.circular && !canMove(eventData)) {
624
- return
625
- }
626
- const { isBoundary, resetOffset } = reachBoundary(eventData)
627
- if (isBoundary && props.circular && !touchfinish.value) {
628
- offset.value = resetOffset
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 })
629
605
  } else {
630
- offset.value = moveDistance + offset.value
606
+ handleBack({ translation: diffOffset })
631
607
  }
632
- preAbsolutePos.value = touchEventData[strAbso]
633
- })
634
- .onTouchesUp((e) => {
608
+ }
609
+ function reachBoundary (eventData: EventDataType) {
635
610
  'worklet'
636
- const touchEventData = e.changedTouches[0]
637
- const moveDistance = touchEventData[strAbso] - moveTranstion.value
638
- touchfinish.value = true
639
- const eventData = {
640
- translation: moveDistance
611
+ // 移动的距离
612
+ const { translation } = eventData
613
+ const elementsLength = step.value * childrenLength.value
614
+
615
+ let isBoundary = false
616
+ let resetOffset = 0
617
+ // Y轴向下滚动, transDistance > 0, 向上滚动 < 0 X轴向左滚动, transDistance > 0
618
+ const currentOffset = offset.value
619
+ const moveStep = Math.ceil(translation / elementsLength)
620
+ if (translation < 0) {
621
+ const posEnd = (childrenLength.value + patchElmNumShared.value + 1) * step.value
622
+ const posReverseEnd = (patchElmNumShared.value - 1) * step.value
623
+ if (currentOffset < -posEnd + step.value) {
624
+ isBoundary = true
625
+ resetOffset = Math.abs(moveStep) === 0 ? patchElmNumShared.value * step.value + translation : moveStep * elementsLength
626
+ }
627
+ if (currentOffset > -posReverseEnd) {
628
+ isBoundary = true
629
+ resetOffset = moveStep * elementsLength
630
+ }
631
+ } else if (translation > 0) {
632
+ const posEnd = (patchElmNumShared.value - 1) * step.value
633
+ const posReverseEnd = (patchElmNumShared.value + childrenLength.value) * step.value
634
+ if (currentOffset > -posEnd) {
635
+ isBoundary = true
636
+ resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? translation : 0)
637
+ }
638
+ if (currentOffset < -posReverseEnd) {
639
+ isBoundary = true
640
+ resetOffset = moveStep * elementsLength + patchElmNumShared.value * step.value
641
+ }
641
642
  }
642
- // 用户手指按下起来, 需要计算正确的位置, 比如在滑动过程中突然按下然后起来,需要计算到正确的位置
643
- if (!props.circular && !canMove(eventData)) {
644
- return
643
+ return {
644
+ isBoundary,
645
+ resetOffset: -resetOffset
645
646
  }
646
- if (!moveDistance) {
647
- handleLongPress({ translation: customTrans.value })
648
- } else {
647
+ }
648
+ const gesturePan = Gesture.Pan()
649
+ .onBegin((e) => {
650
+ 'worklet'
651
+ if (!step.value) return
652
+ touchfinish.value = false
653
+ cancelAnimation(offset)
654
+ runOnJS(pauseLoop)()
655
+ preAbsolutePos.value = e[strAbso]
656
+ moveTranstion.value = e[strAbso]
657
+ moveTime.value = new Date().getTime()
658
+ })
659
+ .onTouchesMove((e) => {
660
+ 'worklet'
661
+ if (touchfinish.value) return
662
+ const touchEventData = e.changedTouches[0]
663
+ const moveDistance = touchEventData[strAbso] - preAbsolutePos.value
664
+ const eventData = {
665
+ translation: moveDistance
666
+ }
667
+ // 处理用户一直拖拽到临界点的场景, 不会执行onEnd
668
+ if (!circularShared.value && !canMove(eventData)) {
669
+ return
670
+ }
671
+ const { isBoundary, resetOffset } = reachBoundary(eventData)
672
+ if (isBoundary && circularShared.value) {
673
+ offset.value = resetOffset
674
+ } else {
675
+ offset.value = moveDistance + offset.value
676
+ }
677
+ preAbsolutePos.value = touchEventData[strAbso]
678
+ })
679
+ .onTouchesUp((e) => {
680
+ 'worklet'
681
+ if(touchfinish.value) return
682
+ const touchEventData = e.changedTouches[0]
683
+ const moveDistance = touchEventData[strAbso] - moveTranstion.value
684
+ touchfinish.value = true
685
+ const eventData = {
686
+ translation: moveDistance
687
+ }
688
+ // 用户手指按下起来, 需要计算正确的位置, 比如在滑动过程中突然按下然后起来,需要计算到正确的位置
689
+ if (!circularShared.value && !canMove(eventData)) {
690
+ return
691
+ }
649
692
  const strVelocity = moveDistance / (new Date().getTime() - moveTime.value) * 1000
650
693
  if (Math.abs(strVelocity) < longPressRatio) {
651
- handleLongPress(eventData)
694
+ handleLongPress()
652
695
  } else {
653
696
  handleEnd(eventData)
654
697
  }
655
- }
656
- })
698
+ })
699
+ return {
700
+ gestureHandler: gesturePan
701
+ }
702
+ }, [])
657
703
 
658
704
  const animatedStyles = useAnimatedStyle(() => {
659
- if (dir.value === 'x') {
705
+ if (dir === 'x') {
660
706
  return { transform: [{ translateX: offset.value }], opacity: step.value > 0 ? 1 : 0 }
661
707
  } else {
662
708
  return { transform: [{ translateY: offset.value }], opacity: step.value > 0 ? 1 : 0 }
@@ -664,9 +710,10 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
664
710
  })
665
711
 
666
712
  function renderSwiper () {
713
+ const arrPages: Array<ReactNode> | ReactNode = renderItems()
667
714
  return (<View style={[normalStyle, layoutStyle, styles.swiper]} {...layoutProps} {...innerProps}>
668
715
  <Animated.View style={[{
669
- flexDirection: dir.value === 'x' ? 'row' : 'column',
716
+ flexDirection: dir === 'x' ? 'row' : 'column',
670
717
  width: '100%',
671
718
  height: '100%'
672
719
  }, animatedStyles]}>
@@ -683,7 +730,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
683
730
  </View>)
684
731
  }
685
732
 
686
- if (totalElements.value === 1) {
733
+ if (children.length === 1) {
687
734
  return renderSwiper()
688
735
  } else {
689
736
  return (<GestureDetector gesture={gestureHandler}>