@mpxjs/webpack-plugin 2.10.14-beta.1 → 2.10.14-beta.11

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 (107) hide show
  1. package/lib/dependencies/ImportDependency.js +102 -0
  2. package/lib/index.js +9 -9
  3. package/lib/platform/style/wx/index.js +7 -1
  4. package/lib/platform/template/wx/component-config/button.js +13 -4
  5. package/lib/platform/template/wx/component-config/index.js +3 -1
  6. package/lib/platform/template/wx/component-config/nav-container.js +27 -0
  7. package/lib/react/processScript.js +4 -2
  8. package/lib/react/script-helper.js +3 -3
  9. package/lib/runtime/components/ali/mpx-nav-container.mpx +3 -0
  10. package/lib/runtime/components/react/context.ts +17 -6
  11. package/lib/runtime/components/react/dist/context.d.ts +78 -0
  12. package/lib/runtime/components/react/dist/context.js +1 -0
  13. package/lib/runtime/components/react/dist/event.config.d.ts +7 -0
  14. package/lib/runtime/components/react/dist/getInnerListeners.d.ts +7 -0
  15. package/lib/runtime/components/react/dist/mpx-async-suspense.d.ts +12 -0
  16. package/lib/runtime/components/react/dist/mpx-button.d.ts +68 -0
  17. package/lib/runtime/components/react/dist/mpx-canvas/Bus.d.ts +23 -0
  18. package/lib/runtime/components/react/dist/mpx-canvas/CanvasGradient.d.ts +7 -0
  19. package/lib/runtime/components/react/dist/mpx-canvas/CanvasRenderingContext2D.d.ts +6 -0
  20. package/lib/runtime/components/react/dist/mpx-canvas/Image.d.ts +20 -0
  21. package/lib/runtime/components/react/dist/mpx-canvas/ImageData.d.ts +8 -0
  22. package/lib/runtime/components/react/dist/mpx-canvas/constructorsRegistry.d.ts +10 -0
  23. package/lib/runtime/components/react/dist/mpx-canvas/html.d.ts +2 -0
  24. package/lib/runtime/components/react/dist/mpx-canvas/index.d.ts +32 -0
  25. package/lib/runtime/components/react/dist/mpx-canvas/utils.d.ts +52 -0
  26. package/lib/runtime/components/react/dist/mpx-checkbox-group.d.ts +20 -0
  27. package/lib/runtime/components/react/dist/mpx-checkbox.d.ts +32 -0
  28. package/lib/runtime/components/react/dist/mpx-form.d.ts +27 -0
  29. package/lib/runtime/components/react/dist/mpx-icon/index.d.ts +18 -0
  30. package/lib/runtime/components/react/dist/mpx-image.d.ts +21 -0
  31. package/lib/runtime/components/react/dist/mpx-inline-text.d.ts +7 -0
  32. package/lib/runtime/components/react/dist/mpx-input.d.ts +50 -0
  33. package/lib/runtime/components/react/dist/mpx-input.jsx +36 -17
  34. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.d.ts +12 -0
  35. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +69 -48
  36. package/lib/runtime/components/react/dist/mpx-label.d.ts +20 -0
  37. package/lib/runtime/components/react/dist/mpx-movable-area.d.ts +20 -0
  38. package/lib/runtime/components/react/dist/mpx-movable-view.d.ts +63 -0
  39. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +24 -17
  40. package/lib/runtime/components/react/dist/mpx-nav-container.d.ts +9 -0
  41. package/lib/runtime/components/react/dist/mpx-nav-container.jsx +23 -0
  42. package/lib/runtime/components/react/dist/mpx-navigator.d.ts +9 -0
  43. package/lib/runtime/components/react/dist/mpx-picker/date.d.ts +6 -0
  44. package/lib/runtime/components/react/dist/mpx-picker/dateData.d.ts +7 -0
  45. package/lib/runtime/components/react/dist/mpx-picker/index.d.ts +6 -0
  46. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.d.ts +6 -0
  47. package/lib/runtime/components/react/dist/mpx-picker/region.d.ts +6 -0
  48. package/lib/runtime/components/react/dist/mpx-picker/regionData.d.ts +2 -0
  49. package/lib/runtime/components/react/dist/mpx-picker/selector.d.ts +6 -0
  50. package/lib/runtime/components/react/dist/mpx-picker/time.d.ts +6 -0
  51. package/lib/runtime/components/react/dist/mpx-picker/type.d.ts +106 -0
  52. package/lib/runtime/components/react/dist/mpx-picker-view/index.d.ts +31 -0
  53. package/lib/runtime/components/react/dist/mpx-picker-view/pickerVIewContext.d.ts +8 -0
  54. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.d.ts +22 -0
  55. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItem.d.ts +14 -0
  56. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewFaces.d.ts +16 -0
  57. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewIndicator.d.ts +12 -0
  58. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewMask.d.ts +11 -0
  59. package/lib/runtime/components/react/dist/mpx-popup/index.d.ts +22 -0
  60. package/lib/runtime/components/react/dist/mpx-popup/popupBase.d.ts +16 -0
  61. package/lib/runtime/components/react/dist/mpx-portal/index.d.ts +15 -0
  62. package/lib/runtime/components/react/dist/mpx-portal/portal-host.d.ts +29 -0
  63. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.d.ts +9 -0
  64. package/lib/runtime/components/react/dist/mpx-radio-group.d.ts +20 -0
  65. package/lib/runtime/components/react/dist/mpx-radio.d.ts +26 -0
  66. package/lib/runtime/components/react/dist/mpx-rich-text/html.d.ts +1 -0
  67. package/lib/runtime/components/react/dist/mpx-rich-text/index.d.ts +24 -0
  68. package/lib/runtime/components/react/dist/mpx-root-portal.d.ts +14 -0
  69. package/lib/runtime/components/react/dist/mpx-scroll-view.d.ts +54 -0
  70. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +26 -8
  71. package/lib/runtime/components/react/dist/mpx-simple-text.d.ts +7 -0
  72. package/lib/runtime/components/react/dist/mpx-simple-view.d.ts +7 -0
  73. package/lib/runtime/components/react/dist/mpx-sticky-header.d.ts +17 -0
  74. package/lib/runtime/components/react/dist/mpx-sticky-section.d.ts +15 -0
  75. package/lib/runtime/components/react/dist/mpx-swiper-item.d.ts +18 -0
  76. package/lib/runtime/components/react/dist/mpx-swiper.d.ts +52 -0
  77. package/lib/runtime/components/react/dist/mpx-swiper.jsx +48 -39
  78. package/lib/runtime/components/react/dist/mpx-switch.d.ts +26 -0
  79. package/lib/runtime/components/react/dist/mpx-text.d.ts +21 -0
  80. package/lib/runtime/components/react/dist/mpx-textarea.d.ts +7 -0
  81. package/lib/runtime/components/react/dist/mpx-video.d.ts +101 -0
  82. package/lib/runtime/components/react/dist/mpx-view.d.ts +34 -0
  83. package/lib/runtime/components/react/dist/mpx-view.jsx +1 -1
  84. package/lib/runtime/components/react/dist/mpx-web-view.d.ts +22 -0
  85. package/lib/runtime/components/react/dist/nav.d.ts +8 -0
  86. package/lib/runtime/components/react/dist/nav.jsx +137 -0
  87. package/lib/runtime/components/react/dist/parser.d.ts +39 -0
  88. package/lib/runtime/components/react/dist/useAnimationHooks.d.ts +32 -0
  89. package/lib/runtime/components/react/dist/useNavShared.d.ts +2 -0
  90. package/lib/runtime/components/react/dist/useNavShared.js +6 -0
  91. package/lib/runtime/components/react/dist/useNodesRef.d.ts +11 -0
  92. package/lib/runtime/components/react/dist/utils.d.ts +122 -0
  93. package/lib/runtime/components/react/mpx-input.tsx +46 -24
  94. package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +78 -47
  95. package/lib/runtime/components/react/mpx-movable-view.tsx +26 -20
  96. package/lib/runtime/components/react/mpx-nav-container.tsx +33 -0
  97. package/lib/runtime/components/react/mpx-scroll-view.tsx +30 -8
  98. package/lib/runtime/components/react/mpx-swiper.tsx +48 -37
  99. package/lib/runtime/components/react/mpx-view.tsx +1 -1
  100. package/lib/runtime/components/react/nav.tsx +163 -0
  101. package/lib/runtime/components/react/types/common.d.ts +19 -0
  102. package/lib/runtime/components/react/useNavShared.ts +8 -0
  103. package/lib/runtime/components/web/mpx-nav-container.vue +13 -0
  104. package/lib/runtime/components/wx/mpx-nav-container.mpx +9 -0
  105. package/lib/utils/dom-tag-config.js +2 -2
  106. package/package.json +1 -1
  107. package/lib/dependencies/ImportDependencyTemplate.js +0 -50
@@ -1,8 +1,9 @@
1
- import React, { ReactNode, useContext, useEffect } from 'react'
1
+ /* eslint-disable space-before-function-paren */
2
+ import React, { ReactNode, useContext, useEffect, useRef } from 'react'
2
3
  import { DimensionValue, EmitterSubscription, Keyboard, View, ViewStyle, NativeSyntheticEvent, NativeTouchEvent } from 'react-native'
3
- import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated'
4
+ import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing, cancelAnimation } from 'react-native-reanimated'
4
5
  import { KeyboardAvoidContext } from './context'
5
- import { isIOS } from './utils'
6
+ import { isAndroid, isIOS } from './utils'
6
7
 
7
8
  type KeyboardAvoidViewProps = {
8
9
  children?: ReactNode
@@ -18,15 +19,38 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
18
19
  const basic = useSharedValue('auto')
19
20
  const keyboardAvoid = useContext(KeyboardAvoidContext)
20
21
 
22
+ // fix: 某些特殊机型下隐藏键盘可能会先触发一次 keyboardWillShow,
23
+ // 比如机型 iPhone 11 Pro,可能会导致显隐动画冲突
24
+ // 因此增加状态标记 + clearTimeout + cancelAnimation 来优化
25
+ const isShow = useRef<boolean>(false)
26
+ const timerRef = useRef<NodeJS.Timeout | null>(null)
27
+
21
28
  const animatedStyle = useAnimatedStyle(() => ({
29
+ // translate/position top可能会导致底部渲染区域缺失(需要 android 配置聚焦时禁用高度缩小),margin-top 会导致 portal 的定位失效,无法顶起 portal
22
30
  transform: [{ translateY: -offset.value }],
23
31
  flexBasis: basic.value as DimensionValue
24
32
  }))
25
33
 
26
34
  const resetKeyboard = () => {
35
+ if (!isShow.current) {
36
+ return
37
+ }
38
+
39
+ isShow.current = false
40
+ timerRef.current && clearTimeout(timerRef.current)
41
+
27
42
  if (keyboardAvoid?.current) {
28
- keyboardAvoid.current = null
43
+ const inputRef = keyboardAvoid.current.ref?.current
44
+ if (inputRef && inputRef.isFocused()) {
45
+ // 修复 Android 点击键盘收起按钮时当前 input 没触发失焦的问题
46
+ inputRef.blur()
47
+ }
48
+ if (!keyboardAvoid.current.onKeyboardShow) {
49
+ keyboardAvoid.current = null
50
+ }
29
51
  }
52
+
53
+ cancelAnimation(offset)
30
54
  offset.value = withTiming(0, { duration, easing })
31
55
  basic.value = 'auto'
32
56
  }
@@ -40,68 +64,75 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
40
64
  useEffect(() => {
41
65
  let subscriptions: EmitterSubscription[] = []
42
66
 
43
- if (isIOS) {
44
- subscriptions = [
45
- Keyboard.addListener('keyboardWillShow', (evt: any) => {
46
- if (!keyboardAvoid?.current) return
47
- const { endCoordinates } = evt
48
- const { ref, cursorSpacing = 0 } = keyboardAvoid.current
49
- setTimeout(() => {
50
- ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
67
+ function keybaordAvoding(evt: any, ios = false) {
68
+ if (!keyboardAvoid?.current || isShow.current) {
69
+ return
70
+ }
71
+
72
+ isShow.current = true
73
+
74
+ if (ios) {
75
+ timerRef.current && clearTimeout(timerRef.current)
76
+ }
77
+
78
+ const { endCoordinates } = evt
79
+ const { ref, cursorSpacing = 0, adjustPosition, onKeyboardShow } = keyboardAvoid.current
80
+ keyboardAvoid.current.keyboardHeight = endCoordinates.height
81
+ onKeyboardShow?.()
82
+ if (adjustPosition) {
83
+ // 默认沿用旧版本逻辑,在 android 原生关闭键盘避让的情况下应该将该配置设置为 false,走 mpx 的键盘避让逻辑,否则bundle内的所有input都会无法避让键盘
84
+ const enableNativeKeyboardAvoiding = mpxGlobal?.__mpx?.config?.rnConfig?.enableNativeKeyboardAvoiding ?? true
85
+ const callback = () => {
86
+ ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
87
+ function calculateOffset() {
88
+ // enableNativeKeyboardAvoding 默认开启
89
+ if (enableNativeKeyboardAvoiding && isAndroid) {
90
+ const aboveOffset = pageY + height - endCoordinates.screenY
91
+ const belowOffset = endCoordinates.height - aboveOffset
92
+ const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
93
+ const belowValue = Math.min(belowOffset, cursorSpacing)
94
+ return aboveOffset > 0 ? belowValue : aboveValue
95
+ }
96
+
51
97
  const aboveOffset = offset.value + pageY + height - endCoordinates.screenY
52
98
  const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
53
99
  const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing)
54
- const value = aboveOffset > 0 ? belowValue : aboveValue
55
- offset.value = withTiming(value, { duration, easing }, (finished) => {
56
- if (finished) {
57
- // Set flexBasic after animation to trigger re-layout and reset layout information
58
- basic.value = '99.99%'
59
- }
60
- })
61
- })
62
- })
63
- }),
64
- Keyboard.addListener('keyboardWillHide', resetKeyboard)
65
- ]
66
- } else {
67
- subscriptions = [
68
- Keyboard.addListener('keyboardDidShow', (evt: any) => {
69
- if (!keyboardAvoid?.current) return
70
- const { endCoordinates } = evt
71
- const { ref, cursorSpacing = 0 } = keyboardAvoid.current
72
- ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
73
- const aboveOffset = pageY + height - endCoordinates.screenY
74
- const belowOffset = endCoordinates.height - aboveOffset
75
- const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
76
- const belowValue = Math.min(belowOffset, cursorSpacing)
77
- const value = aboveOffset > 0 ? belowValue : aboveValue
78
- offset.value = withTiming(value, { duration, easing }, (finished) => {
100
+ return aboveOffset > 0 ? belowValue : aboveValue
101
+ }
102
+
103
+ cancelAnimation(offset)
104
+ offset.value = withTiming(calculateOffset(), { duration, easing }, finished => {
79
105
  if (finished) {
80
106
  // Set flexBasic after animation to trigger re-layout and reset layout information
81
107
  basic.value = '99.99%'
82
108
  }
83
109
  })
84
110
  })
111
+ }
112
+ ;(isIOS ? () => (timerRef.current = setTimeout(callback)) : callback)()
113
+ }
114
+ }
115
+
116
+ if (isIOS) {
117
+ subscriptions = [
118
+ Keyboard.addListener('keyboardWillShow', (evt: any) => {
119
+ keybaordAvoding(evt, true)
85
120
  }),
86
- Keyboard.addListener('keyboardDidHide', resetKeyboard)
121
+ Keyboard.addListener('keyboardWillHide', resetKeyboard)
87
122
  ]
123
+ } else {
124
+ subscriptions = [Keyboard.addListener('keyboardDidShow', keybaordAvoding), Keyboard.addListener('keyboardDidHide', resetKeyboard)]
88
125
  }
89
126
 
90
127
  return () => {
91
128
  subscriptions.forEach(subscription => subscription.remove())
129
+ timerRef.current && clearTimeout(timerRef.current)
92
130
  }
93
131
  }, [keyboardAvoid])
94
132
 
95
133
  return (
96
- <View style={style} onTouchEnd={onTouchEnd}>
97
- <Animated.View
98
- style={[
99
- contentContainerStyle,
100
- animatedStyle
101
- ]}
102
- >
103
- {children}
104
- </Animated.View>
134
+ <View style={style} onTouchEnd={onTouchEnd} onTouchMove={onTouchEnd}>
135
+ <Animated.View style={[contentContainerStyle, animatedStyle]}>{children}</Animated.View>
105
136
  </View>
106
137
  )
107
138
  }
@@ -22,7 +22,7 @@ import { StyleSheet, View, LayoutChangeEvent } from 'react-native'
22
22
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
23
23
  import useNodesRef, { HandlerRef } from './useNodesRef'
24
24
  import { MovableAreaContext } from './context'
25
- import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, GestureHandler, flatGesture, extendObject, omit, useNavigation } from './utils'
25
+ import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, GestureHandler, flatGesture, extendObject, omit, useNavigation, useRunOnJSCallback } from './utils'
26
26
  import { GestureDetector, Gesture, GestureTouchEvent, GestureStateChangeEvent, PanGestureHandlerEventPayload, PanGesture } from 'react-native-gesture-handler'
27
27
  import Animated, {
28
28
  useSharedValue,
@@ -30,7 +30,6 @@ import Animated, {
30
30
  withDecay,
31
31
  runOnJS,
32
32
  runOnUI,
33
- useAnimatedReaction,
34
33
  withSpring
35
34
  } from 'react-native-reanimated'
36
35
  import { collectDataset, noop } from '@mpxjs/utils'
@@ -87,7 +86,6 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
87
86
  const layoutRef = useRef<any>({})
88
87
  const changeSource = useRef<any>('')
89
88
  const hasLayoutRef = useRef(false)
90
-
91
89
  const propsRef = useRef<any>({})
92
90
  propsRef.current = (props || {}) as MovableViewProps
93
91
 
@@ -202,16 +200,6 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
202
200
  )
203
201
  }, [])
204
202
 
205
- // 节流版本的 change 事件触发
206
- const handleTriggerChangeThrottled = useCallback(({ x, y, type }: { x: number; y: number; type?: string }) => {
207
- 'worklet'
208
- const now = Date.now()
209
- if (now - lastChangeTime.value >= changeThrottleTime) {
210
- lastChangeTime.value = now
211
- runOnJS(handleTriggerChange)({ x, y, type })
212
- }
213
- }, [changeThrottleTime])
214
-
215
203
  useEffect(() => {
216
204
  runOnUI(() => {
217
205
  if (offsetX.value !== x || offsetY.value !== y) {
@@ -233,7 +221,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
233
221
  : newY
234
222
  }
235
223
  if (bindchange) {
236
- runOnJS(handleTriggerChange)({
224
+ runOnJS(runOnJSCallback)('handleTriggerChange', {
237
225
  x: newX,
238
226
  y: newY,
239
227
  type: 'setData'
@@ -408,13 +396,31 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
408
396
  catchtouchend && catchtouchend(e)
409
397
  }
410
398
 
399
+ const runOnJSCallbackRef = useRef({
400
+ handleTriggerChange,
401
+ triggerStartOnJS,
402
+ triggerMoveOnJS,
403
+ triggerEndOnJS
404
+ })
405
+ const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
406
+
407
+ // 节流版本的 change 事件触发
408
+ const handleTriggerChangeThrottled = useCallback(({ x, y, type }: { x: number; y: number; type?: string }) => {
409
+ 'worklet'
410
+ const now = Date.now()
411
+ if (now - lastChangeTime.value >= changeThrottleTime) {
412
+ lastChangeTime.value = now
413
+ runOnJS(runOnJSCallback)('handleTriggerChange', { x, y, type })
414
+ }
415
+ }, [changeThrottleTime])
416
+
411
417
  const gesture = useMemo(() => {
412
418
  const handleTriggerMove = (e: GestureTouchEvent) => {
413
419
  'worklet'
414
420
  const hasTouchmove = !!bindhtouchmove || !!bindvtouchmove || !!bindtouchmove
415
421
  const hasCatchTouchmove = !!catchhtouchmove || !!catchvtouchmove || !!catchtouchmove
416
422
  if (hasTouchmove || hasCatchTouchmove) {
417
- runOnJS(triggerMoveOnJS)({
423
+ runOnJS(runOnJSCallback)('triggerMoveOnJS', {
418
424
  e,
419
425
  touchEvent: touchEvent.value,
420
426
  hasTouchmove,
@@ -433,7 +439,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
433
439
  y: changedTouches.y
434
440
  }
435
441
  if (bindtouchstart || catchtouchstart) {
436
- runOnJS(triggerStartOnJS)({ e })
442
+ runOnJS(runOnJSCallback)('triggerStartOnJS', { e })
437
443
  }
438
444
  })
439
445
  .onStart(() => {
@@ -487,7 +493,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
487
493
  isFirstTouch.value = true
488
494
  isMoving.value = false
489
495
  if (bindtouchend || catchtouchend) {
490
- runOnJS(triggerEndOnJS)({ e })
496
+ runOnJS(runOnJSCallback)('triggerEndOnJS', { e })
491
497
  }
492
498
  })
493
499
  .onEnd((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
@@ -515,7 +521,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
515
521
  : y
516
522
  }
517
523
  if (bindchange) {
518
- runOnJS(handleTriggerChange)({
524
+ runOnJS(runOnJSCallback)('handleTriggerChange', {
519
525
  x,
520
526
  y
521
527
  })
@@ -532,7 +538,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
532
538
  }, () => {
533
539
  xInertialMotion.value = false
534
540
  if (bindchange) {
535
- runOnJS(handleTriggerChange)({
541
+ runOnJS(runOnJSCallback)('handleTriggerChange', {
536
542
  x: offsetX.value,
537
543
  y: offsetY.value
538
544
  })
@@ -548,7 +554,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
548
554
  }, () => {
549
555
  yInertialMotion.value = false
550
556
  if (bindchange) {
551
- runOnJS(handleTriggerChange)({
557
+ runOnJS(runOnJSCallback)('handleTriggerChange', {
552
558
  x: offsetX.value,
553
559
  y: offsetY.value
554
560
  })
@@ -0,0 +1,33 @@
1
+ import { AnimatedStyle } from 'react-native-reanimated'
2
+ import { useNavShared } from './useNavShared'
3
+ import { NavSharedContext, NavSharedValue } from './context'
4
+ import { useLayoutEffect, useMemo, useState } from 'react'
5
+ import { StyleProp } from 'react-native'
6
+ import { isAndroid } from './utils'
7
+
8
+ interface MpxNavContainerProps {
9
+ children?: React.ReactNode
10
+ }
11
+
12
+ export default function MpxNavContainer(props: MpxNavContainerProps) {
13
+ const [, setCustomNav] = useNavShared()
14
+
15
+ useLayoutEffect(() => {
16
+ if (!isAndroid) return
17
+ if (props.children) {
18
+ setCustomNav(props.children)
19
+ }
20
+
21
+ return () => {
22
+ setCustomNav(undefined)
23
+ }
24
+ }, [props.children])
25
+
26
+ return isAndroid ? null : props.children
27
+ }
28
+
29
+ export function NavSharedProvider({ children }: { children?: React.ReactNode }) {
30
+ const [customNav, setCustomNav] = useState()
31
+ const value = useMemo(() => ({ customNav, setCustomNav } as NavSharedValue), [customNav])
32
+ return <NavSharedContext.Provider value={value}>{children}</NavSharedContext.Provider>
33
+ }
@@ -38,7 +38,7 @@ import Animated, { useSharedValue, withTiming, useAnimatedStyle, runOnJS } from
38
38
  import { warn, hasOwn } from '@mpxjs/utils'
39
39
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
40
40
  import useNodesRef, { HandlerRef } from './useNodesRef'
41
- import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, GestureHandler, HIDDEN_STYLE } from './utils'
41
+ import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, GestureHandler, HIDDEN_STYLE, useRunOnJSCallback } from './utils'
42
42
  import { IntersectionObserverContext, ScrollViewContext } from './context'
43
43
  import Portal from './mpx-portal'
44
44
 
@@ -210,6 +210,27 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
210
210
  const { textStyle, innerStyle = {} } = splitStyle(normalStyle)
211
211
 
212
212
  const scrollViewRef = useRef<ScrollView>(null)
213
+
214
+ const propsRef = useRef(props)
215
+ const refresherStateRef = useRef({
216
+ hasRefresher,
217
+ refresherTriggered
218
+ })
219
+
220
+ propsRef.current = props
221
+ refresherStateRef.current = {
222
+ hasRefresher,
223
+ refresherTriggered
224
+ }
225
+
226
+ const runOnJSCallbackRef = useRef({
227
+ setEnableScroll,
228
+ setScrollBounces,
229
+ setRefreshing,
230
+ onRefresh
231
+ })
232
+ const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
233
+
213
234
  useNodesRef(props, ref, scrollViewRef, {
214
235
  style: normalStyle,
215
236
  scrollOffset: scrollOptions,
@@ -518,6 +539,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
518
539
 
519
540
  // 处理刷新
520
541
  function onRefresh () {
542
+ const { hasRefresher, refresherTriggered } = refresherStateRef.current
521
543
  if (hasRefresher && refresherTriggered === undefined) {
522
544
  // 处理使用了自定义刷新组件,又没设置 refresherTriggered 的情况
523
545
  setRefreshing(true)
@@ -529,10 +551,10 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
529
551
  }
530
552
  }, 500)
531
553
  }
532
- const { bindrefresherrefresh } = props
554
+ const { bindrefresherrefresh } = propsRef.current
533
555
  bindrefresherrefresh &&
534
556
  bindrefresherrefresh(
535
- getCustomEvent('refresherrefresh', {}, { layoutRef }, props)
557
+ getCustomEvent('refresherrefresh', {}, { layoutRef }, propsRef.current)
536
558
  )
537
559
  }
538
560
 
@@ -587,7 +609,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
587
609
  'worklet'
588
610
  if (enableScrollValue.value !== newValue) {
589
611
  enableScrollValue.value = newValue
590
- runOnJS(setEnableScroll)(newValue)
612
+ runOnJS(runOnJSCallback)('setEnableScroll', newValue)
591
613
  }
592
614
  }
593
615
 
@@ -600,7 +622,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
600
622
  'worklet'
601
623
  if (bouncesValue.value !== newValue) {
602
624
  bouncesValue.value = newValue
603
- runOnJS(setScrollBounces)(newValue)
625
+ runOnJS(runOnJSCallback)('setScrollBounces', newValue)
604
626
  }
605
627
  }
606
628
 
@@ -649,19 +671,19 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
649
671
  if ((event.translationY > 0 && translateY.value < refresherThreshold) || event.translationY < 0) {
650
672
  translateY.value = withTiming(0)
651
673
  updateScrollState(true)
652
- runOnJS(setRefreshing)(false)
674
+ runOnJS(runOnJSCallback)('setRefreshing', false)
653
675
  } else {
654
676
  translateY.value = withTiming(refresherHeight.value)
655
677
  }
656
678
  } else if (event.translationY >= refresherHeight.value) {
657
679
  // 触发刷新
658
680
  translateY.value = withTiming(refresherHeight.value)
659
- runOnJS(onRefresh)()
681
+ runOnJS(runOnJSCallback)('onRefresh')
660
682
  } else {
661
683
  // 回弹
662
684
  translateY.value = withTiming(0)
663
685
  updateScrollState(true)
664
- runOnJS(setRefreshing)(false)
686
+ runOnJS(runOnJSCallback)('setRefreshing', false)
665
687
  }
666
688
  })
667
689
  .simultaneousWithExternalGesture(scrollViewRef)
@@ -5,7 +5,7 @@ import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS
5
5
  import React, { JSX, forwardRef, useRef, useEffect, ReactNode, ReactElement, useMemo, createElement } from 'react'
6
6
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
7
7
  import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数
8
- import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren, extendObject, GestureHandler, flatGesture } from './utils'
8
+ import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren, extendObject, GestureHandler, flatGesture, useRunOnJSCallback } from './utils'
9
9
  import { SwiperContext } from './context'
10
10
  import Portal from './mpx-portal'
11
11
  /**
@@ -148,6 +148,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
148
148
  autoplay = false,
149
149
  circular = false,
150
150
  disableGesture = false,
151
+ current: propCurrent = 0,
151
152
  bindchange
152
153
  } = props
153
154
  const easeingFunc = props['easing-function'] || 'default'
@@ -198,14 +199,16 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
198
199
  // 每个元素的宽度 or 高度,有固定值直接初始化无则0
199
200
  const step = useSharedValue(initStep)
200
201
  // 记录选中元素的索引值
201
- const currentIndex = useSharedValue(props.current || 0)
202
+ const currentIndex = useSharedValue(propCurrent)
202
203
  // const initOffset = getOffset(props.current || 0, initStep)
203
204
  // 记录元素的偏移量
204
- const offset = useSharedValue(getOffset(props.current || 0, initStep))
205
+ const offset = useSharedValue(getOffset(propCurrent, initStep))
205
206
  const strAbso = 'absolute' + dir.toUpperCase() as StrAbsoType
206
207
  const strVelocity = 'velocity' + dir.toUpperCase() as StrVelocityType
207
208
  // 标识手指触摸和抬起, 起点在onBegin
208
209
  const touchfinish = useSharedValue(true)
210
+ // 记录onUpdate时的方向,用于进行onFinalize中的值修正
211
+ const preUpdateTransDir = useSharedValue(0)
209
212
  // 记录上一帧的绝对定位坐标
210
213
  const preAbsolutePos = useSharedValue(0)
211
214
  // 记录从onBegin 到 onTouchesUp 时移动的距离
@@ -270,7 +273,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
270
273
  const iStep = dir === 'x' ? realWidth : realHeight
271
274
  if (iStep !== step.value) {
272
275
  step.value = iStep
273
- updateCurrent(props.current || 0, iStep)
276
+ updateCurrent(propCurrent, iStep)
274
277
  updateAutoplay()
275
278
  }
276
279
  }
@@ -374,7 +377,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
374
377
  easing: easeMap[easeingFunc]
375
378
  }, () => {
376
379
  currentIndex.value = nextIndex
377
- runOnJS(loop)()
380
+ runOnJS(runOnJSCallback)('loop')
378
381
  })
379
382
  } else {
380
383
  // 默认向右, 向下
@@ -389,7 +392,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
389
392
  // 将开始位置设置为真正的位置
390
393
  offset.value = initOffset
391
394
  currentIndex.value = nextIndex
392
- runOnJS(loop)()
395
+ runOnJS(runOnJSCallback)('loop')
393
396
  })
394
397
  } else {
395
398
  nextIndex = currentIndex.value + 1
@@ -400,7 +403,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
400
403
  easing: easeMap[easeingFunc]
401
404
  }, () => {
402
405
  currentIndex.value = nextIndex
403
- runOnJS(loop)()
406
+ runOnJS(runOnJSCallback)('loop')
404
407
  })
405
408
  }
406
409
  }
@@ -429,12 +432,18 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
429
432
  }, [])
430
433
 
431
434
  function handleSwiperChange (current: number) {
432
- if (props.current !== currentIndex.value) {
433
- const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef })
434
- bindchange && bindchange(eventData)
435
- }
435
+ const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef })
436
+ bindchange && bindchange(eventData)
436
437
  }
437
438
 
439
+ const runOnJSCallbackRef = useRef({
440
+ loop,
441
+ pauseLoop,
442
+ resumeLoop,
443
+ handleSwiperChange
444
+ })
445
+ const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
446
+
438
447
  function getOffset (index: number, stepValue: number) {
439
448
  if (!stepValue) return 0
440
449
  let targetOffset = 0
@@ -451,12 +460,12 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
451
460
  const targetOffset = getOffset(index || 0, stepValue)
452
461
  if (targetOffset !== offset.value) {
453
462
  // 内部基于props.current!==currentIndex.value决定是否使用动画及更新currentIndex.value
454
- if (props.current !== undefined && props.current !== currentIndex.value) {
463
+ if (propCurrent !== undefined && propCurrent !== currentIndex.value) {
455
464
  offset.value = withTiming(targetOffset, {
456
465
  duration: easeDuration,
457
466
  easing: easeMap[easeingFunc]
458
467
  }, () => {
459
- currentIndex.value = props.current || 0
468
+ currentIndex.value = propCurrent
460
469
  })
461
470
  } else {
462
471
  offset.value = targetOffset
@@ -473,8 +482,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
473
482
  // 1. 用户在当前页切换选中项,动画;用户携带选中index打开到swiper页直接选中不走动画
474
483
  useAnimatedReaction(() => currentIndex.value, (newIndex: number, preIndex: number) => {
475
484
  // 这里必须传递函数名, 直接写()=> {}形式会报 访问了未sharedValue信息
476
- if (newIndex !== preIndex && bindchange) {
477
- runOnJS(handleSwiperChange)(newIndex)
485
+ if (newIndex !== preIndex && preIndex !== null && preIndex !== undefined && bindchange) {
486
+ runOnJS(runOnJSCallback)('handleSwiperChange', newIndex, propCurrent)
478
487
  }
479
488
  })
480
489
 
@@ -508,12 +517,12 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
508
517
  }, [children.length])
509
518
 
510
519
  useEffect(() => {
511
- // 1. 如果用户在touch的过程中, 外部更新了current以外部为准(小程序表现)
520
+ // 1. 如果用户在touch的过程中, 外部更新了current以内部为准(小程序表现)
512
521
  // 2. 手指滑动过程中更新索引,外部会把current再传入进来,导致offset直接更新,增加判断不同才更新
513
- if (props.current !== currentIndex.value) {
514
- updateCurrent(props.current || 0, step.value)
522
+ if (propCurrent !== currentIndex.value && touchfinish.value) {
523
+ updateCurrent(propCurrent, step.value)
515
524
  }
516
- }, [props.current])
525
+ }, [propCurrent])
517
526
 
518
527
  useEffect(() => {
519
528
  autoplayShared.value = autoplay
@@ -603,7 +612,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
603
612
  if (touchfinish.value !== false) {
604
613
  currentIndex.value = selectedIndex
605
614
  offset.value = resetOffset
606
- runOnJS(resumeLoop)()
615
+ runOnJS(runOnJSCallback)('resumeLoop')
607
616
  }
608
617
  })
609
618
  } else {
@@ -613,7 +622,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
613
622
  }, () => {
614
623
  if (touchfinish.value !== false) {
615
624
  currentIndex.value = selectedIndex
616
- runOnJS(resumeLoop)()
625
+ runOnJS(runOnJSCallback)('resumeLoop')
617
626
  }
618
627
  })
619
628
  }
@@ -635,7 +644,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
635
644
  }, () => {
636
645
  if (touchfinish.value !== false) {
637
646
  currentIndex.value = moveToIndex
638
- runOnJS(resumeLoop)()
647
+ runOnJS(runOnJSCallback)('resumeLoop')
639
648
  }
640
649
  })
641
650
  }
@@ -662,19 +671,10 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
662
671
  'worklet'
663
672
  const { diffOffset, half, isTriggerUpdateHalf } = computeHalf(eventData)
664
673
  if (+diffOffset === 0) {
665
- runOnJS(resumeLoop)()
674
+ runOnJS(runOnJSCallback)('resumeLoop')
666
675
  } 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
- })
676
+ // 如果触发了onUpdate时的索引变更
677
+ handleEnd(eventData)
678
678
  } else if (half) {
679
679
  handleEnd(eventData)
680
680
  } else {
@@ -745,18 +745,19 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
745
745
  if (!step.value) return
746
746
  touchfinish.value = false
747
747
  cancelAnimation(offset)
748
- runOnJS(pauseLoop)()
748
+ runOnJS(runOnJSCallback)('pauseLoop')
749
749
  preAbsolutePos.value = e[strAbso]
750
750
  moveTranstion.value = e[strAbso]
751
751
  })
752
752
  .onUpdate((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
753
753
  'worklet'
754
- if (touchfinish.value) return
755
754
  const moveDistance = e[strAbso] - preAbsolutePos.value
755
+ if (touchfinish.value || moveDistance === 0) return
756
756
  const eventData = {
757
757
  translation: moveDistance,
758
758
  transdir: moveDistance
759
759
  }
760
+ preUpdateTransDir.value = moveDistance
760
761
  // 1. 支持滑动中超出一半更新索引的能力:只更新索引并不会影响onFinalize依据当前offset计算的索引
761
762
  const { half } = computeHalf(eventData)
762
763
  if (childrenLength.value > 1 && half) {
@@ -790,15 +791,25 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
790
791
  }
791
792
  preAbsolutePos.value = e[strAbso]
792
793
  })
794
+ .onEnd((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
795
+ // 修复某些安卓机型小米 onFinalize拿到的absolute值不正确的问题, onUpdate并不是最终的值
796
+ preAbsolutePos.value = e[strAbso]
797
+ })
793
798
  .onFinalize((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
794
799
  'worklet'
795
800
  if (touchfinish.value) return
796
801
  touchfinish.value = true
802
+ /**
803
+ * 安卓修正
804
+ * 问题:部分安卓机型onFinalize中拿到的absoluteX 有问题
805
+ * 案例:比如手指从右向左滑的时候,onUpdate拿到的是241.64346313476562, 而onFinalize中拿到的是241.81817626953125,理论上onFinalize中应该比onUpdate小才对吧
806
+ * 解决方式:修正
807
+ */
797
808
  // 触发过onUpdate正常情况下e[strAbso] - preAbsolutePos.value=0; 未触发过onUpdate的情况下e[strAbso] - preAbsolutePos.value 不为0
798
809
  const moveDistance = e[strAbso] - preAbsolutePos.value
799
810
  const eventData = {
800
811
  translation: moveDistance,
801
- transdir: moveDistance !== 0 ? moveDistance : e[strAbso] - moveTranstion.value
812
+ transdir: Math.abs(moveDistance) > 1 ? moveDistance : preUpdateTransDir.value
802
813
  }
803
814
  // 1. 只有一个元素:循环 和 非循环状态,都走回弹效果
804
815
  if (childrenLength.value === 1) {
@@ -501,7 +501,7 @@ function parseBgImage (text: string): {
501
501
  type?: 'image' | 'linear'
502
502
  src?: string
503
503
  } {
504
- if (!text) return {}
504
+ if (!text || text === 'none') return {}
505
505
 
506
506
  const src = parseUrl(text)
507
507
  if (src) return { src, type: 'image' }