@mpxjs/webpack-plugin 2.9.65 → 2.9.67

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 (80) hide show
  1. package/lib/dependencies/RecordGlobalComponentsDependency.js +11 -12
  2. package/lib/dependencies/RecordRuntimeInfoDependency.js +1 -1
  3. package/lib/index.js +28 -8
  4. package/lib/json-compiler/index.js +2 -11
  5. package/lib/loader.js +24 -45
  6. package/lib/native-loader.js +49 -64
  7. package/lib/platform/json/wx/index.js +3 -10
  8. package/lib/platform/style/wx/index.js +32 -56
  9. package/lib/react/index.js +4 -3
  10. package/lib/react/processJSON.js +5 -13
  11. package/lib/react/processMainScript.js +7 -3
  12. package/lib/react/processScript.js +3 -4
  13. package/lib/react/processTemplate.js +6 -4
  14. package/lib/resolver/AddModePlugin.js +17 -4
  15. package/lib/runtime/components/react/context.ts +8 -0
  16. package/lib/runtime/components/react/dist/context.js +1 -0
  17. package/lib/runtime/components/react/dist/mpx-button.jsx +1 -1
  18. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +30 -17
  19. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +1 -1
  20. package/lib/runtime/components/react/dist/mpx-form.jsx +33 -24
  21. package/lib/runtime/components/react/dist/mpx-icon.jsx +1 -1
  22. package/lib/runtime/components/react/dist/mpx-image/index.jsx +1 -1
  23. package/lib/runtime/components/react/dist/mpx-input.jsx +44 -38
  24. package/lib/runtime/components/react/dist/mpx-label.jsx +10 -7
  25. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +10 -17
  26. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +378 -294
  27. package/lib/runtime/components/react/dist/mpx-navigator.jsx +1 -1
  28. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +30 -17
  29. package/lib/runtime/components/react/dist/mpx-radio.jsx +1 -1
  30. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
  31. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +58 -30
  32. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +77 -77
  33. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +1 -1
  34. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +1 -1
  35. package/lib/runtime/components/react/dist/mpx-switch.jsx +8 -1
  36. package/lib/runtime/components/react/dist/mpx-text.jsx +1 -1
  37. package/lib/runtime/components/react/dist/mpx-textarea.jsx +1 -1
  38. package/lib/runtime/components/react/dist/mpx-view.jsx +31 -12
  39. package/lib/runtime/components/react/dist/mpx-web-view.jsx +2 -2
  40. package/lib/runtime/components/react/dist/useAnimationHooks.js +303 -0
  41. package/lib/runtime/components/react/dist/utils.jsx +13 -3
  42. package/lib/runtime/components/react/getInnerListeners.ts +1 -0
  43. package/lib/runtime/components/react/mpx-button.tsx +1 -1
  44. package/lib/runtime/components/react/mpx-checkbox-group.tsx +52 -29
  45. package/lib/runtime/components/react/mpx-checkbox.tsx +1 -1
  46. package/lib/runtime/components/react/mpx-form.tsx +42 -34
  47. package/lib/runtime/components/react/mpx-icon.tsx +1 -1
  48. package/lib/runtime/components/react/mpx-image/index.tsx +2 -3
  49. package/lib/runtime/components/react/mpx-input.tsx +68 -66
  50. package/lib/runtime/components/react/mpx-label.tsx +11 -8
  51. package/lib/runtime/components/react/mpx-movable-area.tsx +11 -19
  52. package/lib/runtime/components/react/mpx-movable-view.tsx +456 -334
  53. package/lib/runtime/components/react/mpx-navigator.tsx +1 -1
  54. package/lib/runtime/components/react/mpx-radio-group.tsx +55 -29
  55. package/lib/runtime/components/react/mpx-radio.tsx +1 -1
  56. package/lib/runtime/components/react/mpx-root-portal.tsx +1 -1
  57. package/lib/runtime/components/react/mpx-scroll-view.tsx +92 -37
  58. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +77 -76
  59. package/lib/runtime/components/react/mpx-swiper/index.tsx +2 -1
  60. package/lib/runtime/components/react/mpx-swiper-item.tsx +1 -1
  61. package/lib/runtime/components/react/mpx-switch.tsx +10 -2
  62. package/lib/runtime/components/react/mpx-text.tsx +1 -1
  63. package/lib/runtime/components/react/mpx-textarea.tsx +1 -1
  64. package/lib/runtime/components/react/mpx-view.tsx +40 -20
  65. package/lib/runtime/components/react/mpx-web-view.tsx +2 -2
  66. package/lib/runtime/components/react/types/common.ts +8 -2
  67. package/lib/runtime/components/react/useAnimationHooks.ts +332 -0
  68. package/lib/runtime/components/react/useNodesRef.ts +1 -0
  69. package/lib/runtime/components/react/utils.tsx +23 -6
  70. package/lib/runtime/optionProcessorReact.js +0 -15
  71. package/lib/runtime/swanHelper.wxs +1 -1
  72. package/lib/style-compiler/index.js +1 -1
  73. package/lib/style-compiler/plugins/scope-id.js +1 -0
  74. package/lib/template-compiler/compiler.js +68 -33
  75. package/lib/template-compiler/index.js +4 -4
  76. package/lib/utils/pre-process-json.js +113 -0
  77. package/lib/web/index.js +5 -4
  78. package/lib/web/processJSON.js +5 -13
  79. package/lib/web/processTemplate.js +2 -2
  80. package/package.json +5 -4
@@ -1,51 +1,70 @@
1
1
  /**
2
2
  * ✔ direction
3
- * inertia
4
- * out-of-bounds
3
+ * inertia
4
+ * out-of-bounds
5
5
  * ✔ x
6
6
  * ✔ y
7
7
  * ✘ damping
8
- * friction
8
+ * friction
9
9
  * ✔ disabled
10
- * scale
11
- * scale-min
12
- * scale-max
13
- * scale-value
10
+ * scale
11
+ * scale-min
12
+ * scale-max
13
+ * scale-value
14
14
  * ✘ animation
15
15
  * ✔ bindchange
16
- * bindscale
16
+ * bindscale
17
17
  * ✔ htouchmove
18
18
  * ✔ vtouchmove
19
19
  */
20
- import { useRef, useEffect, forwardRef, ReactNode, useContext, useState, useMemo } from 'react'
21
- import { StyleSheet, Animated, NativeSyntheticEvent, PanResponder, View } from 'react-native'
22
- import useInnerProps, { getCustomEvent } from './getInnerListeners'
20
+ import { useEffect, forwardRef, ReactNode, useContext, useCallback, useRef, useMemo } from 'react'
21
+ import { StyleSheet, NativeSyntheticEvent, View, LayoutChangeEvent } from 'react-native'
22
+ import { 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 } from './utils'
26
+ import { GestureDetector, Gesture, GestureTouchEvent, GestureStateChangeEvent, PanGestureHandlerEventPayload, PanGesture } from 'react-native-gesture-handler'
27
+ import Animated, {
28
+ useSharedValue,
29
+ useAnimatedStyle,
30
+ withDecay,
31
+ runOnJS,
32
+ runOnUI,
33
+ useAnimatedReaction,
34
+ withSpring
35
+ } from 'react-native-reanimated'
25
36
 
26
37
  interface MovableViewProps {
27
38
  children: ReactNode;
28
39
  style?: Record<string, any>;
29
40
  direction: 'all' | 'vertical' | 'horizontal' | 'none';
30
- x?: string | number;
31
- y?: string | number;
32
- scale?: boolean;
41
+ x?: number;
42
+ y?: number;
33
43
  disabled?: boolean;
34
- friction?: number;
35
- 'scale-value'?: number;
36
- 'scale-min'?: number;
37
- 'scale-max'?: number;
44
+ animation?: boolean;
38
45
  bindchange?: (event: unknown) => void;
39
- bindscale?: (event: unknown) => void;
40
46
  bindtouchstart?: (event: NativeSyntheticEvent<TouchEvent>) => void;
47
+ catchtouchstart?: (event: NativeSyntheticEvent<TouchEvent>) => void;
41
48
  bindtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
42
49
  catchtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
50
+ catchtouchend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
43
51
  bindtouchend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
44
52
  bindhtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
45
53
  bindvtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
46
54
  catchhtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
47
55
  catchvtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
56
+ onLayout?: (event: LayoutChangeEvent) => void;
57
+ 'out-of-bounds'?: boolean;
58
+ 'wait-for'?: Array<GestureHandler>;
59
+ 'simultaneous-handlers'?: Array<GestureHandler>;
60
+ inertia?: boolean;
61
+ 'enable-var'?: boolean
62
+ 'external-var-context'?: Record<string, any>;
63
+ 'parent-font-size'?: number;
64
+ 'parent-width'?: number;
65
+ 'parent-height'?: number;
48
66
  }
67
+
49
68
  const styles = StyleSheet.create({
50
69
  container: {
51
70
  position: 'absolute',
@@ -54,372 +73,475 @@ const styles = StyleSheet.create({
54
73
  }
55
74
  })
56
75
 
57
- const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewProps>((props: MovableViewProps, ref): JSX.Element => {
76
+ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewProps>((movableViewProps: MovableViewProps, ref): JSX.Element => {
77
+ const { textProps, innerProps: props = {} } = splitProps(movableViewProps)
78
+ const movableGestureRef = useRef<PanGesture>()
79
+ const layoutRef = useRef<any>({})
80
+ const changeSource = useRef<any>('')
81
+ const hasLayoutRef = useRef(false)
82
+
83
+ const propsRef = useRef<any>({})
84
+ propsRef.current = (props || {}) as MovableViewProps
85
+
58
86
  const {
59
- children,
60
- friction = 7,
61
- scale = false,
62
- direction = 'none',
63
87
  x = 0,
64
88
  y = 0,
89
+ inertia = false,
90
+ disabled = false,
91
+ animation = true,
92
+ 'out-of-bounds': outOfBounds = false,
93
+ 'enable-var': enableVar,
94
+ 'external-var-context': externalVarContext,
95
+ 'parent-font-size': parentFontSize,
96
+ 'parent-width': parentWidth,
97
+ 'parent-height': parentHeight,
98
+ direction = 'none',
99
+ 'simultaneous-handlers': originSimultaneousHandlers = [],
100
+ 'wait-for': waitFor = [],
65
101
  style = {},
66
- 'scale-min': scaleMin = 0.1,
67
- 'scale-max': scaleMax = 10,
68
- 'scale-value': originScaleValue = 1,
69
- bindscale,
70
- bindchange
102
+ bindtouchstart,
103
+ catchtouchstart,
104
+ bindhtouchmove,
105
+ bindvtouchmove,
106
+ bindtouchmove,
107
+ catchhtouchmove,
108
+ catchvtouchmove,
109
+ catchtouchmove,
110
+ bindtouchend,
111
+ catchtouchend
71
112
  } = props
72
113
 
73
- const pan = useRef<any>(new Animated.ValueXY())
74
- const scaleValue = useRef<any>(new Animated.Value(1))
75
- const [transformOrigin, setTransformOrigin] = useState('0% 0%')
76
-
77
- const propsRef = useRef<any>({})
78
- const layoutRef = useRef<any>({})
114
+ const {
115
+ hasSelfPercent,
116
+ normalStyle,
117
+ hasVarDec,
118
+ varContextRef,
119
+ setWidth,
120
+ setHeight
121
+ } = useTransformStyle(Object.assign({}, style, styles.container), { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
122
+
123
+ const prevSimultaneousHandlersRef = useRef<Array<GestureHandler>>(originSimultaneousHandlers || [])
124
+ const prevWaitForHandlersRef = useRef<Array<GestureHandler>>(waitFor || [])
125
+ const gestureSwitch = useRef(false)
126
+ const { textStyle, innerStyle } = splitStyle(normalStyle)
127
+
128
+ const offsetX = useSharedValue(x)
129
+ const offsetY = useSharedValue(y)
130
+
131
+ const startPosition = useSharedValue({
132
+ x: 0,
133
+ y: 0
134
+ })
135
+ const draggableXRange = useSharedValue<[min: number, max: number]>([0, 0])
136
+ const draggableYRange = useSharedValue<[min: number, max: number]>([0, 0])
137
+ const isMoving = useSharedValue(false)
138
+ const xInertialMotion = useSharedValue(false)
139
+ const yInertialMotion = useSharedValue(false)
140
+ const isFirstTouch = useSharedValue(true)
141
+ const touchEvent = useSharedValue<string>('')
79
142
 
80
143
  const MovableAreaLayout = useContext(MovableAreaContext)
81
144
 
82
- const movablePosition = useRef({
83
- x: Number(x),
84
- y: Number(y)
85
- })
145
+ const simultaneousHandlers = flatGesture(originSimultaneousHandlers)
146
+ const waitForHandlers = flatGesture(waitFor)
86
147
 
87
148
  const nodeRef = useRef<View>(null)
149
+
88
150
  useNodesRef(props, ref, nodeRef, {
89
- defaultStyle: styles.container
151
+ defaultStyle: styles.container,
152
+ gestureRef: movableGestureRef
90
153
  })
91
154
 
92
- let panResponder: any = {}
155
+ const hasSimultaneousHandlersChanged = prevSimultaneousHandlersRef.current.length !== (originSimultaneousHandlers?.length || 0) ||
156
+ (originSimultaneousHandlers || []).some((handler, index) => handler !== prevSimultaneousHandlersRef.current[index])
93
157
 
94
- const isFirstTouch = useRef<boolean>(true)
95
- const touchEvent = useRef<string>('')
96
- const initialDistance = useRef<number>(0)
158
+ const hasWaitForHandlersChanged = prevWaitForHandlersRef.current.length !== (waitFor?.length || 0) ||
159
+ (waitFor || []).some((handler, index) => handler !== prevWaitForHandlersRef.current[index])
97
160
 
98
- propsRef.current = props
161
+ if (hasSimultaneousHandlersChanged || hasWaitForHandlersChanged) {
162
+ gestureSwitch.current = !gestureSwitch.current
163
+ }
99
164
 
100
- useEffect(() => {
101
- if (scale && (scaleValue.current._value !== originScaleValue)) {
102
- const clampedScale = Math.min(scaleMax, Math.max(scaleMin, originScaleValue))
103
- Animated.spring(scaleValue.current, {
104
- toValue: clampedScale,
105
- friction,
106
- useNativeDriver: false
107
- }).start(() => {
108
- bindscale && bindscale(getCustomEvent('scale', {}, {
109
- detail: {
110
- x: pan.current.x._value,
111
- y: pan.current.y._value,
112
- scale: clampedScale
113
- },
114
- layoutRef
115
- }, props)
116
- )
117
- })
165
+ prevSimultaneousHandlersRef.current = originSimultaneousHandlers || []
166
+ prevWaitForHandlersRef.current = waitFor || []
167
+
168
+ const handleTriggerChange = useCallback(({ x, y, type }: { x: number; y: number; type?: string }) => {
169
+ const { bindchange } = propsRef.current
170
+ if (!bindchange) return
171
+ let source = ''
172
+ if (type !== 'setData') {
173
+ source = getTouchSource(x, y)
174
+ } else {
175
+ changeSource.current = ''
118
176
  }
119
- }, [originScaleValue])
177
+ bindchange(
178
+ getCustomEvent('change', {}, {
179
+ detail: {
180
+ x,
181
+ y,
182
+ source
183
+ },
184
+ layoutRef
185
+ }, propsRef.current)
186
+ )
187
+ }, [])
120
188
 
121
189
  useEffect(() => {
122
- if (movablePosition.current.x !== Number(x) || movablePosition.current.y !== Number(y)) {
123
- const { x: newX, y: newY } = checkBoundaryPosition({
124
- clampedScale: scaleValue.current._value,
125
- width: layoutRef.current.width,
126
- height: layoutRef.current.height,
127
- positionX: Number(x),
128
- positionY: Number(y)
129
- })
130
- movablePosition.current = { x: newX, y: newY }
131
- Animated.spring(pan.current, {
132
- toValue: { x: newX, y: newY },
133
- useNativeDriver: false,
134
- friction
135
- }).start(() => {
136
- bindchange &&
137
- bindchange(
138
- getCustomEvent('change', {}, {
139
- detail: {
140
- x: newX,
141
- y: newY,
142
- source: ''
143
- },
144
- layoutRef
145
- }, props)
146
- )
147
- })
148
- }
190
+ runOnUI(() => {
191
+ if (offsetX.value !== x || offsetY.value !== y) {
192
+ const { x: newX, y: newY } = checkBoundaryPosition({ positionX: Number(x), positionY: Number(y) })
193
+ if (direction === 'horizontal' || direction === 'all') {
194
+ offsetX.value = animation
195
+ ? withSpring(newX, {
196
+ duration: 1500,
197
+ dampingRatio: 0.8
198
+ })
199
+ : newX
200
+ }
201
+ if (direction === 'vertical' || direction === 'all') {
202
+ offsetY.value = animation
203
+ ? withSpring(newY, {
204
+ duration: 1500,
205
+ dampingRatio: 0.8
206
+ })
207
+ : newY
208
+ }
209
+ runOnJS(handleTriggerChange)({
210
+ x: newX,
211
+ y: newY,
212
+ type: 'setData'
213
+ })
214
+ }
215
+ })()
149
216
  }, [x, y])
150
217
 
151
- const handlePanReleaseOrTerminate = () => {
152
- pan.current.flattenOffset()
153
- isFirstTouch.current = true
154
- initialDistance.current = 0
155
- const { x, y } = checkBoundaryPosition({
156
- clampedScale: scaleValue.current._value,
157
- width: layoutRef.current.width,
158
- height: layoutRef.current.height,
159
- positionX: pan.current.x._value,
160
- positionY: pan.current.y._value
161
- })
162
- movablePosition.current = {
163
- x,
164
- y
218
+ useEffect(() => {
219
+ const { width, height } = layoutRef.current
220
+ if (width && height) {
221
+ resetBoundaryAndCheck({ width, height })
165
222
  }
166
- const needChange = x !== pan.current.x._value || y !== pan.current.y._value
167
-
168
- Animated.spring(pan.current, {
169
- toValue: { x, y },
170
- friction: 7,
171
- useNativeDriver: false
172
- }).start(() => {
173
- if (needChange) {
174
- bindchange && bindchange(
175
- getCustomEvent('change', {}, {
176
- detail: {
177
- x,
178
- y,
179
- source: 'out-of-bounds'
180
- },
181
- layoutRef
182
- }, propsRef.current)
183
- )
184
- }
223
+ }, [MovableAreaLayout.height, MovableAreaLayout.width])
224
+
225
+ useAnimatedReaction(
226
+ () => ({
227
+ offsetX: offsetX.value,
228
+ offsetY: offsetY.value
229
+ }),
230
+ (currentValue: { offsetX: any; offsetY: any; }) => {
231
+ const { offsetX, offsetY } = currentValue
232
+ runOnJS(handleTriggerChange)({
233
+ x: offsetX,
234
+ y: offsetY
235
+ })
185
236
  })
186
- }
187
237
 
188
- panResponder = useMemo(() => {
189
- return PanResponder.create({
190
- onMoveShouldSetPanResponder: () => !propsRef.current.disabled,
191
- onMoveShouldSetPanResponderCapture: () => !propsRef.current.disabled,
192
- onPanResponderGrant: (e, gestureState) => {
193
- if (gestureState.numberActiveTouches === 1) {
194
- setTransformOrigin('0% 0%')
195
- pan.current.setOffset({
196
- x: direction === 'all' || direction === 'horizontal' ? pan.current.x._value : 0,
197
- y: direction === 'all' || direction === 'vertical' ? pan.current.y._value : 0
198
- })
199
- pan.current.setValue({ x: 0, y: 0 })
200
- } else {
201
- initialDistance.current = 0
202
- setTransformOrigin('50% 50%')
203
- }
204
- },
205
- onPanResponderMove: (e, gestureState) => {
206
- if (gestureState.numberActiveTouches === 2 && scale) {
207
- setTransformOrigin('50% 50%')
208
- const touch1 = e.nativeEvent.touches[0]
209
- const touch2 = e.nativeEvent.touches[1]
210
- const currentTouchDistance = Math.sqrt(
211
- Math.pow(touch1.pageX - touch2.pageX, 2) + Math.pow(touch1.pageY - touch2.pageY, 2)
212
- )
238
+ const getTouchSource = useCallback((offsetX: number, offsetY: number) => {
239
+ const hasOverBoundary = offsetX < draggableXRange.value[0] || offsetX > draggableXRange.value[1] ||
240
+ offsetY < draggableYRange.value[0] || offsetY > draggableYRange.value[1]
241
+ let source = changeSource.current
242
+ if (hasOverBoundary) {
243
+ if (isMoving.value) {
244
+ source = 'touch-out-of-bounds'
245
+ } else {
246
+ source = 'out-of-bounds'
247
+ }
248
+ } else {
249
+ if (isMoving.value) {
250
+ source = 'touch'
251
+ } else if ((xInertialMotion.value || yInertialMotion.value) && (changeSource.current === 'touch' || changeSource.current === 'friction')) {
252
+ source = 'friction'
253
+ }
254
+ }
255
+ changeSource.current = source
256
+ return source
257
+ }, [])
213
258
 
214
- if (!initialDistance.current) {
215
- initialDistance.current = currentTouchDistance
216
- } else {
217
- const newScale = currentTouchDistance / initialDistance.current
218
- const clampedScale = Math.min(scaleMax, Math.max(scaleMin, newScale))
219
-
220
- Animated.spring(scaleValue.current, {
221
- toValue: clampedScale,
222
- friction: 7,
223
- useNativeDriver: false
224
- }).start()
225
- bindscale && bindscale(getCustomEvent('scale', e, {
226
- detail: {
227
- x: pan.current.x._value,
228
- y: pan.current.y._value,
229
- scale: clampedScale
230
- },
231
- layoutRef
232
- }, propsRef.current))
233
- }
234
- } else if (gestureState.numberActiveTouches === 1) {
235
- if (initialDistance.current) {
236
- return // Skip processing if it's switching from a double touch
237
- }
238
- setTransformOrigin('0% 0%')
239
- if (isFirstTouch.current) {
240
- touchEvent.current = Math.abs(gestureState.dx) > Math.abs(gestureState.dy) ? 'htouchmove' : 'vtouchmove'
241
- isFirstTouch.current = false
242
- }
243
- Animated.event(
244
- [
245
- null,
246
- {
247
- dx: direction === 'all' || direction === 'horizontal' ? pan.current.x : new Animated.Value(0),
248
- dy: direction === 'all' || direction === 'vertical' ? pan.current.y : new Animated.Value(0)
249
- }
250
- ],
251
- {
252
- useNativeDriver: false
253
- }
254
- )(e, gestureState)
259
+ const setBoundary = useCallback(({ width, height }: { width: number; height: number }) => {
260
+ const top = (style.position === 'absolute' && style.top) || 0
261
+ const left = (style.position === 'absolute' && style.left) || 0
255
262
 
256
- movablePosition.current = {
257
- x: pan.current.x.__getValue(),
258
- y: pan.current.y.__getValue()
259
- }
260
- bindchange && bindchange(
261
- getCustomEvent('change', e, {
262
- detail: {
263
- x: movablePosition.current.x,
264
- y: movablePosition.current.y,
265
- source: 'touch'
266
- },
267
- layoutRef
268
- }, propsRef.current)
269
- )
270
- }
271
- },
272
- onPanResponderRelease: () => {
273
- handlePanReleaseOrTerminate()
274
- },
275
- onPanResponderTerminate: () => {
276
- handlePanReleaseOrTerminate()
277
- }
278
- })
279
- }, [MovableAreaLayout.width, MovableAreaLayout.height])
263
+ const scaledWidth = width || 0
264
+ const scaledHeight = height || 0
280
265
 
281
- const onLayout = () => {
282
- nodeRef.current?.measure((x: number, y: number, width: number, height: number) => {
283
- layoutRef.current = { x, y, width, height, offsetLeft: 0, offsetTop: 0 }
284
- const clampedScale = Math.min(scaleMax, Math.max(scaleMin, originScaleValue))
285
- const { x: newX, y: nexY } = checkBoundaryPosition({
286
- clampedScale,
287
- width,
288
- height,
289
- positionX: movablePosition.current.x,
290
- positionY: movablePosition.current.y
291
- })
266
+ const maxY = MovableAreaLayout.height - scaledHeight - top
267
+ const maxX = MovableAreaLayout.width - scaledWidth - left
292
268
 
293
- Animated.spring(pan.current, {
294
- toValue: { x: newX, y: nexY },
295
- useNativeDriver: false,
296
- friction
297
- }).start(() => {
298
- movablePosition.current = { x: newX, y: nexY }
299
- bindchange &&
300
- bindchange(
301
- getCustomEvent('change', {}, {
302
- detail: {
303
- x: newX,
304
- y: nexY,
305
- source: ''
306
- },
307
- layoutRef
308
- }, props)
309
- )
310
- })
311
- })
312
- }
269
+ let xRange: [min: number, max: number]
270
+ let yRange: [min: number, max: number]
313
271
 
314
- const onTouchMove = (e: NativeSyntheticEvent<TouchEvent>) => {
315
- const { bindhtouchmove, bindvtouchmove, bindtouchmove } = props
316
- if (touchEvent.current === 'htouchmove') {
317
- bindhtouchmove && bindhtouchmove(e)
318
- } else if (touchEvent.current === 'vtouchmove') {
319
- bindvtouchmove && bindvtouchmove(e)
272
+ if (MovableAreaLayout.width < scaledWidth) {
273
+ xRange = [maxX, 0]
274
+ } else {
275
+ xRange = [left === 0 ? 0 : -left, maxX < 0 ? 0 : maxX]
320
276
  }
321
- bindtouchmove && bindtouchmove(e)
322
- }
323
277
 
324
- const onCatchTouchMove = (e: NativeSyntheticEvent<TouchEvent>) => {
325
- const { catchhtouchmove, catchvtouchmove, catchtouchmove } = props
326
- if (touchEvent.current === 'htouchmove') {
327
- catchhtouchmove && catchhtouchmove(e)
328
- } else if (touchEvent.current === 'vtouchmove') {
329
- catchvtouchmove && catchvtouchmove(e)
278
+ if (MovableAreaLayout.height < scaledHeight) {
279
+ yRange = [maxY, 0]
280
+ } else {
281
+ yRange = [top === 0 ? 0 : -top, maxY < 0 ? 0 : maxY]
330
282
  }
331
- catchtouchmove && catchtouchmove(e)
332
- }
283
+ draggableXRange.value = xRange
284
+ draggableYRange.value = yRange
285
+ }, [MovableAreaLayout.height, MovableAreaLayout.width, style.position, style.top, style.left])
333
286
 
334
- const checkBoundaryPosition = ({ clampedScale, width, height, positionX, positionY }: { clampedScale: number; width: number; height: number; positionX: number; positionY: number }) => {
335
- // Calculate scaled element size
336
- const scaledWidth = width * clampedScale
337
- const scaledHeight = height * clampedScale
338
-
339
- // Calculate the boundary limits
287
+ const checkBoundaryPosition = useCallback(({ positionX, positionY }: { positionX: number; positionY: number }) => {
288
+ 'worklet'
340
289
  let x = positionX
341
290
  let y = positionY
291
+ // 计算边界限制
292
+ if (x > draggableXRange.value[1]) {
293
+ x = draggableXRange.value[1]
294
+ } else if (x < draggableXRange.value[0]) {
295
+ x = draggableXRange.value[0]
296
+ }
342
297
 
343
- // Correct y coordinate
344
- if (scaledHeight > MovableAreaLayout.height) {
345
- if (y >= 0) {
346
- y = 0
347
- } else if (y < MovableAreaLayout.height - scaledHeight) {
348
- y = MovableAreaLayout.height - scaledHeight
298
+ if (y > draggableYRange.value[1]) {
299
+ y = draggableYRange.value[1]
300
+ } else if (y < draggableYRange.value[0]) {
301
+ y = draggableYRange.value[0]
302
+ }
303
+
304
+ return { x, y }
305
+ }, [])
306
+
307
+ const resetBoundaryAndCheck = ({ width, height }: { width: number; height: number }) => {
308
+ setBoundary({ width, height })
309
+ runOnUI(() => {
310
+ const positionX = offsetX.value
311
+ const positionY = offsetY.value
312
+ const { x: newX, y: newY } = checkBoundaryPosition({ positionX, positionY })
313
+ if (positionX !== newX) {
314
+ offsetX.value = newX
349
315
  }
350
- } else {
351
- if (y < 0) {
352
- y = 0
353
- } else if (y > MovableAreaLayout.height - scaledHeight) {
354
- y = MovableAreaLayout.height - scaledHeight
316
+ if (positionY !== newY) {
317
+ offsetY.value = newY
355
318
  }
319
+ })()
320
+ }
321
+
322
+ const onLayout = (e: LayoutChangeEvent) => {
323
+ hasLayoutRef.current = true
324
+ if (hasSelfPercent) {
325
+ const { width, height } = e?.nativeEvent?.layout || {}
326
+ setWidth(width || 0)
327
+ setHeight(height || 0)
356
328
  }
357
- // Correct x coordinate
358
- if (scaledWidth > MovableAreaLayout.width) {
359
- if (x >= 0) {
360
- x = 0
361
- } else if (x < MovableAreaLayout.width - scaledWidth) {
362
- x = MovableAreaLayout.width - scaledWidth
329
+ nodeRef.current?.measure((x: number, y: number, width: number, height: number) => {
330
+ layoutRef.current = { x, y, width, height, offsetLeft: 0, offsetTop: 0 }
331
+ resetBoundaryAndCheck({ width, height })
332
+ })
333
+ props.onLayout && props.onLayout(e)
334
+ }
335
+
336
+ const extendEvent = useCallback((e: any) => {
337
+ 'worklet'
338
+ const touchArr = [e.changedTouches, e.allTouches]
339
+ touchArr.forEach(touches => {
340
+ touches && touches.forEach((item: { absoluteX: number; absoluteY: number; pageX: number; pageY: number }) => {
341
+ item.pageX = item.absoluteX
342
+ item.pageY = item.absoluteY
343
+ })
344
+ })
345
+ e.touches = e.allTouches
346
+ }, [])
347
+
348
+ const gesture = useMemo(() => {
349
+ const handleTriggerStart = (e: any) => {
350
+ 'worklet'
351
+ extendEvent(e)
352
+ bindtouchstart && runOnJS(bindtouchstart)(e)
353
+ catchtouchstart && runOnJS(catchtouchstart)(e)
354
+ }
355
+
356
+ const handleTriggerMove = (e: any) => {
357
+ 'worklet'
358
+ extendEvent(e)
359
+ const hasTouchmove = !!bindhtouchmove || !!bindvtouchmove || !!bindtouchmove
360
+ const hasCatchTouchmove = !!catchhtouchmove || !!catchvtouchmove || !!catchtouchmove
361
+
362
+ if (hasTouchmove) {
363
+ if (touchEvent.value === 'htouchmove') {
364
+ bindhtouchmove && runOnJS(bindhtouchmove)(e)
365
+ } else if (touchEvent.value === 'vtouchmove') {
366
+ bindvtouchmove && runOnJS(bindvtouchmove)(e)
367
+ }
368
+ bindtouchmove && runOnJS(bindtouchmove)(e)
363
369
  }
364
- } else {
365
- if (x < 0) {
366
- x = 0
367
- } else if (x > MovableAreaLayout.width - scaledWidth) {
368
- x = MovableAreaLayout.width - scaledWidth
370
+
371
+ if (hasCatchTouchmove) {
372
+ if (touchEvent.value === 'htouchmove') {
373
+ catchhtouchmove && runOnJS(catchhtouchmove)(e)
374
+ } else if (touchEvent.value === 'vtouchmove') {
375
+ catchvtouchmove && runOnJS(catchvtouchmove)(e)
376
+ }
377
+ catchtouchmove && runOnJS(catchtouchmove)(e)
369
378
  }
370
379
  }
371
380
 
381
+ const handleTriggerEnd = (e: any) => {
382
+ 'worklet'
383
+ extendEvent(e)
384
+ bindtouchend && runOnJS(bindtouchend)(e)
385
+ catchtouchend && runOnJS(catchtouchend)(e)
386
+ }
387
+ const gesturePan = Gesture.Pan()
388
+ .onTouchesDown((e: GestureTouchEvent) => {
389
+ 'worklet'
390
+ const changedTouches = e.changedTouches[0] || { x: 0, y: 0 }
391
+ isMoving.value = false
392
+ startPosition.value = {
393
+ x: changedTouches.x,
394
+ y: changedTouches.y
395
+ }
396
+ handleTriggerStart(e)
397
+ })
398
+ .onTouchesMove((e: GestureTouchEvent) => {
399
+ 'worklet'
400
+ isMoving.value = true
401
+ const changedTouches = e.changedTouches[0] || { x: 0, y: 0 }
402
+ if (isFirstTouch.value) {
403
+ touchEvent.value = Math.abs(changedTouches.x - startPosition.value.x) > Math.abs(changedTouches.y - startPosition.value.y) ? 'htouchmove' : 'vtouchmove'
404
+ isFirstTouch.value = false
405
+ }
406
+ handleTriggerMove(e)
407
+ if (disabled) return
408
+ const changeX = changedTouches.x - startPosition.value.x
409
+ const changeY = changedTouches.y - startPosition.value.y
410
+ if (direction === 'horizontal' || direction === 'all') {
411
+ const newX = offsetX.value + changeX
412
+ if (!outOfBounds) {
413
+ const { x } = checkBoundaryPosition({ positionX: newX, positionY: offsetY.value })
414
+ offsetX.value = x
415
+ } else {
416
+ offsetX.value = newX
417
+ }
418
+ }
419
+ if (direction === 'vertical' || direction === 'all') {
420
+ const newY = offsetY.value + changeY
421
+ if (!outOfBounds) {
422
+ const { y } = checkBoundaryPosition({ positionX: offsetX.value, positionY: newY })
423
+ offsetY.value = y
424
+ } else {
425
+ offsetY.value = newY
426
+ }
427
+ }
428
+ })
429
+ .onTouchesUp((e: GestureTouchEvent) => {
430
+ 'worklet'
431
+ isFirstTouch.value = true
432
+ isMoving.value = false
433
+ handleTriggerEnd(e)
434
+ if (disabled) return
435
+ if (!inertia) {
436
+ const { x, y } = checkBoundaryPosition({ positionX: offsetX.value, positionY: offsetY.value })
437
+ if (x !== offsetX.value) {
438
+ offsetX.value = animation
439
+ ? withSpring(x, {
440
+ duration: 1500,
441
+ dampingRatio: 0.8
442
+ })
443
+ : x
444
+ }
445
+ if (y !== offsetY.value) {
446
+ offsetY.value = animation
447
+ ? withSpring(y, {
448
+ duration: 1500,
449
+ dampingRatio: 0.8
450
+ })
451
+ : y
452
+ }
453
+ }
454
+ })
455
+ .onFinalize((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
456
+ 'worklet'
457
+ if (!inertia || disabled || !animation) return
458
+ isMoving.value = false
459
+ if (direction === 'horizontal' || direction === 'all') {
460
+ xInertialMotion.value = true
461
+ offsetX.value = withDecay({
462
+ velocity: e.velocityX / 10,
463
+ rubberBandEffect: outOfBounds,
464
+ clamp: draggableXRange.value
465
+ }, () => {
466
+ xInertialMotion.value = false
467
+ })
468
+ }
469
+ if (direction === 'vertical' || direction === 'all') {
470
+ yInertialMotion.value = true
471
+ offsetY.value = withDecay({
472
+ velocity: e.velocityY / 10,
473
+ rubberBandEffect: outOfBounds,
474
+ clamp: draggableYRange.value
475
+ }, () => {
476
+ yInertialMotion.value = false
477
+ })
478
+ }
479
+ })
480
+ .withRef(movableGestureRef)
481
+ if (simultaneousHandlers && simultaneousHandlers.length) {
482
+ gesturePan.simultaneousWithExternalGesture(...simultaneousHandlers)
483
+ }
484
+
485
+ if (waitForHandlers && waitForHandlers.length) {
486
+ gesturePan.requireExternalGestureToFail(...waitForHandlers)
487
+ }
488
+ return gesturePan
489
+ }, [disabled, direction, inertia, outOfBounds, gestureSwitch.current])
490
+
491
+ const animatedStyles = useAnimatedStyle(() => {
372
492
  return {
373
- x,
374
- y
493
+ transform: [
494
+ { translateX: offsetX.value },
495
+ { translateY: offsetY.value }
496
+ ]
375
497
  }
376
- }
498
+ })
377
499
 
378
- const [translateX, translateY] = [pan.current.x, pan.current.y]
379
-
380
- const transformStyle = { transform: [{ translateX }, { translateY }, { scale: scaleValue.current }], transformOrigin: transformOrigin }
381
-
382
- const hasTouchmove = () => !!props.bindhtouchmove || !!props.bindvtouchmove || !!props.bindtouchmove
383
-
384
- const hasCatchTouchmove = () => !!props.catchhtouchmove || !!props.catchvtouchmove || !!props.catchtouchmove
385
-
386
- const innerProps = useInnerProps(props, {
387
- ref: nodeRef,
388
- ...panResponder.panHandlers,
389
- onLayout,
390
- ...(hasTouchmove() ? { bindtouchmove: onTouchMove } : {}),
391
- ...(hasCatchTouchmove() ? { catchtouchmove: onCatchTouchMove } : {})
392
- }, [
393
- 'children',
394
- 'style',
395
- 'direction',
396
- 'x',
397
- 'y',
398
- 'scale',
399
- 'disabled',
400
- 'scale-value',
401
- 'scale-min',
402
- 'scale-max',
403
- 'bindchange',
404
- 'bindscale',
405
- 'htouchmove',
406
- 'vtouchmove'
407
- ], { layoutRef })
500
+ const injectCatchEvent = (props: Record<string, any>) => {
501
+ const eventHandlers: Record<string, any> = {}
502
+ const catchEventList = [
503
+ { name: 'onTouchStart', value: ['catchtouchstart'] },
504
+ { name: 'onTouchMove', value: ['catchtouchmove', 'catchvtouchmove', 'catchhtouchmove'] },
505
+ { name: 'onTouchEnd', value: ['catchtouchend'] }
506
+ ]
507
+ catchEventList.forEach(event => {
508
+ event.value.forEach(name => {
509
+ if (props[name] && !eventHandlers[event.name]) {
510
+ eventHandlers[event.name] = (e: NativeSyntheticEvent<TouchEvent>) => {
511
+ e.stopPropagation()
512
+ }
513
+ }
514
+ })
515
+ })
516
+ return eventHandlers
517
+ }
408
518
 
519
+ const catchEventHandlers = injectCatchEvent(props)
520
+ const layoutStyle = !hasLayoutRef.current && hasSelfPercent ? HIDDEN_STYLE : {}
409
521
  return (
410
- <Animated.View
411
- {...innerProps}
412
- style={{
413
- ...styles.container,
414
- ...style,
415
- ...transformStyle
416
- }}
417
- >
418
- {children}
419
- </Animated.View>
522
+ <GestureDetector gesture={gesture}>
523
+ <Animated.View
524
+ ref={nodeRef}
525
+ onLayout={onLayout}
526
+ style={[innerStyle, animatedStyles, layoutStyle]}
527
+ {...catchEventHandlers}
528
+ >
529
+ {
530
+ wrapChildren(
531
+ props,
532
+ {
533
+ hasVarDec,
534
+ varContext: varContextRef.current,
535
+ textStyle,
536
+ textProps
537
+ }
538
+ )
539
+ }
540
+ </Animated.View>
541
+ </GestureDetector>
420
542
  )
421
543
  })
422
544
 
423
- _MovableView.displayName = 'mpx-movable-view'
545
+ _MovableView.displayName = 'MpxMovableView'
424
546
 
425
547
  export default _MovableView