@mpxjs/webpack-plugin 2.10.3-beta.17 → 2.10.3-beta.18

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 (68) hide show
  1. package/lib/runtime/components/react/dist/context.js +5 -1
  2. package/lib/runtime/components/react/dist/event.config.js +0 -1
  3. package/lib/runtime/components/react/dist/getInnerListeners.js +148 -149
  4. package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +145 -0
  5. package/lib/runtime/components/react/dist/mpx-button.jsx +11 -7
  6. package/lib/runtime/components/react/dist/mpx-canvas/Image.js +2 -4
  7. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +23 -21
  8. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +9 -4
  9. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +9 -5
  10. package/lib/runtime/components/react/dist/mpx-form.jsx +2 -2
  11. package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
  12. package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
  13. package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
  14. package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
  15. package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
  16. package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
  17. package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
  18. package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
  19. package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
  20. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +9 -4
  21. package/lib/runtime/components/react/dist/mpx-image.jsx +92 -41
  22. package/lib/runtime/components/react/dist/mpx-inline-text.jsx +11 -0
  23. package/lib/runtime/components/react/dist/mpx-input.jsx +14 -13
  24. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +22 -7
  25. package/lib/runtime/components/react/dist/mpx-label.jsx +9 -5
  26. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +10 -5
  27. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +206 -80
  28. package/lib/runtime/components/react/dist/mpx-navigator.jsx +11 -3
  29. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +194 -68
  30. package/lib/runtime/components/react/dist/mpx-picker/dateData.js +17 -0
  31. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +178 -98
  32. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +79 -139
  33. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +190 -90
  34. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +60 -75
  35. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +100 -228
  36. package/lib/runtime/components/react/dist/{mpx-picker-view.jsx → mpx-picker-view/index.jsx} +16 -15
  37. package/lib/runtime/components/react/dist/{mpx-picker-view-column.jsx → mpx-picker-view-column/index.jsx} +95 -26
  38. package/lib/runtime/components/react/dist/{mpx-picker-view-column-item.jsx → mpx-picker-view-column/pickerViewColumnItem.jsx} +16 -16
  39. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItemLite.jsx +20 -0
  40. package/lib/runtime/components/react/dist/{pickerFaces.js → mpx-picker-view-column/pickerViewFaces.js} +6 -0
  41. package/lib/runtime/components/react/dist/mpx-popup/index.jsx +61 -0
  42. package/lib/runtime/components/react/dist/mpx-popup/popupBase.jsx +92 -0
  43. package/lib/runtime/components/react/dist/mpx-portal/index.jsx +5 -1
  44. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +3 -5
  45. package/lib/runtime/components/react/dist/mpx-progress.jsx +163 -0
  46. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +11 -4
  47. package/lib/runtime/components/react/dist/mpx-radio.jsx +9 -5
  48. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +12 -4
  49. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +317 -89
  50. package/lib/runtime/components/react/dist/mpx-simple-text.jsx +7 -5
  51. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +11 -15
  52. package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
  53. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +117 -0
  54. package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
  55. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +15 -14
  56. package/lib/runtime/components/react/dist/mpx-swiper.jsx +245 -121
  57. package/lib/runtime/components/react/dist/mpx-switch.jsx +10 -7
  58. package/lib/runtime/components/react/dist/mpx-text.jsx +43 -13
  59. package/lib/runtime/components/react/dist/mpx-video.jsx +12 -7
  60. package/lib/runtime/components/react/dist/mpx-view.jsx +34 -18
  61. package/lib/runtime/components/react/dist/mpx-web-view.jsx +40 -35
  62. package/lib/runtime/components/react/dist/useAnimationHooks.js +35 -90
  63. package/lib/runtime/components/react/dist/utils.jsx +215 -109
  64. package/lib/runtime/components/web/mpx-titlebar.vue +21 -18
  65. package/package.json +1 -1
  66. /package/lib/runtime/components/react/dist/{pickerVIewContext.js → mpx-picker-view/pickerVIewContext.js} +0 -0
  67. /package/lib/runtime/components/react/dist/{pickerViewIndicator.jsx → mpx-picker-view-column/pickerViewIndicator.jsx} +0 -0
  68. /package/lib/runtime/components/react/dist/{pickerViewMask.jsx → mpx-picker-view-column/pickerViewMask.jsx} +0 -0
@@ -1,11 +1,12 @@
1
1
  import { View } from 'react-native';
2
2
  import { GestureDetector, Gesture } from 'react-native-gesture-handler';
3
3
  import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation } from 'react-native-reanimated';
4
- import React, { forwardRef, useRef, useEffect, useMemo } from 'react';
4
+ import React, { forwardRef, useRef, useEffect, useMemo, createElement } from 'react';
5
5
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
6
6
  import useNodesRef from './useNodesRef'; // 引入辅助函数
7
- import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren } from './utils';
7
+ import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren, extendObject, flatGesture, useRunOnJSCallback } from './utils';
8
8
  import { SwiperContext } from './context';
9
+ import Portal from './mpx-portal';
9
10
  /**
10
11
  * 默认的Style类型
11
12
  */
@@ -70,14 +71,19 @@ const easeMap = {
70
71
  easeInOutCubic: Easing.inOut(Easing.cubic)
71
72
  };
72
73
  const SwiperWrapper = forwardRef((props, ref) => {
73
- 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 = {}, autoplay = false, circular = false, disableGesture = false } = props;
74
+ const { 'indicator-dots': showPagination, 'indicator-color': dotColor = 'rgba(0, 0, 0, .3)', 'indicator-active-color': activeDotColor = '#000000', 'enable-var': enableVar = false, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'external-var-context': externalVarContext, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, autoplay = false, circular = false, disableGesture = false, current: propCurrent = 0, bindchange } = props;
74
75
  const easeingFunc = props['easing-function'] || 'default';
75
76
  const easeDuration = props.duration || 500;
76
77
  const horizontal = props.vertical !== undefined ? !props.vertical : true;
77
78
  const nodeRef = useRef(null);
78
- useNodesRef(props, ref, nodeRef, {});
79
+ // 手势协同gesture 1.0
80
+ const swiperGestureRef = useRef();
81
+ useNodesRef(props, ref, nodeRef, {
82
+ // scrollView内部会过滤是否绑定了gestureRef,withRef(swiperGestureRef)给gesture对象设置一个ref(2.0版本)
83
+ gestureRef: swiperGestureRef
84
+ });
79
85
  // 计算transfrom之类的
80
- const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, {
86
+ const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, hasPositionFixed, setWidth, setHeight } = useTransformStyle(style, {
81
87
  enableVar,
82
88
  externalVarContext,
83
89
  parentFontSize,
@@ -106,27 +112,43 @@ const SwiperWrapper = forwardRef((props, ref) => {
106
112
  // 每个元素的宽度 or 高度,有固定值直接初始化无则0
107
113
  const step = useSharedValue(initStep);
108
114
  // 记录选中元素的索引值
109
- const currentIndex = useSharedValue(props.current || 0);
115
+ const currentIndex = useSharedValue(propCurrent);
110
116
  // const initOffset = getOffset(props.current || 0, initStep)
111
117
  // 记录元素的偏移量
112
- const offset = useSharedValue(getOffset(props.current || 0, initStep));
118
+ const offset = useSharedValue(getOffset(propCurrent, initStep));
113
119
  const strAbso = 'absolute' + dir.toUpperCase();
120
+ const strVelocity = 'velocity' + dir.toUpperCase();
114
121
  // 标识手指触摸和抬起, 起点在onBegin
115
122
  const touchfinish = useSharedValue(true);
116
123
  // 记录上一帧的绝对定位坐标
117
124
  const preAbsolutePos = useSharedValue(0);
118
125
  // 记录从onBegin 到 onTouchesUp 时移动的距离
119
126
  const moveTranstion = useSharedValue(0);
120
- // 记录从onBegin 到 onTouchesUp 的时间
121
- const moveTime = useSharedValue(0);
122
127
  const timerId = useRef(0);
123
128
  const intervalTimer = props.interval || 500;
129
+ const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
130
+ const waitForHandlers = flatGesture(waitFor);
131
+ // 判断gesture手势是否需要协同处理、等待手势失败响应
132
+ const gestureSwitch = useRef(false);
133
+ // 初始化上一次的手势
134
+ const prevSimultaneousHandlersRef = useRef(originSimultaneousHandlers || []);
135
+ const prevWaitForHandlersRef = useRef(waitFor || []);
136
+ const hasSimultaneousHandlersChanged = prevSimultaneousHandlersRef.current.length !== (originSimultaneousHandlers?.length || 0) ||
137
+ (originSimultaneousHandlers || []).some((handler, index) => handler !== prevSimultaneousHandlersRef.current[index]);
138
+ const hasWaitForHandlersChanged = prevWaitForHandlersRef.current.length !== (waitFor?.length || 0) ||
139
+ (waitFor || []).some((handler, index) => handler !== prevWaitForHandlersRef.current[index]);
140
+ if (hasSimultaneousHandlersChanged || hasWaitForHandlersChanged) {
141
+ gestureSwitch.current = !gestureSwitch.current;
142
+ }
143
+ // 存储上一次的手势
144
+ prevSimultaneousHandlersRef.current = originSimultaneousHandlers || [];
145
+ prevWaitForHandlersRef.current = waitFor || [];
124
146
  const {
125
147
  // 存储layout布局信息
126
148
  layoutRef, layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout: onWrapperLayout });
127
- const innerProps = useInnerProps(props, {
149
+ const innerProps = useInnerProps(extendObject({}, props, {
128
150
  ref: nodeRef
129
- }, [
151
+ }), [
130
152
  'style',
131
153
  'indicator-dots',
132
154
  'indicator-color',
@@ -148,7 +170,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
148
170
  const iStep = dir === 'x' ? realWidth : realHeight;
149
171
  if (iStep !== step.value) {
150
172
  step.value = iStep;
151
- updateCurrent(props.current || 0, iStep);
173
+ updateCurrent(propCurrent, iStep);
152
174
  updateAutoplay();
153
175
  }
154
176
  }
@@ -164,8 +186,6 @@ const SwiperWrapper = forwardRef((props, ref) => {
164
186
  }
165
187
  });
166
188
  function renderPagination() {
167
- if (children.length <= 1)
168
- return null;
169
189
  const activeColor = activeDotColor || '#007aff';
170
190
  const unActionColor = dotColor || 'rgba(0,0,0,.2)';
171
191
  // 正常渲染所有dots
@@ -187,8 +207,8 @@ const SwiperWrapper = forwardRef((props, ref) => {
187
207
  dotAnimatedStyle
188
208
  ]}/>
189
209
  {dots}
190
- </View>
191
- </View>);
210
+ </View>
211
+ </View>);
192
212
  }
193
213
  function renderItems() {
194
214
  const intLen = children.length;
@@ -252,7 +272,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
252
272
  easing: easeMap[easeingFunc]
253
273
  }, () => {
254
274
  currentIndex.value = nextIndex;
255
- runOnJS(loop)();
275
+ runOnJS(runOnJSCallback)('loop');
256
276
  });
257
277
  }
258
278
  else {
@@ -268,7 +288,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
268
288
  // 将开始位置设置为真正的位置
269
289
  offset.value = initOffset;
270
290
  currentIndex.value = nextIndex;
271
- runOnJS(loop)();
291
+ runOnJS(runOnJSCallback)('loop');
272
292
  });
273
293
  }
274
294
  else {
@@ -280,7 +300,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
280
300
  easing: easeMap[easeingFunc]
281
301
  }, () => {
282
302
  currentIndex.value = nextIndex;
283
- runOnJS(loop)();
303
+ runOnJS(runOnJSCallback)('loop');
284
304
  });
285
305
  }
286
306
  }
@@ -305,12 +325,19 @@ const SwiperWrapper = forwardRef((props, ref) => {
305
325
  resumeLoop
306
326
  };
307
327
  }, []);
308
- function handleSwiperChange(current) {
309
- if (props.current !== currentIndex.value) {
328
+ function handleSwiperChange(current, pCurrent) {
329
+ if (pCurrent !== currentIndex.value) {
310
330
  const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef });
311
- props.bindchange && props.bindchange(eventData);
331
+ bindchange && bindchange(eventData);
312
332
  }
313
333
  }
334
+ const runOnJSCallbackRef = useRef({
335
+ loop,
336
+ pauseLoop,
337
+ resumeLoop,
338
+ handleSwiperChange
339
+ });
340
+ const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef);
314
341
  function getOffset(index, stepValue) {
315
342
  if (!stepValue)
316
343
  return 0;
@@ -328,12 +355,12 @@ const SwiperWrapper = forwardRef((props, ref) => {
328
355
  const targetOffset = getOffset(index || 0, stepValue);
329
356
  if (targetOffset !== offset.value) {
330
357
  // 内部基于props.current!==currentIndex.value决定是否使用动画及更新currentIndex.value
331
- if (props.current !== undefined && props.current !== currentIndex.value) {
358
+ if (propCurrent !== undefined && propCurrent !== currentIndex.value) {
332
359
  offset.value = withTiming(targetOffset, {
333
360
  duration: easeDuration,
334
361
  easing: easeMap[easeingFunc]
335
362
  }, () => {
336
- currentIndex.value = props.current || 0;
363
+ currentIndex.value = propCurrent;
337
364
  });
338
365
  }
339
366
  else {
@@ -352,8 +379,8 @@ const SwiperWrapper = forwardRef((props, ref) => {
352
379
  // 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画
353
380
  useAnimatedReaction(() => currentIndex.value, (newIndex, preIndex) => {
354
381
  // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
355
- if (newIndex !== preIndex && props.bindchange) {
356
- runOnJS(handleSwiperChange)(newIndex);
382
+ if (newIndex !== preIndex && bindchange) {
383
+ runOnJS(runOnJSCallback)('handleSwiperChange', newIndex, propCurrent);
357
384
  }
358
385
  });
359
386
  useEffect(() => {
@@ -384,8 +411,12 @@ const SwiperWrapper = forwardRef((props, ref) => {
384
411
  }
385
412
  }, [children.length]);
386
413
  useEffect(() => {
387
- updateCurrent(props.current || 0, step.value);
388
- }, [props.current]);
414
+ // 1. 如果用户在touch的过程中, 外部更新了current以外部为准(小程序表现)
415
+ // 2. 手指滑动过程中更新索引,外部会把current再传入进来,导致offset直接更新,增加判断不同才更新
416
+ if (propCurrent !== currentIndex.value) {
417
+ updateCurrent(propCurrent, step.value);
418
+ }
419
+ }, [propCurrent]);
389
420
  useEffect(() => {
390
421
  autoplayShared.value = autoplay;
391
422
  updateAutoplay();
@@ -406,7 +437,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
406
437
  function getTargetPosition(eventData) {
407
438
  'worklet';
408
439
  // 移动的距离
409
- const { translation } = eventData;
440
+ const { transdir } = eventData;
410
441
  let resetOffsetPos = 0;
411
442
  let selectedIndex = currentIndex.value;
412
443
  // 是否临界点
@@ -414,9 +445,9 @@ const SwiperWrapper = forwardRef((props, ref) => {
414
445
  // 真实滚动到的偏移量坐标
415
446
  let moveToTargetPos = 0;
416
447
  const tmp = !circularShared.value ? 0 : preMarginShared.value;
417
- const currentOffset = translation < 0 ? offset.value - tmp : offset.value + tmp;
448
+ const currentOffset = transdir < 0 ? offset.value - tmp : offset.value + tmp;
418
449
  const computedIndex = Math.abs(currentOffset) / step.value;
419
- const moveToIndex = translation < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex);
450
+ const moveToIndex = transdir < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex);
420
451
  // 实际应该定位的索引值
421
452
  if (!circularShared.value) {
422
453
  selectedIndex = moveToIndex;
@@ -449,14 +480,18 @@ const SwiperWrapper = forwardRef((props, ref) => {
449
480
  }
450
481
  function canMove(eventData) {
451
482
  'worklet';
452
- const { translation } = eventData;
453
- const currentOffset = Math.abs(offset.value);
483
+ // 旧版:如果在快速多次滑动时,只根据当前的offset判断,会出现offset没超出,加上translation后越界的场景(如在倒数第二个元素快速滑动)
484
+ // 新版:会加上translation
485
+ const { translation, transdir } = eventData;
486
+ const gestureMovePos = offset.value + translation;
454
487
  if (!circularShared.value) {
455
- if (translation < 0) {
456
- return currentOffset < step.value * (childrenLength.value - 1);
488
+ // 如果只判断区间,中间非滑动状态(handleResistanceMove)向左滑动,突然改为向右滑动,但是还在非滑动态,本应该可滑动判断为了不可滑动
489
+ const posEnd = -step.value * (childrenLength.value - 1);
490
+ if (transdir < 0) {
491
+ return gestureMovePos > posEnd;
457
492
  }
458
493
  else {
459
- return currentOffset > 0;
494
+ return gestureMovePos < 0;
460
495
  }
461
496
  }
462
497
  else {
@@ -474,7 +509,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
474
509
  if (touchfinish.value !== false) {
475
510
  currentIndex.value = selectedIndex;
476
511
  offset.value = resetOffset;
477
- runOnJS(resumeLoop)();
512
+ runOnJS(runOnJSCallback)('resumeLoop');
478
513
  }
479
514
  });
480
515
  }
@@ -485,21 +520,21 @@ const SwiperWrapper = forwardRef((props, ref) => {
485
520
  }, () => {
486
521
  if (touchfinish.value !== false) {
487
522
  currentIndex.value = selectedIndex;
488
- runOnJS(resumeLoop)();
523
+ runOnJS(runOnJSCallback)('resumeLoop');
489
524
  }
490
525
  });
491
526
  }
492
527
  }
493
528
  function handleBack(eventData) {
494
529
  'worklet';
495
- const { translation } = eventData;
530
+ const { transdir } = eventData;
496
531
  // 向右滑动的back:trans < 0, 向左滑动的back: trans < 0
497
532
  let currentOffset = Math.abs(offset.value);
498
533
  if (circularShared.value) {
499
- currentOffset += translation < 0 ? preMarginShared.value : -preMarginShared.value;
534
+ currentOffset += transdir < 0 ? preMarginShared.value : -preMarginShared.value;
500
535
  }
501
536
  const curIndex = currentOffset / step.value;
502
- const moveToIndex = (translation < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElmNumShared.value;
537
+ const moveToIndex = (transdir < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElmNumShared.value;
503
538
  const targetOffset = -(moveToIndex + patchElmNumShared.value) * step.value + (circularShared.value ? preMarginShared.value : 0);
504
539
  offset.value = withTiming(targetOffset, {
505
540
  duration: easeDuration,
@@ -507,69 +542,106 @@ const SwiperWrapper = forwardRef((props, ref) => {
507
542
  }, () => {
508
543
  if (touchfinish.value !== false) {
509
544
  currentIndex.value = moveToIndex;
510
- runOnJS(resumeLoop)();
545
+ runOnJS(runOnJSCallback)('resumeLoop');
511
546
  }
512
547
  });
513
548
  }
514
- function handleLongPress() {
549
+ // 当前的offset和index多对应的offset进行对比,判断是否超过一半
550
+ function computeHalf(eventData) {
515
551
  'worklet';
552
+ const { transdir } = eventData;
516
553
  const currentOffset = Math.abs(offset.value);
517
554
  let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value;
518
555
  if (circularShared.value) {
519
556
  preOffset -= preMarginShared.value;
520
557
  }
521
- // 正常事件中拿到的transition值(正向滑动<0,倒着滑>0)
558
+ // 正常事件中拿到的translation值(正向滑动<0,倒着滑>0)
522
559
  const diffOffset = preOffset - currentOffset;
523
560
  const half = Math.abs(diffOffset) > step.value / 2;
561
+ const isTriggerUpdateHalf = (transdir < 0 && currentOffset < preOffset) || (transdir > 0 && currentOffset > preOffset);
562
+ return {
563
+ diffOffset,
564
+ half,
565
+ isTriggerUpdateHalf
566
+ };
567
+ }
568
+ function handleLongPress(eventData) {
569
+ 'worklet';
570
+ const { diffOffset, half, isTriggerUpdateHalf } = computeHalf(eventData);
524
571
  if (+diffOffset === 0) {
525
- runOnJS(resumeLoop)();
572
+ runOnJS(runOnJSCallback)('resumeLoop');
573
+ }
574
+ else if (isTriggerUpdateHalf) {
575
+ // 如果触发了onUpdate时的索引变更
576
+ handleEnd(eventData);
526
577
  }
527
578
  else if (half) {
528
- handleEnd({ translation: diffOffset });
579
+ handleEnd(eventData);
529
580
  }
530
581
  else {
531
- handleBack({ translation: diffOffset });
582
+ handleBack(eventData);
532
583
  }
533
584
  }
534
585
  function reachBoundary(eventData) {
535
586
  'worklet';
536
- // 移动的距离
587
+ // 1. 基于当前的offset和translation判断是否超过当前边界值
537
588
  const { translation } = eventData;
538
- const elementsLength = step.value * childrenLength.value;
589
+ const boundaryStart = -patchElmNumShared.value * step.value;
590
+ const boundaryEnd = -(childrenLength.value + patchElmNumShared.value) * step.value;
591
+ const moveToOffset = offset.value + translation;
539
592
  let isBoundary = false;
540
593
  let resetOffset = 0;
541
- // Y轴向下滚动, transDistance > 0, 向上滚动 < 0 X轴向左滚动, transDistance > 0
542
- const currentOffset = offset.value;
543
- const moveStep = Math.ceil(translation / elementsLength);
544
- if (translation < 0) {
545
- const posEnd = (childrenLength.value + patchElmNumShared.value + 1) * step.value;
546
- const posReverseEnd = (patchElmNumShared.value - 1) * step.value;
547
- if (currentOffset < -posEnd + step.value) {
548
- isBoundary = true;
549
- resetOffset = Math.abs(moveStep) === 0 ? patchElmNumShared.value * step.value + translation : moveStep * elementsLength;
550
- }
551
- if (currentOffset > -posReverseEnd) {
552
- isBoundary = true;
553
- resetOffset = moveStep * elementsLength;
554
- }
594
+ if (moveToOffset < boundaryEnd) {
595
+ isBoundary = true;
596
+ // 超过边界的距离
597
+ const exceedLength = Math.abs(moveToOffset) - Math.abs(boundaryEnd);
598
+ // 计算对标正常元素所在的offset
599
+ resetOffset = patchElmNumShared.value * step.value + exceedLength;
555
600
  }
556
- else if (translation > 0) {
557
- const posEnd = (patchElmNumShared.value - 1) * step.value;
558
- const posReverseEnd = (patchElmNumShared.value + childrenLength.value) * step.value;
559
- if (currentOffset > -posEnd) {
560
- isBoundary = true;
561
- resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? translation : 0);
562
- }
563
- if (currentOffset < -posReverseEnd) {
564
- isBoundary = true;
565
- resetOffset = moveStep * elementsLength + patchElmNumShared.value * step.value;
566
- }
601
+ if (moveToOffset > boundaryStart) {
602
+ isBoundary = true;
603
+ // 超过边界的距离
604
+ const exceedLength = Math.abs(boundaryStart) - Math.abs(moveToOffset);
605
+ // 计算对标正常元素所在的offset
606
+ resetOffset = (patchElmNumShared.value + childrenLength.value - 1) * step.value + (step.value - exceedLength);
567
607
  }
568
608
  return {
569
609
  isBoundary,
570
610
  resetOffset: -resetOffset
571
611
  };
572
612
  }
613
+ // 非循环超出边界,应用阻力; 开始滑动少阻力小,滑动越长阻力越大
614
+ function handleResistanceMove(eventData) {
615
+ 'worklet';
616
+ const { translation, transdir } = eventData;
617
+ const moveToOffset = offset.value + translation;
618
+ const maxOverDrag = Math.floor(step.value / 2);
619
+ const maxOffset = translation < 0 ? -(childrenLength.value - 1) * step.value : 0;
620
+ let resistance = 0.1;
621
+ let overDrag = 0;
622
+ let finalOffset = 0;
623
+ // 向右向下小于0, 向左向上大于0;
624
+ if (transdir < 0) {
625
+ overDrag = Math.abs(moveToOffset - maxOffset);
626
+ }
627
+ else {
628
+ overDrag = Math.abs(moveToOffset);
629
+ }
630
+ // 滑动越多resistance越小
631
+ resistance = 1 - overDrag / maxOverDrag;
632
+ // 确保阻力在合理范围内
633
+ resistance = Math.min(0.5, resistance);
634
+ // 限制在最大拖拽范围内
635
+ if (transdir < 0) {
636
+ const adjustOffset = offset.value + translation * resistance;
637
+ finalOffset = Math.max(adjustOffset, maxOffset - maxOverDrag);
638
+ }
639
+ else {
640
+ const adjustOffset = offset.value + translation * resistance;
641
+ finalOffset = Math.min(adjustOffset, maxOverDrag);
642
+ }
643
+ return finalOffset;
644
+ }
573
645
  const gesturePan = Gesture.Pan()
574
646
  .onBegin((e) => {
575
647
  'worklet';
@@ -577,59 +649,113 @@ const SwiperWrapper = forwardRef((props, ref) => {
577
649
  return;
578
650
  touchfinish.value = false;
579
651
  cancelAnimation(offset);
580
- runOnJS(pauseLoop)();
652
+ runOnJS(runOnJSCallback)('pauseLoop');
581
653
  preAbsolutePos.value = e[strAbso];
582
654
  moveTranstion.value = e[strAbso];
583
- moveTime.value = new Date().getTime();
584
655
  })
585
- .onTouchesMove((e) => {
656
+ .onUpdate((e) => {
586
657
  'worklet';
587
- if (touchfinish.value)
658
+ const moveDistance = e[strAbso] - preAbsolutePos.value;
659
+ if (touchfinish.value || moveDistance === 0)
588
660
  return;
589
- const touchEventData = e.changedTouches[0];
590
- const moveDistance = touchEventData[strAbso] - preAbsolutePos.value;
591
661
  const eventData = {
592
- translation: moveDistance
662
+ translation: moveDistance,
663
+ transdir: moveDistance
593
664
  };
594
- // 处理用户一直拖拽到临界点的场景, 不会执行onEnd
595
- if (!circularShared.value && !canMove(eventData)) {
665
+ // 1. 支持滑动中超出一半更新索引的能力:只更新索引并不会影响onFinalize依据当前offset计算的索引
666
+ const { half } = computeHalf(eventData);
667
+ if (childrenLength.value > 1 && half) {
668
+ const { selectedIndex } = getTargetPosition(eventData);
669
+ currentIndex.value = selectedIndex;
670
+ }
671
+ // 2. 非循环: 处理用户一直拖拽到临界点的场景,如果放到onFinalize无法阻止offset.value更新为越界的值
672
+ if (!circularShared.value) {
673
+ if (canMove(eventData)) {
674
+ offset.value = moveDistance + offset.value;
675
+ }
676
+ else {
677
+ const finalOffset = handleResistanceMove(eventData);
678
+ offset.value = finalOffset;
679
+ }
680
+ preAbsolutePos.value = e[strAbso];
596
681
  return;
597
682
  }
683
+ // 3. 循环更新: 只有一个元素时可滑动,加入阻力
684
+ if (circularShared.value && childrenLength.value === 1) {
685
+ const finalOffset = handleResistanceMove(eventData);
686
+ offset.value = finalOffset;
687
+ preAbsolutePos.value = e[strAbso];
688
+ return;
689
+ }
690
+ // 4. 循环更新:正常
598
691
  const { isBoundary, resetOffset } = reachBoundary(eventData);
599
- if (isBoundary && circularShared.value) {
692
+ if (childrenLength.value > 1 && isBoundary && circularShared.value) {
600
693
  offset.value = resetOffset;
601
694
  }
602
695
  else {
603
696
  offset.value = moveDistance + offset.value;
604
697
  }
605
- preAbsolutePos.value = touchEventData[strAbso];
698
+ preAbsolutePos.value = e[strAbso];
606
699
  })
607
- .onTouchesUp((e) => {
700
+ .onFinalize((e) => {
608
701
  'worklet';
609
702
  if (touchfinish.value)
610
703
  return;
611
- const touchEventData = e.changedTouches[0];
612
- const moveDistance = touchEventData[strAbso] - moveTranstion.value;
613
704
  touchfinish.value = true;
705
+ // 触发过onUpdate正常情况下e[strAbso] - preAbsolutePos.value=0; 未触发过onUpdate的情况下e[strAbso] - preAbsolutePos.value 不为0
706
+ const moveDistance = e[strAbso] - preAbsolutePos.value;
614
707
  const eventData = {
615
- translation: moveDistance
708
+ translation: moveDistance,
709
+ transdir: moveDistance !== 0 ? moveDistance : e[strAbso] - moveTranstion.value
616
710
  };
617
- // 用户手指按下起来, 需要计算正确的位置, 比如在滑动过程中突然按下然后起来,需要计算到正确的位置
711
+ // 1. 只有一个元素:循环 和 非循环状态,都走回弹效果
712
+ if (childrenLength.value === 1) {
713
+ offset.value = withTiming(0, {
714
+ duration: easeDuration,
715
+ easing: easeMap[easeingFunc]
716
+ });
717
+ return;
718
+ }
719
+ // 2.非循环状态不可移动态:最后一个元素 和 第一个元素
720
+ // 非循环支持最后元素可滑动能力后,向左快速移动未超过最大可移动范围一半,因为offset为正值,向左滑动handleBack,默认向上取整
721
+ // 但是在offset大于0时,取0。[-100, 0](back取0), [0, 100](back取1), 所以handleLongPress里的处理逻辑需要兼容支持,因此这里直接单独处理,不耦合下方公共的判断逻辑。
618
722
  if (!circularShared.value && !canMove(eventData)) {
723
+ if (eventData.transdir < 0) {
724
+ handleBack(eventData);
725
+ }
726
+ else {
727
+ handleEnd(eventData);
728
+ }
619
729
  return;
620
730
  }
621
- const strVelocity = moveDistance / (new Date().getTime() - moveTime.value) * 1000;
622
- if (Math.abs(strVelocity) < longPressRatio) {
623
- handleLongPress();
731
+ // 3. 非循环状态可移动态、循环状态, 正常逻辑处理
732
+ const velocity = e[strVelocity];
733
+ if (Math.abs(velocity) < longPressRatio) {
734
+ handleLongPress(eventData);
624
735
  }
625
736
  else {
626
737
  handleEnd(eventData);
627
738
  }
628
- });
739
+ })
740
+ .withRef(swiperGestureRef);
741
+ // swiper横向,当y轴滑动5像素手势失效;swiper纵向只响应swiper的滑动事件
742
+ if (dir === 'x') {
743
+ gesturePan.activeOffsetX([-2, 2]).failOffsetY([-5, 5]);
744
+ }
745
+ else {
746
+ gesturePan.activeOffsetY([-2, 2]).failOffsetX([-5, 5]);
747
+ }
748
+ // 手势协同2.0
749
+ if (simultaneousHandlers && simultaneousHandlers.length) {
750
+ gesturePan.simultaneousWithExternalGesture(...simultaneousHandlers);
751
+ }
752
+ if (waitForHandlers && waitForHandlers.length) {
753
+ gesturePan.requireExternalGestureToFail(...waitForHandlers);
754
+ }
629
755
  return {
630
756
  gestureHandler: gesturePan
631
757
  };
632
- }, []);
758
+ }, [gestureSwitch.current]);
633
759
  const animatedStyles = useAnimatedStyle(() => {
634
760
  if (dir === 'x') {
635
761
  return { transform: [{ translateX: offset.value }], opacity: step.value > 0 ? 1 : 0 };
@@ -638,34 +764,32 @@ const SwiperWrapper = forwardRef((props, ref) => {
638
764
  return { transform: [{ translateY: offset.value }], opacity: step.value > 0 ? 1 : 0 };
639
765
  }
640
766
  });
641
- function renderSwiper() {
642
- const arrPages = renderItems();
643
- return (<View style={[normalStyle, layoutStyle, styles.swiper]} {...layoutProps} {...innerProps}>
644
- <Animated.View style={[{
645
- flexDirection: dir === 'x' ? 'row' : 'column',
646
- width: '100%',
647
- height: '100%'
648
- }, animatedStyles]}>
649
- {wrapChildren({
650
- children: arrPages
651
- }, {
652
- hasVarDec,
653
- varContext: varContextRef.current,
654
- textStyle,
655
- textProps
656
- })}
657
- </Animated.View>
658
- {showsPagination && renderPagination()}
659
- </View>);
660
- }
661
- if (children.length === 1 || disableGesture) {
662
- return renderSwiper();
767
+ let finalComponent;
768
+ const arrPages = renderItems();
769
+ const mergeProps = Object.assign({
770
+ style: [normalStyle, layoutStyle, styles.swiper]
771
+ }, layoutProps, innerProps);
772
+ const animateComponent = createElement(Animated.View, {
773
+ style: [{ flexDirection: dir === 'x' ? 'row' : 'column', width: '100%', height: '100%' }, animatedStyles]
774
+ }, wrapChildren({
775
+ children: arrPages
776
+ }, {
777
+ hasVarDec,
778
+ varContext: varContextRef.current,
779
+ textStyle,
780
+ textProps
781
+ }));
782
+ const renderChildrens = showPagination ? [animateComponent, renderPagination()] : animateComponent;
783
+ finalComponent = createElement(View, mergeProps, renderChildrens);
784
+ if (!disableGesture) {
785
+ finalComponent = createElement(GestureDetector, {
786
+ gesture: gestureHandler
787
+ }, finalComponent);
663
788
  }
664
- else {
665
- return (<GestureDetector gesture={gestureHandler}>
666
- {renderSwiper()}
667
- </GestureDetector>);
789
+ if (hasPositionFixed) {
790
+ finalComponent = createElement(Portal, null, finalComponent);
668
791
  }
792
+ return finalComponent;
669
793
  });
670
794
  SwiperWrapper.displayName = 'MpxSwiperWrapper';
671
795
  export default SwiperWrapper;