@mpxjs/webpack-plugin 2.10.4-beta.18 → 2.10.4-beta.19-input

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 (36) hide show
  1. package/lib/runtime/components/react/dist/getInnerListeners.js +36 -22
  2. package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +145 -0
  3. package/lib/runtime/components/react/dist/mpx-button.jsx +7 -2
  4. package/lib/runtime/components/react/dist/mpx-canvas/Image.js +2 -4
  5. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +20 -17
  6. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +7 -2
  7. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +7 -2
  8. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +7 -2
  9. package/lib/runtime/components/react/dist/mpx-image.jsx +33 -20
  10. package/lib/runtime/components/react/dist/mpx-input.jsx +7 -2
  11. package/lib/runtime/components/react/dist/mpx-label.jsx +7 -2
  12. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +8 -3
  13. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +205 -79
  14. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +11 -13
  15. package/lib/runtime/components/react/dist/mpx-picker-view/index.jsx +8 -7
  16. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +29 -11
  17. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +3 -5
  18. package/lib/runtime/components/react/dist/mpx-progress.jsx +163 -0
  19. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +9 -2
  20. package/lib/runtime/components/react/dist/mpx-radio.jsx +7 -2
  21. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -2
  22. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +104 -51
  23. package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
  24. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +3 -1
  25. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +11 -9
  26. package/lib/runtime/components/react/dist/mpx-swiper.jsx +203 -141
  27. package/lib/runtime/components/react/dist/mpx-switch.jsx +7 -2
  28. package/lib/runtime/components/react/dist/mpx-text.jsx +7 -2
  29. package/lib/runtime/components/react/dist/mpx-video.jsx +7 -2
  30. package/lib/runtime/components/react/dist/mpx-view.jsx +28 -26
  31. package/lib/runtime/components/react/dist/mpx-web-view.jsx +34 -29
  32. package/lib/runtime/components/react/dist/useAnimationHooks.js +12 -89
  33. package/lib/runtime/components/react/dist/utils.jsx +199 -114
  34. package/lib/runtime/components/react/mpx-input.tsx +6 -6
  35. package/lib/template-compiler/compiler.js +1 -1
  36. package/package.json +1 -1
@@ -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, extendObject, flatGesture } 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,16 +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, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], 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
79
  // 手势协同gesture 1.0
79
80
  const swiperGestureRef = useRef();
80
- useNodesRef(props, ref, nodeRef, {});
81
+ useNodesRef(props, ref, nodeRef, {
82
+ // scrollView内部会过滤是否绑定了gestureRef,withRef(swiperGestureRef)给gesture对象设置一个ref(2.0版本)
83
+ gestureRef: swiperGestureRef
84
+ });
81
85
  // 计算transfrom之类的
82
- const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, {
86
+ const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, hasPositionFixed, setWidth, setHeight } = useTransformStyle(style, {
83
87
  enableVar,
84
88
  externalVarContext,
85
89
  parentFontSize,
@@ -108,23 +112,18 @@ const SwiperWrapper = forwardRef((props, ref) => {
108
112
  // 每个元素的宽度 or 高度,有固定值直接初始化无则0
109
113
  const step = useSharedValue(initStep);
110
114
  // 记录选中元素的索引值
111
- const currentIndex = useSharedValue(props.current || 0);
115
+ const currentIndex = useSharedValue(propCurrent);
112
116
  // const initOffset = getOffset(props.current || 0, initStep)
113
117
  // 记录元素的偏移量
114
- const offset = useSharedValue(getOffset(props.current || 0, initStep));
118
+ const offset = useSharedValue(getOffset(propCurrent, initStep));
115
119
  const strAbso = 'absolute' + dir.toUpperCase();
120
+ const strVelocity = 'velocity' + dir.toUpperCase();
116
121
  // 标识手指触摸和抬起, 起点在onBegin
117
122
  const touchfinish = useSharedValue(true);
118
123
  // 记录上一帧的绝对定位坐标
119
124
  const preAbsolutePos = useSharedValue(0);
120
125
  // 记录从onBegin 到 onTouchesUp 时移动的距离
121
126
  const moveTranstion = useSharedValue(0);
122
- // 记录从onBegin 到 onTouchesUp 的时间
123
- const moveTime = useSharedValue(0);
124
- // 记录从onBegin 到 onTouchesCancelled 另外一个方向移动的距离
125
- const anotherDirectionMove = useSharedValue(0);
126
- // 另一个方向的
127
- const anotherAbso = 'absolute' + (dir === 'x' ? 'y' : 'x').toUpperCase();
128
127
  const timerId = useRef(0);
129
128
  const intervalTimer = props.interval || 500;
130
129
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
@@ -171,7 +170,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
171
170
  const iStep = dir === 'x' ? realWidth : realHeight;
172
171
  if (iStep !== step.value) {
173
172
  step.value = iStep;
174
- updateCurrent(props.current || 0, iStep);
173
+ updateCurrent(propCurrent, iStep);
175
174
  updateAutoplay();
176
175
  }
177
176
  }
@@ -187,8 +186,6 @@ const SwiperWrapper = forwardRef((props, ref) => {
187
186
  }
188
187
  });
189
188
  function renderPagination() {
190
- if (children.length <= 1)
191
- return null;
192
189
  const activeColor = activeDotColor || '#007aff';
193
190
  const unActionColor = dotColor || 'rgba(0,0,0,.2)';
194
191
  // 正常渲染所有dots
@@ -275,7 +272,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
275
272
  easing: easeMap[easeingFunc]
276
273
  }, () => {
277
274
  currentIndex.value = nextIndex;
278
- runOnJS(loop)();
275
+ runOnJS(runOnJSCallback)('loop');
279
276
  });
280
277
  }
281
278
  else {
@@ -291,7 +288,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
291
288
  // 将开始位置设置为真正的位置
292
289
  offset.value = initOffset;
293
290
  currentIndex.value = nextIndex;
294
- runOnJS(loop)();
291
+ runOnJS(runOnJSCallback)('loop');
295
292
  });
296
293
  }
297
294
  else {
@@ -303,7 +300,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
303
300
  easing: easeMap[easeingFunc]
304
301
  }, () => {
305
302
  currentIndex.value = nextIndex;
306
- runOnJS(loop)();
303
+ runOnJS(runOnJSCallback)('loop');
307
304
  });
308
305
  }
309
306
  }
@@ -328,12 +325,19 @@ const SwiperWrapper = forwardRef((props, ref) => {
328
325
  resumeLoop
329
326
  };
330
327
  }, []);
331
- function handleSwiperChange(current) {
332
- if (props.current !== currentIndex.value) {
328
+ function handleSwiperChange(current, pCurrent) {
329
+ if (pCurrent !== currentIndex.value) {
333
330
  const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef });
334
- props.bindchange && props.bindchange(eventData);
331
+ bindchange && bindchange(eventData);
335
332
  }
336
333
  }
334
+ const runOnJSCallbackRef = useRef({
335
+ loop,
336
+ pauseLoop,
337
+ resumeLoop,
338
+ handleSwiperChange
339
+ });
340
+ const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef);
337
341
  function getOffset(index, stepValue) {
338
342
  if (!stepValue)
339
343
  return 0;
@@ -351,12 +355,12 @@ const SwiperWrapper = forwardRef((props, ref) => {
351
355
  const targetOffset = getOffset(index || 0, stepValue);
352
356
  if (targetOffset !== offset.value) {
353
357
  // 内部基于props.current!==currentIndex.value决定是否使用动画及更新currentIndex.value
354
- if (props.current !== undefined && props.current !== currentIndex.value) {
358
+ if (propCurrent !== undefined && propCurrent !== currentIndex.value) {
355
359
  offset.value = withTiming(targetOffset, {
356
360
  duration: easeDuration,
357
361
  easing: easeMap[easeingFunc]
358
362
  }, () => {
359
- currentIndex.value = props.current || 0;
363
+ currentIndex.value = propCurrent;
360
364
  });
361
365
  }
362
366
  else {
@@ -375,8 +379,8 @@ const SwiperWrapper = forwardRef((props, ref) => {
375
379
  // 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画
376
380
  useAnimatedReaction(() => currentIndex.value, (newIndex, preIndex) => {
377
381
  // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
378
- if (newIndex !== preIndex && props.bindchange) {
379
- runOnJS(handleSwiperChange)(newIndex);
382
+ if (newIndex !== preIndex && bindchange) {
383
+ runOnJS(runOnJSCallback)('handleSwiperChange', newIndex, propCurrent);
380
384
  }
381
385
  });
382
386
  useEffect(() => {
@@ -408,11 +412,11 @@ const SwiperWrapper = forwardRef((props, ref) => {
408
412
  }, [children.length]);
409
413
  useEffect(() => {
410
414
  // 1. 如果用户在touch的过程中, 外部更新了current以外部为准(小程序表现)
411
- // 2. 手指滑动过程中更新索引,外部会把current再穿进来,导致offset直接更新了
412
- if (props.current !== currentIndex.value) {
413
- updateCurrent(props.current || 0, step.value);
415
+ // 2. 手指滑动过程中更新索引,外部会把current再传入进来,导致offset直接更新,增加判断不同才更新
416
+ if (propCurrent !== currentIndex.value) {
417
+ updateCurrent(propCurrent, step.value);
414
418
  }
415
- }, [props.current]);
419
+ }, [propCurrent]);
416
420
  useEffect(() => {
417
421
  autoplayShared.value = autoplay;
418
422
  updateAutoplay();
@@ -433,7 +437,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
433
437
  function getTargetPosition(eventData) {
434
438
  'worklet';
435
439
  // 移动的距离
436
- const { translation } = eventData;
440
+ const { transdir } = eventData;
437
441
  let resetOffsetPos = 0;
438
442
  let selectedIndex = currentIndex.value;
439
443
  // 是否临界点
@@ -441,9 +445,9 @@ const SwiperWrapper = forwardRef((props, ref) => {
441
445
  // 真实滚动到的偏移量坐标
442
446
  let moveToTargetPos = 0;
443
447
  const tmp = !circularShared.value ? 0 : preMarginShared.value;
444
- const currentOffset = translation < 0 ? offset.value - tmp : offset.value + tmp;
448
+ const currentOffset = transdir < 0 ? offset.value - tmp : offset.value + tmp;
445
449
  const computedIndex = Math.abs(currentOffset) / step.value;
446
- const moveToIndex = translation < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex);
450
+ const moveToIndex = transdir < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex);
447
451
  // 实际应该定位的索引值
448
452
  if (!circularShared.value) {
449
453
  selectedIndex = moveToIndex;
@@ -474,26 +478,24 @@ const SwiperWrapper = forwardRef((props, ref) => {
474
478
  targetOffset: -moveToTargetPos
475
479
  };
476
480
  }
477
- function checkUnCircular(eventData) {
481
+ function canMove(eventData) {
478
482
  'worklet';
479
- const { translation } = eventData;
480
- const currentOffset = Math.abs(offset.value);
481
- // 向右滑动swiper
482
- if (translation < 0) {
483
- const boundaryOffset = step.value * (childrenLength.value - 1);
484
- const gestureMovePos = Math.abs(translation) + currentOffset;
485
- return {
486
- // 防止快速连续向右滑动时,手势移动的距离 当前的offset超出边界
487
- targetOffset: gestureMovePos > boundaryOffset ? -boundaryOffset : offset.value + translation,
488
- canMove: currentOffset < boundaryOffset
489
- };
483
+ // 旧版:如果在快速多次滑动时,只根据当前的offset判断,会出现offset没超出,加上translation后越界的场景(如在倒数第二个元素快速滑动)
484
+ // 新版:会加上translation
485
+ const { translation, transdir } = eventData;
486
+ const gestureMovePos = offset.value + translation;
487
+ if (!circularShared.value) {
488
+ // 如果只判断区间,中间非滑动状态(handleResistanceMove)向左滑动,突然改为向右滑动,但是还在非滑动态,本应该可滑动判断为了不可滑动
489
+ const posEnd = -step.value * (childrenLength.value - 1);
490
+ if (transdir < 0) {
491
+ return gestureMovePos > posEnd;
492
+ }
493
+ else {
494
+ return gestureMovePos < 0;
495
+ }
490
496
  }
491
497
  else {
492
- const gestureMovePos = currentOffset - translation;
493
- return {
494
- targetOffset: gestureMovePos < 0 ? 0 : offset.value + translation,
495
- canMove: currentOffset > 0
496
- };
498
+ return true;
497
499
  }
498
500
  }
499
501
  function handleEnd(eventData) {
@@ -507,7 +509,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
507
509
  if (touchfinish.value !== false) {
508
510
  currentIndex.value = selectedIndex;
509
511
  offset.value = resetOffset;
510
- runOnJS(resumeLoop)();
512
+ runOnJS(runOnJSCallback)('resumeLoop');
511
513
  }
512
514
  });
513
515
  }
@@ -518,21 +520,21 @@ const SwiperWrapper = forwardRef((props, ref) => {
518
520
  }, () => {
519
521
  if (touchfinish.value !== false) {
520
522
  currentIndex.value = selectedIndex;
521
- runOnJS(resumeLoop)();
523
+ runOnJS(runOnJSCallback)('resumeLoop');
522
524
  }
523
525
  });
524
526
  }
525
527
  }
526
528
  function handleBack(eventData) {
527
529
  'worklet';
528
- const { translation } = eventData;
530
+ const { transdir } = eventData;
529
531
  // 向右滑动的back:trans < 0, 向左滑动的back: trans < 0
530
532
  let currentOffset = Math.abs(offset.value);
531
533
  if (circularShared.value) {
532
- currentOffset += translation < 0 ? preMarginShared.value : -preMarginShared.value;
534
+ currentOffset += transdir < 0 ? preMarginShared.value : -preMarginShared.value;
533
535
  }
534
536
  const curIndex = currentOffset / step.value;
535
- 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;
536
538
  const targetOffset = -(moveToIndex + patchElmNumShared.value) * step.value + (circularShared.value ? preMarginShared.value : 0);
537
539
  offset.value = withTiming(targetOffset, {
538
540
  duration: easeDuration,
@@ -540,77 +542,106 @@ const SwiperWrapper = forwardRef((props, ref) => {
540
542
  }, () => {
541
543
  if (touchfinish.value !== false) {
542
544
  currentIndex.value = moveToIndex;
543
- runOnJS(resumeLoop)();
545
+ runOnJS(runOnJSCallback)('resumeLoop');
544
546
  }
545
547
  });
546
548
  }
547
- function computeHalf() {
549
+ // 当前的offset和index多对应的offset进行对比,判断是否超过一半
550
+ function computeHalf(eventData) {
548
551
  'worklet';
552
+ const { transdir } = eventData;
549
553
  const currentOffset = Math.abs(offset.value);
550
554
  let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value;
551
555
  if (circularShared.value) {
552
556
  preOffset -= preMarginShared.value;
553
557
  }
554
- // 正常事件中拿到的transition值(正向滑动<0,倒着滑>0)
558
+ // 正常事件中拿到的translation值(正向滑动<0,倒着滑>0)
555
559
  const diffOffset = preOffset - currentOffset;
556
560
  const half = Math.abs(diffOffset) > step.value / 2;
561
+ const isTriggerUpdateHalf = (transdir < 0 && currentOffset < preOffset) || (transdir > 0 && currentOffset > preOffset);
557
562
  return {
558
563
  diffOffset,
559
- half
564
+ half,
565
+ isTriggerUpdateHalf
560
566
  };
561
567
  }
562
- function handleLongPress() {
568
+ function handleLongPress(eventData) {
563
569
  'worklet';
564
- const { diffOffset, half } = computeHalf();
570
+ const { diffOffset, half, isTriggerUpdateHalf } = computeHalf(eventData);
565
571
  if (+diffOffset === 0) {
566
- runOnJS(resumeLoop)();
572
+ runOnJS(runOnJSCallback)('resumeLoop');
573
+ }
574
+ else if (isTriggerUpdateHalf) {
575
+ // 如果触发了onUpdate时的索引变更
576
+ handleEnd(eventData);
567
577
  }
568
578
  else if (half) {
569
- handleEnd({ translation: diffOffset });
579
+ handleEnd(eventData);
570
580
  }
571
581
  else {
572
- handleBack({ translation: diffOffset });
582
+ handleBack(eventData);
573
583
  }
574
584
  }
575
585
  function reachBoundary(eventData) {
576
586
  'worklet';
577
- // 移动的距离
587
+ // 1. 基于当前的offset和translation判断是否超过当前边界值
578
588
  const { translation } = eventData;
579
- 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;
580
592
  let isBoundary = false;
581
593
  let resetOffset = 0;
582
- // Y轴向下滚动, transDistance > 0, 向上滚动 < 0 X轴向左滚动, transDistance > 0
583
- const currentOffset = offset.value;
584
- const moveStep = Math.ceil(translation / elementsLength);
585
- if (translation < 0) {
586
- const posEnd = (childrenLength.value + patchElmNumShared.value + 1) * step.value;
587
- const posReverseEnd = (patchElmNumShared.value - 1) * step.value;
588
- if (currentOffset < -posEnd + step.value) {
589
- isBoundary = true;
590
- resetOffset = Math.abs(moveStep) === 0 ? patchElmNumShared.value * step.value + translation : moveStep * elementsLength;
591
- }
592
- if (currentOffset > -posReverseEnd) {
593
- isBoundary = true;
594
- resetOffset = moveStep * elementsLength;
595
- }
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;
596
600
  }
597
- else if (translation > 0) {
598
- const posEnd = (patchElmNumShared.value - 1) * step.value;
599
- const posReverseEnd = (patchElmNumShared.value + childrenLength.value) * step.value;
600
- if (currentOffset > -posEnd) {
601
- isBoundary = true;
602
- resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? translation : 0);
603
- }
604
- if (currentOffset < -posReverseEnd) {
605
- isBoundary = true;
606
- resetOffset = moveStep * elementsLength + patchElmNumShared.value * step.value;
607
- }
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);
608
607
  }
609
608
  return {
610
609
  isBoundary,
611
610
  resetOffset: -resetOffset
612
611
  };
613
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
+ }
614
645
  const gesturePan = Gesture.Pan()
615
646
  .onBegin((e) => {
616
647
  'worklet';
@@ -618,37 +649,47 @@ const SwiperWrapper = forwardRef((props, ref) => {
618
649
  return;
619
650
  touchfinish.value = false;
620
651
  cancelAnimation(offset);
621
- runOnJS(pauseLoop)();
652
+ runOnJS(runOnJSCallback)('pauseLoop');
622
653
  preAbsolutePos.value = e[strAbso];
623
654
  moveTranstion.value = e[strAbso];
624
- anotherDirectionMove.value = e[anotherAbso];
625
- moveTime.value = new Date().getTime();
626
655
  })
627
656
  .onUpdate((e) => {
628
657
  'worklet';
629
- if (touchfinish.value)
630
- return;
631
658
  const moveDistance = e[strAbso] - preAbsolutePos.value;
659
+ if (touchfinish.value || moveDistance === 0)
660
+ return;
632
661
  const eventData = {
633
- translation: moveDistance
662
+ translation: moveDistance,
663
+ transdir: moveDistance
634
664
  };
635
- // 1. 在Move过程中,如果手指一直没抬起来,超过一半的话也会更新索引
636
- const { half } = computeHalf();
637
- if (half) {
665
+ // 1. 支持滑动中超出一半更新索引的能力:只更新索引并不会影响onFinalize依据当前offset计算的索引
666
+ const { half } = computeHalf(eventData);
667
+ if (childrenLength.value > 1 && half) {
638
668
  const { selectedIndex } = getTargetPosition(eventData);
639
669
  currentIndex.value = selectedIndex;
640
670
  }
641
- // 2. 处理用户一直拖拽到临界点的场景, 不会执行onEnd
642
- const { canMove, targetOffset } = checkUnCircular(eventData);
671
+ // 2. 非循环: 处理用户一直拖拽到临界点的场景,如果放到onFinalize无法阻止offset.value更新为越界的值
643
672
  if (!circularShared.value) {
644
- if (canMove) {
645
- offset.value = targetOffset;
646
- preAbsolutePos.value = e[strAbso];
673
+ if (canMove(eventData)) {
674
+ offset.value = moveDistance + offset.value;
647
675
  }
676
+ else {
677
+ const finalOffset = handleResistanceMove(eventData);
678
+ offset.value = finalOffset;
679
+ }
680
+ preAbsolutePos.value = e[strAbso];
681
+ return;
682
+ }
683
+ // 3. 循环更新: 只有一个元素时可滑动,加入阻力
684
+ if (circularShared.value && childrenLength.value === 1) {
685
+ const finalOffset = handleResistanceMove(eventData);
686
+ offset.value = finalOffset;
687
+ preAbsolutePos.value = e[strAbso];
648
688
  return;
649
689
  }
690
+ // 4. 循环更新:正常
650
691
  const { isBoundary, resetOffset } = reachBoundary(eventData);
651
- if (isBoundary && circularShared.value) {
692
+ if (childrenLength.value > 1 && isBoundary && circularShared.value) {
652
693
  offset.value = resetOffset;
653
694
  }
654
695
  else {
@@ -660,26 +701,49 @@ const SwiperWrapper = forwardRef((props, ref) => {
660
701
  'worklet';
661
702
  if (touchfinish.value)
662
703
  return;
663
- const moveDistance = e[strAbso] - moveTranstion.value;
664
704
  touchfinish.value = true;
705
+ // 触发过onUpdate正常情况下e[strAbso] - preAbsolutePos.value=0; 未触发过onUpdate的情况下e[strAbso] - preAbsolutePos.value 不为0
706
+ const moveDistance = e[strAbso] - preAbsolutePos.value;
665
707
  const eventData = {
666
- translation: moveDistance
708
+ translation: moveDistance,
709
+ transdir: moveDistance !== 0 ? moveDistance : e[strAbso] - moveTranstion.value
667
710
  };
668
- const strVelocity = moveDistance / (new Date().getTime() - moveTime.value) * 1000;
669
- if (Math.abs(strVelocity) < longPressRatio) {
670
- handleLongPress();
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里的处理逻辑需要兼容支持,因此这里直接单独处理,不耦合下方公共的判断逻辑。
722
+ if (!circularShared.value && !canMove(eventData)) {
723
+ if (eventData.transdir < 0) {
724
+ handleBack(eventData);
725
+ }
726
+ else {
727
+ handleEnd(eventData);
728
+ }
729
+ return;
730
+ }
731
+ // 3. 非循环状态可移动态、循环状态, 正常逻辑处理
732
+ const velocity = e[strVelocity];
733
+ if (Math.abs(velocity) < longPressRatio) {
734
+ handleLongPress(eventData);
671
735
  }
672
736
  else {
673
- // 如果触发了onTouchesCancelled,不会触发onUpdate不会更新offset值, 索引不会变更
674
737
  handleEnd(eventData);
675
738
  }
676
- }).withRef(swiperGestureRef);
739
+ })
740
+ .withRef(swiperGestureRef);
677
741
  // swiper横向,当y轴滑动5像素手势失效;swiper纵向只响应swiper的滑动事件
678
742
  if (dir === 'x') {
679
- gesturePan.activeOffsetX([-5, 5]).failOffsetY([-5, 5]);
743
+ gesturePan.activeOffsetX([-2, 2]).failOffsetY([-5, 5]);
680
744
  }
681
745
  else {
682
- gesturePan.activeOffsetY([-5, 5]).failOffsetX([-5, 5]);
746
+ gesturePan.activeOffsetY([-2, 2]).failOffsetX([-5, 5]);
683
747
  }
684
748
  // 手势协同2.0
685
749
  if (simultaneousHandlers && simultaneousHandlers.length) {
@@ -700,34 +764,32 @@ const SwiperWrapper = forwardRef((props, ref) => {
700
764
  return { transform: [{ translateY: offset.value }], opacity: step.value > 0 ? 1 : 0 };
701
765
  }
702
766
  });
703
- function renderSwiper() {
704
- const arrPages = renderItems();
705
- return (<View style={[normalStyle, layoutStyle, styles.swiper]} {...layoutProps} {...innerProps}>
706
- <Animated.View style={[{
707
- flexDirection: dir === 'x' ? 'row' : 'column',
708
- width: '100%',
709
- height: '100%'
710
- }, animatedStyles]}>
711
- {wrapChildren({
712
- children: arrPages
713
- }, {
714
- hasVarDec,
715
- varContext: varContextRef.current,
716
- textStyle,
717
- textProps
718
- })}
719
- </Animated.View>
720
- {showsPagination && renderPagination()}
721
- </View>);
722
- }
723
- if (children.length === 1 || disableGesture) {
724
- 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);
725
788
  }
726
- else {
727
- return (<GestureDetector gesture={gestureHandler}>
728
- {renderSwiper()}
729
- </GestureDetector>);
789
+ if (hasPositionFixed) {
790
+ finalComponent = createElement(Portal, null, finalComponent);
730
791
  }
792
+ return finalComponent;
731
793
  });
732
794
  SwiperWrapper.displayName = 'MpxSwiperWrapper';
733
795
  export default SwiperWrapper;
@@ -10,6 +10,7 @@ import { warn } from '@mpxjs/utils';
10
10
  import useNodesRef from './useNodesRef'; // 引入辅助函数
11
11
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
12
12
  import CheckBox from './mpx-checkbox';
13
+ import Portal from './mpx-portal';
13
14
  import { FormContext } from './context';
14
15
  import { useTransformStyle, useLayout, extendObject } from './utils';
15
16
  const _Switch = forwardRef((props, ref) => {
@@ -21,7 +22,7 @@ const _Switch = forwardRef((props, ref) => {
21
22
  if (formContext) {
22
23
  formValuesMap = formContext.formValuesMap;
23
24
  }
24
- const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, {
25
+ const { normalStyle, hasSelfPercent, setWidth, setHeight, hasPositionFixed } = useTransformStyle(style, {
25
26
  enableVar,
26
27
  externalVarContext,
27
28
  parentFontSize,
@@ -83,13 +84,17 @@ const _Switch = forwardRef((props, ref) => {
83
84
  checked: isChecked
84
85
  }));
85
86
  }
86
- return createElement(Switch, extendObject({}, innerProps, {
87
+ let finalComponent = createElement(Switch, extendObject({}, innerProps, {
87
88
  style: normalStyle,
88
89
  value: isChecked,
89
90
  trackColor: { false: '#FFF', true: color },
90
91
  thumbColor: isChecked ? '#FFF' : '#f4f3f4',
91
92
  ios_backgroundColor: '#FFF'
92
93
  }));
94
+ if (hasPositionFixed) {
95
+ finalComponent = createElement(Portal, null, finalComponent);
96
+ }
97
+ return finalComponent;
93
98
  });
94
99
  _Switch.displayName = 'MpxSwitch';
95
100
  export default _Switch;
@@ -5,12 +5,13 @@
5
5
  */
6
6
  import { Text } from 'react-native';
7
7
  import { useRef, forwardRef, createElement } from 'react';
8
+ import Portal from './mpx-portal';
8
9
  import useInnerProps from './getInnerListeners';
9
10
  import useNodesRef from './useNodesRef'; // 引入辅助函数
10
11
  import { useTransformStyle, wrapChildren, extendObject } from './utils';
11
12
  const _Text = forwardRef((props, ref) => {
12
13
  const { style = {}, allowFontScaling = false, selectable, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'user-select': userSelect, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
13
- const { normalStyle, hasVarDec, varContextRef } = useTransformStyle(style, {
14
+ const { normalStyle, hasVarDec, varContextRef, hasPositionFixed } = useTransformStyle(style, {
14
15
  enableVar,
15
16
  externalVarContext,
16
17
  parentFontSize,
@@ -29,10 +30,14 @@ const _Text = forwardRef((props, ref) => {
29
30
  }), [
30
31
  'user-select'
31
32
  ]);
32
- return createElement(Text, innerProps, wrapChildren(props, {
33
+ let finalComponent = createElement(Text, innerProps, wrapChildren(props, {
33
34
  hasVarDec,
34
35
  varContext: varContextRef.current
35
36
  }));
37
+ if (hasPositionFixed) {
38
+ finalComponent = createElement(Portal, null, finalComponent);
39
+ }
40
+ return finalComponent;
36
41
  });
37
42
  _Text.displayName = 'MpxText';
38
43
  export default _Text;