@mpxjs/webpack-plugin 2.10.11 → 2.10.13

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.
@@ -5,7 +5,6 @@ import { VarContext, ScrollViewContext, RouteContext } from './context';
5
5
  import { ExpressionParser, parseFunc, ReplaceSource } from './parser';
6
6
  import { initialWindowMetrics } from 'react-native-safe-area-context';
7
7
  import FastImage from '@d11/react-native-fast-image';
8
- import { runOnJS } from 'react-native-reanimated';
9
8
  import { Gesture } from 'react-native-gesture-handler';
10
9
  export const TEXT_STYLE_REGEX = /color|font.*|text.*|letterSpacing|lineHeight|includeFontPadding|writingDirection/;
11
10
  export const PERCENT_REGEX = /^\s*-?\d+(\.\d+)?%\s*$/;
@@ -663,13 +662,11 @@ export function useHover({ enableHover, hoverStartTime, hoverStayTime, disabled
663
662
  const gesture = useMemo(() => {
664
663
  return Gesture.Pan()
665
664
  .onTouchesDown(() => {
666
- 'worklet';
667
- runOnJS(setStartTimer)();
665
+ setStartTimer();
668
666
  })
669
667
  .onTouchesUp(() => {
670
- 'worklet';
671
- runOnJS(setStayTimer)();
672
- });
668
+ setStayTimer();
669
+ }).runOnJS(true);
673
670
  }, []);
674
671
  if (gestureRef) {
675
672
  gesture.simultaneousWithExternalGesture(gestureRef);
@@ -679,3 +676,17 @@ export function useHover({ enableHover, hoverStartTime, hoverStayTime, disabled
679
676
  gesture
680
677
  };
681
678
  }
679
+ export function useRunOnJSCallback(callbackMapRef) {
680
+ const invokeCallback = useCallback((key, ...args) => {
681
+ const callback = callbackMapRef.current[key];
682
+ // eslint-disable-next-line node/no-callback-literal
683
+ if (isFunction(callback))
684
+ return callback(...args);
685
+ }, []);
686
+ useEffect(() => {
687
+ return () => {
688
+ callbackMapRef.current = {};
689
+ };
690
+ }, []);
691
+ return invokeCallback;
692
+ }
@@ -1,6 +1,7 @@
1
1
  import { useState, ComponentType, useEffect, useCallback, useRef, ReactNode, createElement } from 'react'
2
2
  import { View, Image, StyleSheet, Text, TouchableOpacity } from 'react-native'
3
3
  import FastImage from '@d11/react-native-fast-image'
4
+ import { AnyFunc } from './types/common'
4
5
 
5
6
  const asyncChunkMap = new Map()
6
7
 
@@ -136,10 +137,19 @@ const AsyncSuspense: React.FC<AsyncSuspenseProps> = ({
136
137
  .catch((e) => {
137
138
  if (cancelled) return
138
139
  if (type === 'component') {
139
- global.onLazyLoadError({
140
- type: 'subpackage',
141
- subpackage: [chunkName],
142
- errMsg: `loadSubpackage: ${e.type}`
140
+ global.__mpxAppCbs.lazyLoad.forEach((cb: AnyFunc) => {
141
+ // eslint-disable-next-line node/no-callback-literal
142
+ cb({
143
+ type: 'subpackage',
144
+ subpackage: [chunkName],
145
+ errMsg: `loadSubpackage: ${e.type}`
146
+ })
147
+ })
148
+ }
149
+ if (type === 'page' && typeof mpxGlobal.__mpx.config?.rnConfig?.lazyLoadPageErrorHandler === 'function') {
150
+ mpxGlobal.__mpx.config.rnConfig.lazyLoadPageErrorHandler({
151
+ subpackage: chunkName,
152
+ errType: e.type
143
153
  })
144
154
  }
145
155
  loadChunkPromise.current = null
@@ -1,5 +1,5 @@
1
1
  import { View, NativeSyntheticEvent, LayoutChangeEvent } from 'react-native'
2
- import { GestureDetector, Gesture, PanGesture } from 'react-native-gesture-handler'
2
+ import { GestureDetector, Gesture, PanGesture, GestureStateChangeEvent, PanGestureHandlerEventPayload } from 'react-native-gesture-handler'
3
3
  import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS, useAnimatedReaction, cancelAnimation } from 'react-native-reanimated'
4
4
 
5
5
  import React, { JSX, forwardRef, useRef, useEffect, ReactNode, ReactElement, useMemo, createElement } from 'react'
@@ -26,8 +26,12 @@ import Portal from './mpx-portal'
26
26
  */
27
27
  type EaseType = 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic'
28
28
  type StrAbsoType = 'absoluteX' | 'absoluteY'
29
+ type StrVelocityType = 'velocityX' | 'velocityY'
29
30
  type EventDataType = {
31
+ // 和上一帧offset值的对比
30
32
  translation: number
33
+ // onUpdate时根据上一个判断方向,onFinalize根据transformStart判断
34
+ transdir: number
31
35
  }
32
36
 
33
37
  interface SwiperProps {
@@ -46,7 +50,7 @@ interface SwiperProps {
46
50
  vertical?: boolean
47
51
  style: {
48
52
  [key: string]: any
49
- };
53
+ }
50
54
  'easing-function'?: EaseType
51
55
  'previous-margin'?: string
52
56
  'next-margin'?: string
@@ -54,7 +58,7 @@ interface SwiperProps {
54
58
  'enable-var': boolean
55
59
  'parent-font-size'?: number
56
60
  'parent-width'?: number
57
- 'parent-height'?: number;
61
+ 'parent-height'?: number
58
62
  'external-var-context'?: Record<string, any>
59
63
  'wait-for'?: Array<GestureHandler>
60
64
  'simultaneous-handlers'?: Array<GestureHandler>
@@ -143,7 +147,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
143
147
  style = {},
144
148
  autoplay = false,
145
149
  circular = false,
146
- disableGesture = false
150
+ disableGesture = false,
151
+ bindchange
147
152
  } = props
148
153
  const easeingFunc = props['easing-function'] || 'default'
149
154
  const easeDuration = props.duration || 500
@@ -198,14 +203,13 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
198
203
  // 记录元素的偏移量
199
204
  const offset = useSharedValue(getOffset(props.current || 0, initStep))
200
205
  const strAbso = 'absolute' + dir.toUpperCase() as StrAbsoType
206
+ const strVelocity = 'velocity' + dir.toUpperCase() as StrVelocityType
201
207
  // 标识手指触摸和抬起, 起点在onBegin
202
208
  const touchfinish = useSharedValue(true)
203
209
  // 记录上一帧的绝对定位坐标
204
210
  const preAbsolutePos = useSharedValue(0)
205
211
  // 记录从onBegin 到 onTouchesUp 时移动的距离
206
212
  const moveTranstion = useSharedValue(0)
207
- // 记录从onBegin 到 onTouchesUp 的时间
208
- const moveTime = useSharedValue(0)
209
213
  const timerId = useRef(0 as number | ReturnType<typeof setTimeout>)
210
214
  const intervalTimer = props.interval || 500
211
215
 
@@ -427,7 +431,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
427
431
  function handleSwiperChange (current: number) {
428
432
  if (props.current !== currentIndex.value) {
429
433
  const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef })
430
- props.bindchange && props.bindchange(eventData)
434
+ bindchange && bindchange(eventData)
431
435
  }
432
436
  }
433
437
 
@@ -469,7 +473,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
469
473
  // 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画
470
474
  useAnimatedReaction(() => currentIndex.value, (newIndex: number, preIndex: number) => {
471
475
  // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
472
- if (newIndex !== preIndex && props.bindchange) {
476
+ if (newIndex !== preIndex && bindchange) {
473
477
  runOnJS(handleSwiperChange)(newIndex)
474
478
  }
475
479
  })
@@ -504,7 +508,11 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
504
508
  }, [children.length])
505
509
 
506
510
  useEffect(() => {
507
- updateCurrent(props.current || 0, step.value)
511
+ // 1. 如果用户在touch的过程中, 外部更新了current以外部为准(小程序表现)
512
+ // 2. 手指滑动过程中更新索引,外部会把current再传入进来,导致offset直接更新,增加判断不同才更新
513
+ if (props.current !== currentIndex.value) {
514
+ updateCurrent(props.current || 0, step.value)
515
+ }
508
516
  }, [props.current])
509
517
 
510
518
  useEffect(() => {
@@ -528,7 +536,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
528
536
  function getTargetPosition (eventData: EventDataType) {
529
537
  'worklet'
530
538
  // 移动的距离
531
- const { translation } = eventData
539
+ const { transdir } = eventData
532
540
  let resetOffsetPos = 0
533
541
  let selectedIndex = currentIndex.value
534
542
  // 是否临界点
@@ -536,9 +544,9 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
536
544
  // 真实滚动到的偏移量坐标
537
545
  let moveToTargetPos = 0
538
546
  const tmp = !circularShared.value ? 0 : preMarginShared.value
539
- const currentOffset = translation < 0 ? offset.value - tmp : offset.value + tmp
547
+ const currentOffset = transdir < 0 ? offset.value - tmp : offset.value + tmp
540
548
  const computedIndex = Math.abs(currentOffset) / step.value
541
- const moveToIndex = translation < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex)
549
+ const moveToIndex = transdir < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex)
542
550
  // 实际应该定位的索引值
543
551
  if (!circularShared.value) {
544
552
  selectedIndex = moveToIndex
@@ -568,13 +576,17 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
568
576
  }
569
577
  function canMove (eventData: EventDataType) {
570
578
  'worklet'
571
- const { translation } = eventData
572
- const currentOffset = Math.abs(offset.value)
579
+ // 旧版:如果在快速多次滑动时,只根据当前的offset判断,会出现offset没超出,加上translation后越界的场景(如在倒数第二个元素快速滑动)
580
+ // 新版:会加上translation
581
+ const { translation, transdir } = eventData
582
+ const gestureMovePos = offset.value + translation
573
583
  if (!circularShared.value) {
574
- if (translation < 0) {
575
- return currentOffset < step.value * (childrenLength.value - 1)
584
+ // 如果只判断区间,中间非滑动状态(handleResistanceMove)向左滑动,突然改为向右滑动,但是还在非滑动态,本应该可滑动判断为了不可滑动
585
+ const posEnd = -step.value * (childrenLength.value - 1)
586
+ if (transdir < 0) {
587
+ return gestureMovePos > posEnd
576
588
  } else {
577
- return currentOffset > 0
589
+ return gestureMovePos < 0
578
590
  }
579
591
  } else {
580
592
  return true
@@ -606,25 +618,16 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
606
618
  })
607
619
  }
608
620
  }
609
- function handleBackInit () {
610
- 'worklet'
611
- // 微信的效果
612
- // 1. 只有一个元素,即使设置了circular,也不会产生循环的效果,2. 可以响应手势,但是会有回弹的效果
613
- offset.value = withTiming(0, {
614
- duration: easeDuration,
615
- easing: easeMap[easeingFunc]
616
- })
617
- }
618
621
  function handleBack (eventData: EventDataType) {
619
622
  'worklet'
620
- const { translation } = eventData
623
+ const { transdir } = eventData
621
624
  // 向右滑动的back:trans < 0, 向左滑动的back: trans < 0
622
625
  let currentOffset = Math.abs(offset.value)
623
626
  if (circularShared.value) {
624
- currentOffset += translation < 0 ? preMarginShared.value : -preMarginShared.value
627
+ currentOffset += transdir < 0 ? preMarginShared.value : -preMarginShared.value
625
628
  }
626
629
  const curIndex = currentOffset / step.value
627
- const moveToIndex = (translation < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElmNumShared.value
630
+ const moveToIndex = (transdir < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElmNumShared.value
628
631
  const targetOffset = -(moveToIndex + patchElmNumShared.value) * step.value + (circularShared.value ? preMarginShared.value : 0)
629
632
  offset.value = withTiming(targetOffset, {
630
633
  duration: easeDuration,
@@ -636,64 +639,108 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
636
639
  }
637
640
  })
638
641
  }
639
- function handleLongPress () {
642
+ // 当前的offset和index多对应的offset进行对比,判断是否超过一半
643
+ function computeHalf (eventData: EventDataType) {
640
644
  'worklet'
645
+ const { transdir } = eventData
641
646
  const currentOffset = Math.abs(offset.value)
642
647
  let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value
643
648
  if (circularShared.value) {
644
649
  preOffset -= preMarginShared.value
645
650
  }
646
- // 正常事件中拿到的transition值(正向滑动<0,倒着滑>0)
651
+ // 正常事件中拿到的translation值(正向滑动<0,倒着滑>0)
647
652
  const diffOffset = preOffset - currentOffset
648
653
  const half = Math.abs(diffOffset) > step.value / 2
654
+ const isTriggerUpdateHalf = (transdir < 0 && currentOffset < preOffset) || (transdir > 0 && currentOffset > preOffset)
655
+ return {
656
+ diffOffset,
657
+ half,
658
+ isTriggerUpdateHalf
659
+ }
660
+ }
661
+ function handleLongPress (eventData: EventDataType) {
662
+ 'worklet'
663
+ const { diffOffset, half, isTriggerUpdateHalf } = computeHalf(eventData)
649
664
  if (+diffOffset === 0) {
650
665
  runOnJS(resumeLoop)()
666
+ } else if (isTriggerUpdateHalf) {
667
+ // 如果触发了onUpdate时的索引变更,则直接以update时的index为准
668
+ const targetIndex = !circularShared.value ? currentIndex.value : currentIndex.value + patchElmNumShared.value - 1
669
+ offset.value = withTiming(-targetIndex * step.value, {
670
+ duration: easeDuration,
671
+ easing: easeMap[easeingFunc]
672
+ }, () => {
673
+ if (touchfinish.value !== false) {
674
+ currentIndex.value = targetIndex
675
+ runOnJS(resumeLoop)()
676
+ }
677
+ })
651
678
  } else if (half) {
652
- handleEnd({ translation: diffOffset })
679
+ handleEnd(eventData)
653
680
  } else {
654
- handleBack({ translation: diffOffset })
681
+ handleBack(eventData)
655
682
  }
656
683
  }
657
684
  function reachBoundary (eventData: EventDataType) {
658
685
  'worklet'
659
- // 移动的距离
686
+ // 1. 基于当前的offset和translation判断是否超过当前边界值
660
687
  const { translation } = eventData
661
- const elementsLength = step.value * childrenLength.value
688
+ const boundaryStart = -patchElmNumShared.value * step.value
689
+ const boundaryEnd = -(childrenLength.value + patchElmNumShared.value) * step.value
690
+ const moveToOffset = offset.value + translation
662
691
  let isBoundary = false
663
692
  let resetOffset = 0
664
- // Y轴向下滚动, transDistance > 0, 向上滚动 < 0 X轴向左滚动, transDistance > 0
665
- const currentOffset = offset.value
666
- const moveStep = Math.ceil(translation / elementsLength)
667
- if (translation < 0) {
668
- const posEnd = (childrenLength.value + patchElmNumShared.value + 1) * step.value
669
- const posReverseEnd = (patchElmNumShared.value - 1) * step.value
670
- if (currentOffset < -posEnd + step.value) {
671
- isBoundary = true
672
- resetOffset = Math.abs(moveStep) === 0 ? patchElmNumShared.value * step.value + translation : moveStep * elementsLength
673
- }
674
- if (currentOffset > -posReverseEnd) {
675
- isBoundary = true
676
- resetOffset = moveStep * elementsLength
677
- }
678
- } else if (translation > 0) {
679
- const posEnd = (patchElmNumShared.value - 1) * step.value
680
- const posReverseEnd = (patchElmNumShared.value + childrenLength.value) * step.value
681
- if (currentOffset > -posEnd) {
682
- isBoundary = true
683
- resetOffset = moveStep * elementsLength + step.value + (moveStep === 1 ? translation : 0)
684
- }
685
- if (currentOffset < -posReverseEnd) {
686
- isBoundary = true
687
- resetOffset = moveStep * elementsLength + patchElmNumShared.value * step.value
688
- }
693
+ if (moveToOffset < boundaryEnd) {
694
+ isBoundary = true
695
+ // 超过边界的距离
696
+ const exceedLength = Math.abs(moveToOffset) - Math.abs(boundaryEnd)
697
+ // 计算对标正常元素所在的offset
698
+ resetOffset = patchElmNumShared.value * step.value + exceedLength
699
+ }
700
+ if (moveToOffset > boundaryStart) {
701
+ isBoundary = true
702
+ // 超过边界的距离
703
+ const exceedLength = Math.abs(boundaryStart) - Math.abs(moveToOffset)
704
+ // 计算对标正常元素所在的offset
705
+ resetOffset = (patchElmNumShared.value + childrenLength.value - 1) * step.value + (step.value - exceedLength)
689
706
  }
690
707
  return {
691
708
  isBoundary,
692
709
  resetOffset: -resetOffset
693
710
  }
694
711
  }
712
+ // 非循环超出边界,应用阻力; 开始滑动少阻力小,滑动越长阻力越大
713
+ function handleResistanceMove (eventData: EventDataType) {
714
+ 'worklet'
715
+ const { translation, transdir } = eventData
716
+ const moveToOffset = offset.value + translation
717
+ const maxOverDrag = Math.floor(step.value / 2)
718
+ const maxOffset = translation < 0 ? -(childrenLength.value - 1) * step.value : 0
719
+ let resistance = 0.1
720
+ let overDrag = 0
721
+ let finalOffset = 0
722
+ // 向右向下小于0, 向左向上大于0;
723
+ if (transdir < 0) {
724
+ overDrag = Math.abs(moveToOffset - maxOffset)
725
+ } else {
726
+ overDrag = Math.abs(moveToOffset)
727
+ }
728
+ // 滑动越多resistance越小
729
+ resistance = 1 - overDrag / maxOverDrag
730
+ // 确保阻力在合理范围内
731
+ resistance = Math.min(0.5, resistance)
732
+ // 限制在最大拖拽范围内
733
+ if (transdir < 0) {
734
+ const adjustOffset = offset.value + translation * resistance
735
+ finalOffset = Math.max(adjustOffset, maxOffset - maxOverDrag)
736
+ } else {
737
+ const adjustOffset = offset.value + translation * resistance
738
+ finalOffset = Math.min(adjustOffset, maxOverDrag)
739
+ }
740
+ return finalOffset
741
+ }
695
742
  const gesturePan = Gesture.Pan()
696
- .onBegin((e) => {
743
+ .onBegin((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
697
744
  'worklet'
698
745
  if (!step.value) return
699
746
  touchfinish.value = false
@@ -701,47 +748,81 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
701
748
  runOnJS(pauseLoop)()
702
749
  preAbsolutePos.value = e[strAbso]
703
750
  moveTranstion.value = e[strAbso]
704
- moveTime.value = new Date().getTime()
705
751
  })
706
- .onTouchesMove((e) => {
752
+ .onUpdate((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
707
753
  'worklet'
708
754
  if (touchfinish.value) return
709
- const touchEventData = e.changedTouches[0]
710
- const moveDistance = touchEventData[strAbso] - preAbsolutePos.value
755
+ const moveDistance = e[strAbso] - preAbsolutePos.value
711
756
  const eventData = {
712
- translation: moveDistance
757
+ translation: moveDistance,
758
+ transdir: moveDistance !== 0 ? moveDistance : e[strAbso] - moveTranstion.value
713
759
  }
714
- // 处理用户一直拖拽到临界点的场景, 不会执行onEnd
715
- if (!circularShared.value && !canMove(eventData)) {
760
+ // 1. 支持滑动中超出一半更新索引的能力:只更新索引并不会影响onFinalize依据当前offset计算的索引
761
+ const { half } = computeHalf(eventData)
762
+ if (childrenLength.value > 1 && half) {
763
+ const { selectedIndex } = getTargetPosition(eventData)
764
+ currentIndex.value = selectedIndex
765
+ }
766
+ // 2. 非循环: 处理用户一直拖拽到临界点的场景,如果放到onFinalize无法阻止offset.value更新为越界的值
767
+ if (!circularShared.value) {
768
+ if (canMove(eventData)) {
769
+ offset.value = moveDistance + offset.value
770
+ } else {
771
+ const finalOffset = handleResistanceMove(eventData)
772
+ offset.value = finalOffset
773
+ }
774
+ preAbsolutePos.value = e[strAbso]
716
775
  return
717
776
  }
777
+ // 3. 循环更新: 只有一个元素时可滑动,加入阻力
778
+ if (circularShared.value && childrenLength.value === 1) {
779
+ const finalOffset = handleResistanceMove(eventData)
780
+ offset.value = finalOffset
781
+ preAbsolutePos.value = e[strAbso]
782
+ return
783
+ }
784
+ // 4. 循环更新:正常
718
785
  const { isBoundary, resetOffset } = reachBoundary(eventData)
719
786
  if (childrenLength.value > 1 && isBoundary && circularShared.value) {
720
787
  offset.value = resetOffset
721
788
  } else {
722
789
  offset.value = moveDistance + offset.value
723
790
  }
724
- preAbsolutePos.value = touchEventData[strAbso]
791
+ preAbsolutePos.value = e[strAbso]
725
792
  })
726
- .onTouchesUp((e) => {
793
+ .onFinalize((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
727
794
  'worklet'
728
795
  if (touchfinish.value) return
729
- const touchEventData = e.changedTouches[0]
730
- const moveDistance = touchEventData[strAbso] - moveTranstion.value
731
796
  touchfinish.value = true
797
+ // 触发过onUpdate正常情况下e[strAbso] - preAbsolutePos.value=0; 未触发过onUpdate的情况下e[strAbso] - preAbsolutePos.value 不为0
798
+ const moveDistance = e[strAbso] - preAbsolutePos.value
732
799
  const eventData = {
733
- translation: moveDistance
800
+ translation: moveDistance,
801
+ transdir: moveDistance !== 0 ? moveDistance : e[strAbso] - moveTranstion.value
734
802
  }
803
+ // 1. 只有一个元素:循环 和 非循环状态,都走回弹效果
735
804
  if (childrenLength.value === 1) {
736
- return handleBackInit()
805
+ offset.value = withTiming(0, {
806
+ duration: easeDuration,
807
+ easing: easeMap[easeingFunc]
808
+ })
809
+ return
737
810
  }
738
- // 用户手指按下起来, 需要计算正确的位置, 比如在滑动过程中突然按下然后起来,需要计算到正确的位置
811
+ // 2.非循环状态不可移动态:最后一个元素 第一个元素
812
+ // 非循环支持最后元素可滑动能力后,向左快速移动未超过最大可移动范围一半,因为offset为正值,向左滑动handleBack,默认向上取整
813
+ // 但是在offset大于0时,取0。[-100, 0](back取0), [0, 100](back取1), 所以handleLongPress里的处理逻辑需要兼容支持,因此这里直接单独处理,不耦合下方公共的判断逻辑。
739
814
  if (!circularShared.value && !canMove(eventData)) {
815
+ if (eventData.transdir < 0) {
816
+ handleBack(eventData)
817
+ } else {
818
+ handleEnd(eventData)
819
+ }
740
820
  return
741
821
  }
742
- const strVelocity = moveDistance / (new Date().getTime() - moveTime.value) * 1000
743
- if (Math.abs(strVelocity) < longPressRatio) {
744
- handleLongPress()
822
+ // 3. 非循环状态可移动态、循环状态, 正常逻辑处理
823
+ const velocity = e[strVelocity]
824
+ if (Math.abs(velocity) < longPressRatio) {
825
+ handleLongPress(eventData)
745
826
  } else {
746
827
  handleEnd(eventData)
747
828
  }
@@ -40,6 +40,8 @@ declare let global: {
40
40
  __formatValue (value: string): string | number
41
41
  } & Record<string, any>
42
42
 
43
+ declare let mpxGlobal: Record<string, any>
44
+
43
45
  declare module '@react-navigation/native' {
44
46
  export function useNavigation (): Record<string, any>
45
47
  export function usePreventRemove(
@@ -14,6 +14,7 @@ import {
14
14
  } from 'react-native-reanimated'
15
15
  import type { AnimationCallback, WithTimingConfig, SharedValue, AnimatableValue } from 'react-native-reanimated'
16
16
  import { error, hasOwn, collectDataset } from '@mpxjs/utils'
17
+ import { useRunOnJSCallback } from './utils'
17
18
  import { ExtendedViewStyle } from './types/common'
18
19
  import type { _ViewProps } from './mpx-view'
19
20
 
@@ -218,13 +219,19 @@ export default function useAnimationHooks<T, P> (props: _ViewProps & { enableAni
218
219
  timeStamp: Date.now()
219
220
  })
220
221
  }
222
+ // eslint-disable-next-line react-hooks/rules-of-hooks
223
+ const runOnJSCallbackRef = useRef({
224
+ withTimingCallback
225
+ })
226
+ // eslint-disable-next-line react-hooks/rules-of-hooks
227
+ const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
221
228
  // 创建单个animation
222
229
  function getAnimation ({ key, value }: { key: string, value: string|number }, { delay, duration, easing }: ExtendWithTimingConfig, callback?: AnimationCallback) {
223
230
  const animation = typeof callback === 'function'
224
231
  ? withTiming(value, { duration, easing }, (finished, current) => {
225
232
  callback(finished, current)
226
233
  if (transitionend && finished) {
227
- runOnJS(withTimingCallback)(finished, current, duration)
234
+ runOnJS(runOnJSCallback)('withTimingCallback', finished, current, duration)
228
235
  }
229
236
  })
230
237
  : withTiming(value, { duration, easing })
@@ -1,4 +1,4 @@
1
- import { useEffect, useCallback, useMemo, useRef, ReactNode, ReactElement, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement, createElement } from 'react'
1
+ import { useEffect, useCallback, useMemo, useRef, ReactNode, ReactElement, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement, createElement, MutableRefObject } from 'react'
2
2
  import { LayoutChangeEvent, TextStyle, ImageProps, Image } from 'react-native'
3
3
  import { isObject, isFunction, isNumber, hasOwn, diffAndCloneA, error, warn } from '@mpxjs/utils'
4
4
  import { VarContext, ScrollViewContext, RouteContext } from './context'
@@ -787,13 +787,11 @@ export function useHover ({ enableHover, hoverStartTime, hoverStayTime, disabled
787
787
  const gesture = useMemo(() => {
788
788
  return Gesture.Pan()
789
789
  .onTouchesDown(() => {
790
- 'worklet'
791
- runOnJS(setStartTimer)()
790
+ setStartTimer()
792
791
  })
793
792
  .onTouchesUp(() => {
794
- 'worklet'
795
- runOnJS(setStayTimer)()
796
- })
793
+ setStayTimer()
794
+ }).runOnJS(true)
797
795
  }, [])
798
796
 
799
797
  if (gestureRef) {
@@ -805,3 +803,19 @@ export function useHover ({ enableHover, hoverStartTime, hoverStayTime, disabled
805
803
  gesture
806
804
  }
807
805
  }
806
+
807
+ export function useRunOnJSCallback (callbackMapRef: MutableRefObject<Record<string, AnyFunc>>) {
808
+ const invokeCallback = useCallback((key: string, ...args: any) => {
809
+ const callback = callbackMapRef.current[key]
810
+ // eslint-disable-next-line node/no-callback-literal
811
+ if (isFunction(callback)) return callback(...args)
812
+ }, [])
813
+
814
+ useEffect(() => {
815
+ return () => {
816
+ callbackMapRef.current = {}
817
+ }
818
+ }, [])
819
+
820
+ return invokeCallback
821
+ }
@@ -49,7 +49,7 @@ import { processAppOption, getComponent } from ${stringifyRequest(loaderContext,
49
49
  Vue.use(VueRouter)\n`
50
50
 
51
51
  if (i18n) {
52
- output += buildI18n({ i18n, loaderContext })
52
+ output += buildI18n({ i18n, isMain: true, loaderContext })
53
53
  }
54
54
 
55
55
  output += buildGlobalParams({
@@ -7,7 +7,8 @@ const {
7
7
  buildComponentsMap,
8
8
  getRequireScript,
9
9
  buildGlobalParams,
10
- stringifyRequest
10
+ stringifyRequest,
11
+ buildI18n
11
12
  } = require('./script-helper')
12
13
 
13
14
  module.exports = function (script, {
@@ -24,7 +25,7 @@ module.exports = function (script, {
24
25
  wxsModuleMap,
25
26
  localComponentsMap
26
27
  }, callback) {
27
- const { projectRoot, appInfo, webConfig } = loaderContext.getMpx()
28
+ const { projectRoot, appInfo, webConfig, i18n } = loaderContext.getMpx()
28
29
 
29
30
  let output = '/* script */\n'
30
31
 
@@ -70,6 +71,9 @@ module.exports = function (script, {
70
71
  }
71
72
 
72
73
  content += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, webConfig, hasApp })
74
+ if (!hasApp && i18n) {
75
+ content += buildI18n({ i18n, loaderContext })
76
+ }
73
77
  content += getRequireScript({ ctorType, script, loaderContext })
74
78
  content += `
75
79
  export default processComponentOption({
@@ -186,13 +186,17 @@ function buildGlobalParams ({
186
186
  return content
187
187
  }
188
188
 
189
- function buildI18n ({ i18n, loaderContext }) {
189
+ function buildI18n ({ i18n, isMain, loaderContext }) {
190
190
  let i18nContent = ''
191
191
  const i18nObj = Object.assign({}, i18n)
192
- i18nContent += `
193
- import VueI18n from 'vue-i18n'
192
+ if (!isMain) {
193
+ i18nContent += `import Vue from 'vue'
194
+ import Mpx from '@mpxjs/core'\n`
195
+ }
196
+ i18nContent += `import VueI18n from 'vue-i18n'
194
197
  import { createI18n } from 'vue-i18n-bridge'
195
- Vue.use(VueI18n , { bridge: true })\n`
198
+ if (!Mpx.i18n) {
199
+ Vue.use(VueI18n , { bridge: true })\n`
196
200
  const requestObj = {}
197
201
  const i18nKeys = ['messages', 'dateTimeFormats', 'numberFormats']
198
202
  i18nKeys.forEach((key) => {
@@ -201,15 +205,16 @@ function buildI18n ({ i18n, loaderContext }) {
201
205
  delete i18nObj[`${key}Path`]
202
206
  }
203
207
  })
204
- i18nContent += ` var i18nCfg = ${JSON.stringify(i18nObj)}\n`
208
+ i18nContent += ` var i18nCfg = ${JSON.stringify(i18nObj)}\n`
205
209
  Object.keys(requestObj).forEach((key) => {
206
- i18nContent += ` i18nCfg.${key} = require(${requestObj[key]})\n`
210
+ i18nContent += ` i18nCfg.${key} = require(${requestObj[key]})\n`
207
211
  })
208
212
  i18nContent += `
209
- i18nCfg.legacy = false
210
- var i18n = createI18n(i18nCfg, VueI18n)
211
- Vue.use(i18n)
212
- Mpx.i18n = i18n\n`
213
+ i18nCfg.legacy = false
214
+ var i18n = createI18n(i18nCfg, VueI18n)
215
+ Vue.use(i18n)
216
+ Mpx.i18n = i18n
217
+ }\n`
213
218
  return i18nContent
214
219
  }
215
220