@mpxjs/webpack-plugin 2.10.6 → 2.10.7-beta.10
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.
- package/lib/dependencies/RecordPageConfigsMapDependency.js +1 -1
- package/lib/index.js +71 -51
- package/lib/parser.js +1 -1
- package/lib/platform/json/wx/index.js +0 -1
- package/lib/platform/style/wx/index.js +7 -0
- package/lib/platform/template/wx/component-config/button.js +1 -1
- package/lib/platform/template/wx/component-config/index.js +5 -1
- package/lib/platform/template/wx/component-config/input.js +1 -1
- package/lib/platform/template/wx/component-config/movable-view.js +1 -10
- package/lib/platform/template/wx/component-config/sticky-header.js +23 -0
- package/lib/platform/template/wx/component-config/sticky-section.js +23 -0
- package/lib/react/processJSON.js +2 -1
- package/lib/runtime/components/react/AsyncContainer.tsx +189 -0
- package/lib/runtime/components/react/context.ts +23 -4
- package/lib/runtime/components/react/dist/AsyncContainer.jsx +141 -0
- package/lib/runtime/components/react/dist/context.js +5 -2
- package/lib/runtime/components/react/dist/mpx-button.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-input.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-movable-area.jsx +64 -10
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +358 -98
- package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +3 -0
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +31 -15
- package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +117 -0
- package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +53 -27
- package/lib/runtime/components/react/dist/mpx-view.jsx +21 -7
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +13 -13
- package/lib/runtime/components/react/dist/utils.jsx +94 -1
- package/lib/runtime/components/react/mpx-button.tsx +3 -2
- package/lib/runtime/components/react/mpx-input.tsx +1 -1
- package/lib/runtime/components/react/mpx-movable-area.tsx +99 -12
- package/lib/runtime/components/react/mpx-movable-view.tsx +413 -100
- package/lib/runtime/components/react/mpx-rich-text/index.tsx +3 -0
- package/lib/runtime/components/react/mpx-scroll-view.tsx +84 -59
- package/lib/runtime/components/react/mpx-sticky-header.tsx +181 -0
- package/lib/runtime/components/react/mpx-sticky-section.tsx +96 -0
- package/lib/runtime/components/react/mpx-swiper-item.tsx +2 -2
- package/lib/runtime/components/react/mpx-swiper.tsx +53 -25
- package/lib/runtime/components/react/mpx-view.tsx +20 -7
- package/lib/runtime/components/react/mpx-web-view.tsx +12 -12
- package/lib/runtime/components/react/utils.tsx +93 -1
- package/lib/runtime/components/web/mpx-scroll-view.vue +18 -4
- package/lib/runtime/components/web/mpx-sticky-header.vue +99 -0
- package/lib/runtime/components/web/mpx-sticky-section.vue +15 -0
- package/lib/runtime/optionProcessor.js +0 -2
- package/lib/script-setup-compiler/index.js +27 -5
- package/lib/template-compiler/bind-this.js +2 -1
- package/lib/template-compiler/compiler.js +4 -3
- package/package.json +4 -4
- package/LICENSE +0 -433
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
* ✘ damping
|
|
8
8
|
* ✘ friction
|
|
9
9
|
* ✔ disabled
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* ✔ scale
|
|
11
|
+
* ✔ scale-min
|
|
12
|
+
* ✔ scale-max
|
|
13
|
+
* ✔ scale-value
|
|
14
14
|
* ✔ animation
|
|
15
15
|
* ✔ bindchange
|
|
16
|
-
*
|
|
16
|
+
* ✔ bindscale
|
|
17
17
|
* ✔ htouchmove
|
|
18
18
|
* ✔ vtouchmove
|
|
19
19
|
*/
|
|
@@ -24,7 +24,7 @@ import useNodesRef from './useNodesRef';
|
|
|
24
24
|
import { MovableAreaContext } from './context';
|
|
25
25
|
import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, flatGesture, extendObject, omit, useNavigation } from './utils';
|
|
26
26
|
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
|
|
27
|
-
import Animated, { useSharedValue, useAnimatedStyle, withDecay, runOnJS, runOnUI, withSpring } from 'react-native-reanimated';
|
|
27
|
+
import Animated, { useSharedValue, useAnimatedStyle, withDecay, runOnJS, runOnUI, withSpring, withTiming } from 'react-native-reanimated';
|
|
28
28
|
import { collectDataset, noop } from '@mpxjs/utils';
|
|
29
29
|
const styles = StyleSheet.create({
|
|
30
30
|
container: {
|
|
@@ -41,8 +41,8 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
41
41
|
const hasLayoutRef = useRef(false);
|
|
42
42
|
const propsRef = useRef({});
|
|
43
43
|
propsRef.current = (props || {});
|
|
44
|
-
const { x = 0, y = 0, inertia = false, disabled = false, animation = true, 'out-of-bounds': outOfBounds = false, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, direction = 'none', 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, bindtouchstart, catchtouchstart, bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove, bindtouchend, catchtouchend } = props;
|
|
45
|
-
const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(Object.assign({},
|
|
44
|
+
const { x = 0, y = 0, inertia = false, disabled = false, animation = true, scale = false, 'scale-min': scaleMin = 0.1, 'scale-max': scaleMax = 10, 'scale-value': scaleValue = 1, 'out-of-bounds': outOfBounds = false, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, direction = 'none', 'disable-event-passthrough': disableEventPassthrough = false, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, changeThrottleTime = 60, bindtouchstart, catchtouchstart, bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove, bindtouchend, catchtouchend, bindscale, bindchange, onLayout: propsOnLayout } = props;
|
|
45
|
+
const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(Object.assign({}, styles.container, style), { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
46
46
|
const navigation = useNavigation();
|
|
47
47
|
const prevSimultaneousHandlersRef = useRef(originSimultaneousHandlers || []);
|
|
48
48
|
const prevWaitForHandlersRef = useRef(waitFor || []);
|
|
@@ -50,6 +50,8 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
50
50
|
const { textStyle, innerStyle } = splitStyle(normalStyle);
|
|
51
51
|
const offsetX = useSharedValue(x);
|
|
52
52
|
const offsetY = useSharedValue(y);
|
|
53
|
+
const currentScale = useSharedValue(1);
|
|
54
|
+
const layoutValue = useSharedValue({});
|
|
53
55
|
const startPosition = useSharedValue({
|
|
54
56
|
x: 0,
|
|
55
57
|
y: 0
|
|
@@ -62,6 +64,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
62
64
|
const isFirstTouch = useSharedValue(true);
|
|
63
65
|
const touchEvent = useSharedValue('');
|
|
64
66
|
const initialViewPosition = useSharedValue({ x: x || 0, y: y || 0 });
|
|
67
|
+
const lastChangeTime = useSharedValue(0);
|
|
65
68
|
const MovableAreaLayout = useContext(MovableAreaContext);
|
|
66
69
|
const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
|
|
67
70
|
const waitForHandlers = flatGesture(waitFor);
|
|
@@ -99,6 +102,47 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
99
102
|
layoutRef
|
|
100
103
|
}, propsRef.current));
|
|
101
104
|
}, []);
|
|
105
|
+
const handleTriggerScale = useCallback(({ x, y, scale }) => {
|
|
106
|
+
const { bindscale } = propsRef.current;
|
|
107
|
+
if (!bindscale)
|
|
108
|
+
return;
|
|
109
|
+
bindscale(getCustomEvent('scale', {}, {
|
|
110
|
+
detail: {
|
|
111
|
+
x,
|
|
112
|
+
y,
|
|
113
|
+
scale
|
|
114
|
+
},
|
|
115
|
+
layoutRef
|
|
116
|
+
}, propsRef.current));
|
|
117
|
+
}, []);
|
|
118
|
+
const checkBoundaryPosition = useCallback(({ positionX, positionY }) => {
|
|
119
|
+
'worklet';
|
|
120
|
+
let x = positionX;
|
|
121
|
+
let y = positionY;
|
|
122
|
+
// 计算边界限制
|
|
123
|
+
if (x > draggableXRange.value[1]) {
|
|
124
|
+
x = draggableXRange.value[1];
|
|
125
|
+
}
|
|
126
|
+
else if (x < draggableXRange.value[0]) {
|
|
127
|
+
x = draggableXRange.value[0];
|
|
128
|
+
}
|
|
129
|
+
if (y > draggableYRange.value[1]) {
|
|
130
|
+
y = draggableYRange.value[1];
|
|
131
|
+
}
|
|
132
|
+
else if (y < draggableYRange.value[0]) {
|
|
133
|
+
y = draggableYRange.value[0];
|
|
134
|
+
}
|
|
135
|
+
return { x, y };
|
|
136
|
+
}, []);
|
|
137
|
+
// 节流版本的 change 事件触发
|
|
138
|
+
const handleTriggerChangeThrottled = useCallback(({ x, y, type }) => {
|
|
139
|
+
'worklet';
|
|
140
|
+
const now = Date.now();
|
|
141
|
+
if (now - lastChangeTime.value >= changeThrottleTime) {
|
|
142
|
+
lastChangeTime.value = now;
|
|
143
|
+
runOnJS(handleTriggerChange)({ x, y, type });
|
|
144
|
+
}
|
|
145
|
+
}, [changeThrottleTime]);
|
|
102
146
|
useEffect(() => {
|
|
103
147
|
runOnUI(() => {
|
|
104
148
|
if (offsetX.value !== x || offsetY.value !== y) {
|
|
@@ -119,7 +163,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
119
163
|
})
|
|
120
164
|
: newY;
|
|
121
165
|
}
|
|
122
|
-
if (
|
|
166
|
+
if (bindchange) {
|
|
123
167
|
runOnJS(handleTriggerChange)({
|
|
124
168
|
x: newX,
|
|
125
169
|
y: newY,
|
|
@@ -129,12 +173,193 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
129
173
|
}
|
|
130
174
|
})();
|
|
131
175
|
}, [x, y]);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
176
|
+
// 提取通用的缩放边界计算函数
|
|
177
|
+
const calculateScaleBoundaryPosition = useCallback(({ currentOffsetX, currentOffsetY, newScale, width, height }) => {
|
|
178
|
+
'worklet';
|
|
179
|
+
const prevScale = currentScale.value;
|
|
180
|
+
// 计算元素当前中心点(在屏幕上的位置)
|
|
181
|
+
const currentCenterX = currentOffsetX + (width * prevScale) / 2;
|
|
182
|
+
const currentCenterY = currentOffsetY + (height * prevScale) / 2;
|
|
183
|
+
// 实现中心缩放:保持元素中心点不变
|
|
184
|
+
// 计算缩放后为了保持中心点不变需要的新offset位置
|
|
185
|
+
let newOffsetX = currentCenterX - (width * newScale) / 2;
|
|
186
|
+
let newOffsetY = currentCenterY - (height * newScale) / 2;
|
|
187
|
+
// 缩放过程中实时边界检测
|
|
188
|
+
// 计算新的边界范围
|
|
189
|
+
const top = (style.position === 'absolute' && style.top) || 0;
|
|
190
|
+
const left = (style.position === 'absolute' && style.left) || 0;
|
|
191
|
+
const scaledWidth = width * newScale;
|
|
192
|
+
const scaledHeight = height * newScale;
|
|
193
|
+
// 计算新缩放值下的边界限制
|
|
194
|
+
const maxOffsetY = MovableAreaLayout.height - scaledHeight - top;
|
|
195
|
+
const maxOffsetX = MovableAreaLayout.width - scaledWidth - left;
|
|
196
|
+
let xMin, xMax, yMin, yMax;
|
|
197
|
+
if (MovableAreaLayout.width < scaledWidth) {
|
|
198
|
+
xMin = maxOffsetX;
|
|
199
|
+
xMax = -left;
|
|
136
200
|
}
|
|
201
|
+
else {
|
|
202
|
+
xMin = -left;
|
|
203
|
+
xMax = maxOffsetX < 0 ? -left : maxOffsetX;
|
|
204
|
+
}
|
|
205
|
+
if (MovableAreaLayout.height < scaledHeight) {
|
|
206
|
+
yMin = maxOffsetY;
|
|
207
|
+
yMax = -top;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
yMin = -top;
|
|
211
|
+
yMax = maxOffsetY < 0 ? -top : maxOffsetY;
|
|
212
|
+
}
|
|
213
|
+
// 应用边界限制
|
|
214
|
+
if (newOffsetX > xMax) {
|
|
215
|
+
newOffsetX = xMax;
|
|
216
|
+
}
|
|
217
|
+
else if (newOffsetX < xMin) {
|
|
218
|
+
newOffsetX = xMin;
|
|
219
|
+
}
|
|
220
|
+
if (newOffsetY > yMax) {
|
|
221
|
+
newOffsetY = yMax;
|
|
222
|
+
}
|
|
223
|
+
else if (newOffsetY < yMin) {
|
|
224
|
+
newOffsetY = yMin;
|
|
225
|
+
}
|
|
226
|
+
return { x: newOffsetX, y: newOffsetY };
|
|
227
|
+
}, [MovableAreaLayout.height, MovableAreaLayout.width, style.position, style.top, style.left]);
|
|
228
|
+
// 提取通用的缩放处理函数
|
|
229
|
+
const handleScaleUpdate = useCallback((scaleInfo) => {
|
|
230
|
+
'worklet';
|
|
231
|
+
if (disabled)
|
|
232
|
+
return;
|
|
233
|
+
// 判断缩放方向并计算新的缩放值
|
|
234
|
+
const isZoomingIn = scaleInfo.scale > 1;
|
|
235
|
+
const isZoomingOut = scaleInfo.scale < 1;
|
|
236
|
+
let newScale;
|
|
237
|
+
if (isZoomingIn) {
|
|
238
|
+
// 放大:增加缩放值
|
|
239
|
+
newScale = currentScale.value + (scaleInfo.scale - 1) * 0.5;
|
|
240
|
+
}
|
|
241
|
+
else if (isZoomingOut) {
|
|
242
|
+
// 缩小:减少缩放值
|
|
243
|
+
newScale = currentScale.value - (1 - scaleInfo.scale) * 0.5;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
// 没有缩放变化
|
|
247
|
+
newScale = currentScale.value;
|
|
248
|
+
}
|
|
249
|
+
// 限制缩放值在 scaleMin 和 scaleMax 之间
|
|
250
|
+
newScale = Math.max(scaleMin, Math.min(scaleMax, newScale));
|
|
251
|
+
// 只有当缩放值真正改变时才调整位置
|
|
252
|
+
if (Math.abs(newScale - currentScale.value) > 0.01) {
|
|
253
|
+
// 获取元素尺寸
|
|
254
|
+
const { width = 0, height = 0 } = layoutValue.value;
|
|
255
|
+
if (width > 0 && height > 0) {
|
|
256
|
+
// 使用通用的边界计算函数
|
|
257
|
+
const { x: newOffsetX, y: newOffsetY } = calculateScaleBoundaryPosition({
|
|
258
|
+
currentOffsetX: offsetX.value,
|
|
259
|
+
currentOffsetY: offsetY.value,
|
|
260
|
+
newScale,
|
|
261
|
+
width,
|
|
262
|
+
height
|
|
263
|
+
});
|
|
264
|
+
offsetX.value = newOffsetX;
|
|
265
|
+
offsetY.value = newOffsetY;
|
|
266
|
+
// 更新缩放值
|
|
267
|
+
currentScale.value = newScale;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
currentScale.value = newScale;
|
|
272
|
+
}
|
|
273
|
+
if (bindscale) {
|
|
274
|
+
runOnJS(handleTriggerScale)({
|
|
275
|
+
x: offsetX.value,
|
|
276
|
+
y: offsetY.value,
|
|
277
|
+
scale: newScale
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}, [disabled, scaleMin, scaleMax, bindscale, handleTriggerScale, calculateScaleBoundaryPosition, style.position, style.top, style.left, MovableAreaLayout.height, MovableAreaLayout.width]);
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
runOnUI(() => {
|
|
283
|
+
if (currentScale.value !== scaleValue) {
|
|
284
|
+
// 限制缩放值在 scaleMin 和 scaleMax 之间
|
|
285
|
+
const clampedScale = Math.max(scaleMin, Math.min(scaleMax, scaleValue));
|
|
286
|
+
// 实现中心缩放的位置补偿
|
|
287
|
+
const { width = 0, height = 0 } = layoutValue.value;
|
|
288
|
+
if (width > 0 && height > 0) {
|
|
289
|
+
// 使用通用的边界计算函数
|
|
290
|
+
const { x: newOffsetX, y: newOffsetY } = calculateScaleBoundaryPosition({
|
|
291
|
+
currentOffsetX: offsetX.value,
|
|
292
|
+
currentOffsetY: offsetY.value,
|
|
293
|
+
newScale: clampedScale,
|
|
294
|
+
width,
|
|
295
|
+
height
|
|
296
|
+
});
|
|
297
|
+
// 同时更新缩放值和位置
|
|
298
|
+
if (animation) {
|
|
299
|
+
currentScale.value = withTiming(clampedScale, {
|
|
300
|
+
duration: 1000
|
|
301
|
+
}, () => {
|
|
302
|
+
handleRestBoundaryAndCheck();
|
|
303
|
+
});
|
|
304
|
+
offsetX.value = withTiming(newOffsetX, { duration: 1000 });
|
|
305
|
+
offsetY.value = withTiming(newOffsetY, { duration: 1000 });
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
currentScale.value = clampedScale;
|
|
309
|
+
offsetX.value = newOffsetX;
|
|
310
|
+
offsetY.value = newOffsetY;
|
|
311
|
+
handleRestBoundaryAndCheck();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
// 如果还没有尺寸信息,只更新缩放值
|
|
316
|
+
if (animation) {
|
|
317
|
+
currentScale.value = withTiming(clampedScale, {
|
|
318
|
+
duration: 1000
|
|
319
|
+
}, () => {
|
|
320
|
+
handleRestBoundaryAndCheck();
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
currentScale.value = clampedScale;
|
|
325
|
+
handleRestBoundaryAndCheck();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (bindscale) {
|
|
329
|
+
runOnJS(handleTriggerScale)({
|
|
330
|
+
x: offsetX.value,
|
|
331
|
+
y: offsetY.value,
|
|
332
|
+
scale: clampedScale
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
})();
|
|
337
|
+
}, [scaleValue, scaleMin, scaleMax, animation]);
|
|
338
|
+
useEffect(() => {
|
|
339
|
+
runOnUI(handleRestBoundaryAndCheck)();
|
|
137
340
|
}, [MovableAreaLayout.height, MovableAreaLayout.width]);
|
|
341
|
+
// 生成唯一 ID
|
|
342
|
+
const viewId = useMemo(() => `movable-view-${Date.now()}-${Math.random()}`, []);
|
|
343
|
+
// 注册到 MovableArea(如果启用了 scale-area)
|
|
344
|
+
useEffect(() => {
|
|
345
|
+
if (MovableAreaLayout.scaleArea && MovableAreaLayout.registerMovableView && MovableAreaLayout.unregisterMovableView) {
|
|
346
|
+
const handleAreaScale = (scaleInfo) => {
|
|
347
|
+
'worklet';
|
|
348
|
+
handleScaleUpdate({ scale: scaleInfo.scale });
|
|
349
|
+
};
|
|
350
|
+
const handleAreaScaleEnd = () => {
|
|
351
|
+
'worklet';
|
|
352
|
+
handleRestBoundaryAndCheck();
|
|
353
|
+
};
|
|
354
|
+
MovableAreaLayout.registerMovableView?.(viewId, {
|
|
355
|
+
onScale: scale ? handleAreaScale : noop,
|
|
356
|
+
onScaleEnd: scale ? handleAreaScaleEnd : noop
|
|
357
|
+
});
|
|
358
|
+
return () => {
|
|
359
|
+
MovableAreaLayout.unregisterMovableView?.(viewId);
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
}, [MovableAreaLayout.scaleArea, viewId, scale, handleScaleUpdate]);
|
|
138
363
|
const getTouchSource = useCallback((offsetX, offsetY) => {
|
|
139
364
|
const hasOverBoundary = offsetX < draggableXRange.value[0] || offsetX > draggableXRange.value[1] ||
|
|
140
365
|
offsetY < draggableYRange.value[0] || offsetY > draggableYRange.value[1];
|
|
@@ -159,61 +384,45 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
159
384
|
return source;
|
|
160
385
|
}, []);
|
|
161
386
|
const setBoundary = useCallback(({ width, height }) => {
|
|
387
|
+
'worklet';
|
|
162
388
|
const top = (style.position === 'absolute' && style.top) || 0;
|
|
163
389
|
const left = (style.position === 'absolute' && style.left) || 0;
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
const
|
|
390
|
+
// 使用左上角缩放,计算offset位置的边界范围
|
|
391
|
+
const currentScaleVal = currentScale.value;
|
|
392
|
+
const scaledWidth = (width || 0) * currentScaleVal;
|
|
393
|
+
const scaledHeight = (height || 0) * currentScaleVal;
|
|
394
|
+
// offset位置的边界:左上角可以移动的范围
|
|
395
|
+
const maxOffsetY = MovableAreaLayout.height - scaledHeight - top;
|
|
396
|
+
const maxOffsetX = MovableAreaLayout.width - scaledWidth - left;
|
|
168
397
|
let xRange;
|
|
169
398
|
let yRange;
|
|
170
399
|
if (MovableAreaLayout.width < scaledWidth) {
|
|
171
|
-
xRange = [
|
|
400
|
+
xRange = [maxOffsetX, -left];
|
|
172
401
|
}
|
|
173
402
|
else {
|
|
174
|
-
xRange = [
|
|
403
|
+
xRange = [-left, maxOffsetX < 0 ? -left : maxOffsetX];
|
|
175
404
|
}
|
|
176
405
|
if (MovableAreaLayout.height < scaledHeight) {
|
|
177
|
-
yRange = [
|
|
406
|
+
yRange = [maxOffsetY, -top];
|
|
178
407
|
}
|
|
179
408
|
else {
|
|
180
|
-
yRange = [
|
|
409
|
+
yRange = [-top, maxOffsetY < 0 ? -top : maxOffsetY];
|
|
181
410
|
}
|
|
182
411
|
draggableXRange.value = xRange;
|
|
183
412
|
draggableYRange.value = yRange;
|
|
184
413
|
}, [MovableAreaLayout.height, MovableAreaLayout.width, style.position, style.top, style.left]);
|
|
185
|
-
const
|
|
414
|
+
const resetBoundaryAndCheck = ({ width, height }) => {
|
|
186
415
|
'worklet';
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
else if (x < draggableXRange.value[0]) {
|
|
194
|
-
x = draggableXRange.value[0];
|
|
195
|
-
}
|
|
196
|
-
if (y > draggableYRange.value[1]) {
|
|
197
|
-
y = draggableYRange.value[1];
|
|
416
|
+
setBoundary({ width, height });
|
|
417
|
+
const positionX = offsetX.value;
|
|
418
|
+
const positionY = offsetY.value;
|
|
419
|
+
const { x: newX, y: newY } = checkBoundaryPosition({ positionX, positionY });
|
|
420
|
+
if (positionX !== newX) {
|
|
421
|
+
offsetX.value = newX;
|
|
198
422
|
}
|
|
199
|
-
|
|
200
|
-
|
|
423
|
+
if (positionY !== newY) {
|
|
424
|
+
offsetY.value = newY;
|
|
201
425
|
}
|
|
202
|
-
return { x, y };
|
|
203
|
-
}, []);
|
|
204
|
-
const resetBoundaryAndCheck = ({ width, height }) => {
|
|
205
|
-
setBoundary({ width, height });
|
|
206
|
-
runOnUI(() => {
|
|
207
|
-
const positionX = offsetX.value;
|
|
208
|
-
const positionY = offsetY.value;
|
|
209
|
-
const { x: newX, y: newY } = checkBoundaryPosition({ positionX, positionY });
|
|
210
|
-
if (positionX !== newX) {
|
|
211
|
-
offsetX.value = newX;
|
|
212
|
-
}
|
|
213
|
-
if (positionY !== newY) {
|
|
214
|
-
offsetY.value = newY;
|
|
215
|
-
}
|
|
216
|
-
})();
|
|
217
426
|
};
|
|
218
427
|
const onLayout = (e) => {
|
|
219
428
|
hasLayoutRef.current = true;
|
|
@@ -225,13 +434,18 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
225
434
|
nodeRef.current?.measure((x, y, width, height) => {
|
|
226
435
|
const { y: navigationY = 0 } = navigation?.layout || {};
|
|
227
436
|
layoutRef.current = { x, y: y - navigationY, width, height, offsetLeft: 0, offsetTop: 0 };
|
|
228
|
-
|
|
437
|
+
// 同时更新 layoutValue,供缩放逻辑使用
|
|
438
|
+
runOnUI(() => {
|
|
439
|
+
layoutValue.value = { width, height };
|
|
440
|
+
resetBoundaryAndCheck({ width: width, height: height });
|
|
441
|
+
})();
|
|
229
442
|
});
|
|
230
|
-
|
|
443
|
+
propsOnLayout && propsOnLayout(e);
|
|
231
444
|
};
|
|
232
445
|
const extendEvent = useCallback((e, type) => {
|
|
233
446
|
const { y: navigationY = 0 } = navigation?.layout || {};
|
|
234
447
|
const touchArr = [e.changedTouches, e.allTouches];
|
|
448
|
+
const currentProps = propsRef.current;
|
|
235
449
|
touchArr.forEach(touches => {
|
|
236
450
|
touches && touches.forEach((item) => {
|
|
237
451
|
item.pageX = item.absoluteX;
|
|
@@ -243,8 +457,8 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
243
457
|
Object.assign(e, {
|
|
244
458
|
touches: type === 'end' ? [] : e.allTouches,
|
|
245
459
|
currentTarget: {
|
|
246
|
-
id:
|
|
247
|
-
dataset: collectDataset(
|
|
460
|
+
id: currentProps.id || '',
|
|
461
|
+
dataset: collectDataset(currentProps),
|
|
248
462
|
offsetLeft: 0,
|
|
249
463
|
offsetTop: 0
|
|
250
464
|
},
|
|
@@ -252,11 +466,13 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
252
466
|
});
|
|
253
467
|
}, []);
|
|
254
468
|
const triggerStartOnJS = ({ e }) => {
|
|
469
|
+
const { bindtouchstart, catchtouchstart } = propsRef.current;
|
|
255
470
|
extendEvent(e, 'start');
|
|
256
471
|
bindtouchstart && bindtouchstart(e);
|
|
257
472
|
catchtouchstart && catchtouchstart(e);
|
|
258
473
|
};
|
|
259
474
|
const triggerMoveOnJS = ({ e, hasTouchmove, hasCatchTouchmove, touchEvent }) => {
|
|
475
|
+
const { bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove } = propsRef.current;
|
|
260
476
|
extendEvent(e, 'move');
|
|
261
477
|
if (hasTouchmove) {
|
|
262
478
|
if (touchEvent === 'htouchmove') {
|
|
@@ -278,10 +494,18 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
278
494
|
}
|
|
279
495
|
};
|
|
280
496
|
const triggerEndOnJS = ({ e }) => {
|
|
497
|
+
const { bindtouchend, catchtouchend } = propsRef.current;
|
|
281
498
|
extendEvent(e, 'end');
|
|
282
499
|
bindtouchend && bindtouchend(e);
|
|
283
500
|
catchtouchend && catchtouchend(e);
|
|
284
501
|
};
|
|
502
|
+
const handleRestBoundaryAndCheck = () => {
|
|
503
|
+
'worklet';
|
|
504
|
+
const { width, height } = layoutValue.value;
|
|
505
|
+
if (width && height) {
|
|
506
|
+
resetBoundaryAndCheck({ width, height });
|
|
507
|
+
}
|
|
508
|
+
};
|
|
285
509
|
const gesture = useMemo(() => {
|
|
286
510
|
const handleTriggerMove = (e) => {
|
|
287
511
|
'worklet';
|
|
@@ -297,6 +521,8 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
297
521
|
}
|
|
298
522
|
};
|
|
299
523
|
const gesturePan = Gesture.Pan()
|
|
524
|
+
.minPointers(1)
|
|
525
|
+
.maxPointers(1)
|
|
300
526
|
.onTouchesDown((e) => {
|
|
301
527
|
'worklet';
|
|
302
528
|
const changedTouches = e.changedTouches[0] || { x: 0, y: 0 };
|
|
@@ -350,8 +576,9 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
350
576
|
offsetY.value = newY;
|
|
351
577
|
}
|
|
352
578
|
}
|
|
353
|
-
if (
|
|
354
|
-
runOnJS
|
|
579
|
+
if (bindchange) {
|
|
580
|
+
// 使用节流版本减少 runOnJS 调用
|
|
581
|
+
handleTriggerChangeThrottled({
|
|
355
582
|
x: offsetX.value,
|
|
356
583
|
y: offsetY.value
|
|
357
584
|
});
|
|
@@ -390,55 +617,58 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
390
617
|
})
|
|
391
618
|
: y;
|
|
392
619
|
}
|
|
393
|
-
if (
|
|
620
|
+
if (bindchange) {
|
|
394
621
|
runOnJS(handleTriggerChange)({
|
|
395
622
|
x,
|
|
396
623
|
y
|
|
397
624
|
});
|
|
398
625
|
}
|
|
399
626
|
}
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
// 惯性处理
|
|
403
|
-
if (direction === 'horizontal' || direction === 'all') {
|
|
404
|
-
xInertialMotion.value = true;
|
|
405
|
-
offsetX.value = withDecay({
|
|
406
|
-
velocity: e.velocityX / 10,
|
|
407
|
-
rubberBandEffect: outOfBounds,
|
|
408
|
-
clamp: draggableXRange.value
|
|
409
|
-
}, () => {
|
|
410
|
-
xInertialMotion.value = false;
|
|
411
|
-
if (propsRef.current.bindchange) {
|
|
412
|
-
runOnJS(handleTriggerChange)({
|
|
413
|
-
x: offsetX.value,
|
|
414
|
-
y: offsetY.value
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
627
|
}
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
628
|
+
else if (inertia) {
|
|
629
|
+
// 惯性处理
|
|
630
|
+
if (direction === 'horizontal' || direction === 'all') {
|
|
631
|
+
xInertialMotion.value = true;
|
|
632
|
+
offsetX.value = withDecay({
|
|
633
|
+
velocity: e.velocityX / 10,
|
|
634
|
+
rubberBandEffect: outOfBounds,
|
|
635
|
+
clamp: draggableXRange.value
|
|
636
|
+
}, () => {
|
|
637
|
+
xInertialMotion.value = false;
|
|
638
|
+
if (bindchange) {
|
|
639
|
+
runOnJS(handleTriggerChange)({
|
|
640
|
+
x: offsetX.value,
|
|
641
|
+
y: offsetY.value
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
if (direction === 'vertical' || direction === 'all') {
|
|
647
|
+
yInertialMotion.value = true;
|
|
648
|
+
offsetY.value = withDecay({
|
|
649
|
+
velocity: e.velocityY / 10,
|
|
650
|
+
rubberBandEffect: outOfBounds,
|
|
651
|
+
clamp: draggableYRange.value
|
|
652
|
+
}, () => {
|
|
653
|
+
yInertialMotion.value = false;
|
|
654
|
+
if (bindchange) {
|
|
655
|
+
runOnJS(handleTriggerChange)({
|
|
656
|
+
x: offsetX.value,
|
|
657
|
+
y: offsetY.value
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
}
|
|
434
662
|
}
|
|
435
663
|
})
|
|
436
664
|
.withRef(movableGestureRef);
|
|
437
|
-
if (
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
665
|
+
if (!disableEventPassthrough) {
|
|
666
|
+
if (direction === 'horizontal') {
|
|
667
|
+
gesturePan.activeOffsetX([-5, 5]).failOffsetY([-5, 5]);
|
|
668
|
+
}
|
|
669
|
+
else if (direction === 'vertical') {
|
|
670
|
+
gesturePan.activeOffsetY([-5, 5]).failOffsetX([-5, 5]);
|
|
671
|
+
}
|
|
442
672
|
}
|
|
443
673
|
if (simultaneousHandlers && simultaneousHandlers.length) {
|
|
444
674
|
gesturePan.simultaneousWithExternalGesture(...simultaneousHandlers);
|
|
@@ -446,13 +676,43 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
446
676
|
if (waitForHandlers && waitForHandlers.length) {
|
|
447
677
|
gesturePan.requireExternalGestureToFail(...waitForHandlers);
|
|
448
678
|
}
|
|
679
|
+
// 添加缩放手势支持
|
|
680
|
+
if (scale && !MovableAreaLayout.scaleArea) {
|
|
681
|
+
const gesturePinch = Gesture.Pinch()
|
|
682
|
+
.onUpdate((e) => {
|
|
683
|
+
'worklet';
|
|
684
|
+
handleScaleUpdate({ scale: e.scale });
|
|
685
|
+
})
|
|
686
|
+
.onEnd((e) => {
|
|
687
|
+
'worklet';
|
|
688
|
+
if (disabled)
|
|
689
|
+
return;
|
|
690
|
+
// 确保最终缩放值在有效范围内
|
|
691
|
+
const finalScale = Math.max(scaleMin, Math.min(scaleMax, currentScale.value));
|
|
692
|
+
if (finalScale !== currentScale.value) {
|
|
693
|
+
currentScale.value = finalScale;
|
|
694
|
+
if (bindscale) {
|
|
695
|
+
runOnJS(handleTriggerScale)({
|
|
696
|
+
x: offsetX.value,
|
|
697
|
+
y: offsetY.value,
|
|
698
|
+
scale: finalScale
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
// 缩放结束后重新检查边界
|
|
703
|
+
handleRestBoundaryAndCheck();
|
|
704
|
+
});
|
|
705
|
+
// 根据手指数量自动区分手势:一指移动,两指缩放
|
|
706
|
+
return Gesture.Exclusive(gesturePan, gesturePinch);
|
|
707
|
+
}
|
|
449
708
|
return gesturePan;
|
|
450
|
-
}, [disabled, direction, inertia, outOfBounds, gestureSwitch.current]);
|
|
709
|
+
}, [disabled, direction, inertia, outOfBounds, scale, scaleMin, scaleMax, animation, gestureSwitch.current, handleScaleUpdate, MovableAreaLayout.scaleArea]);
|
|
451
710
|
const animatedStyles = useAnimatedStyle(() => {
|
|
452
711
|
return {
|
|
453
712
|
transform: [
|
|
454
713
|
{ translateX: offsetX.value },
|
|
455
|
-
{ translateY: offsetY.value }
|
|
714
|
+
{ translateY: offsetY.value },
|
|
715
|
+
{ scale: currentScale.value }
|
|
456
716
|
]
|
|
457
717
|
};
|
|
458
718
|
});
|
|
@@ -489,7 +749,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
489
749
|
const innerProps = useInnerProps(extendObject({}, filterProps, {
|
|
490
750
|
ref: nodeRef,
|
|
491
751
|
onLayout: onLayout,
|
|
492
|
-
style: [innerStyle, animatedStyles, layoutStyle]
|
|
752
|
+
style: [{ transformOrigin: 'top left' }, innerStyle, animatedStyles, layoutStyle]
|
|
493
753
|
}, rewriteCatchEvent()));
|
|
494
754
|
return createElement(GestureDetector, { gesture: gesture }, createElement(Animated.View, innerProps, wrapChildren(props, {
|
|
495
755
|
hasVarDec,
|