@mpxjs/webpack-plugin 2.10.7-beta.7 → 2.10.7-beta.9
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/platform/style/wx/index.js +7 -0
- package/lib/platform/template/wx/component-config/movable-view.js +1 -10
- package/lib/runtime/components/react/context.ts +12 -2
- package/lib/runtime/components/react/dist/context.js +1 -1
- package/lib/runtime/components/react/dist/mpx-movable-area.jsx +63 -9
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +306 -61
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +15 -10
- package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +3 -1
- package/lib/runtime/components/react/mpx-movable-area.tsx +98 -11
- package/lib/runtime/components/react/mpx-movable-view.tsx +356 -62
- package/lib/runtime/components/react/mpx-scroll-view.tsx +16 -9
- package/lib/runtime/components/react/mpx-sticky-header.tsx +3 -1
- package/lib/runtime/components/web/mpx-scroll-view.vue +4 -7
- package/lib/runtime/components/web/mpx-sticky-header.vue +39 -31
- package/lib/template-compiler/bind-this.js +2 -1
- package/lib/template-compiler/compiler.js +1 -1
- package/package.json +1 -1
|
@@ -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
|
*/
|
|
@@ -30,8 +30,8 @@ import Animated, {
|
|
|
30
30
|
withDecay,
|
|
31
31
|
runOnJS,
|
|
32
32
|
runOnUI,
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
withSpring,
|
|
34
|
+
withTiming
|
|
35
35
|
} from 'react-native-reanimated'
|
|
36
36
|
import { collectDataset, noop } from '@mpxjs/utils'
|
|
37
37
|
|
|
@@ -43,9 +43,14 @@ interface MovableViewProps {
|
|
|
43
43
|
y?: number
|
|
44
44
|
disabled?: boolean
|
|
45
45
|
animation?: boolean
|
|
46
|
+
scale?: boolean
|
|
47
|
+
'scale-min'?: number
|
|
48
|
+
'scale-max'?: number
|
|
49
|
+
'scale-value'?: number
|
|
46
50
|
id?: string
|
|
47
51
|
changeThrottleTime?:number
|
|
48
52
|
bindchange?: (event: unknown) => void
|
|
53
|
+
bindscale?: (event: unknown) => void
|
|
49
54
|
bindtouchstart?: (event: GestureTouchEvent) => void
|
|
50
55
|
catchtouchstart?: (event: GestureTouchEvent) => void
|
|
51
56
|
bindtouchmove?: (event: GestureTouchEvent) => void
|
|
@@ -70,6 +75,7 @@ interface MovableViewProps {
|
|
|
70
75
|
'parent-font-size'?: number
|
|
71
76
|
'parent-width'?: number
|
|
72
77
|
'parent-height'?: number
|
|
78
|
+
'disable-event-passthrough'?: boolean
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
const styles = StyleSheet.create({
|
|
@@ -96,6 +102,10 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
96
102
|
inertia = false,
|
|
97
103
|
disabled = false,
|
|
98
104
|
animation = true,
|
|
105
|
+
scale = false,
|
|
106
|
+
'scale-min': scaleMin = 0.1,
|
|
107
|
+
'scale-max': scaleMax = 10,
|
|
108
|
+
'scale-value': scaleValue = 1,
|
|
99
109
|
'out-of-bounds': outOfBounds = false,
|
|
100
110
|
'enable-var': enableVar,
|
|
101
111
|
'external-var-context': externalVarContext,
|
|
@@ -103,6 +113,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
103
113
|
'parent-width': parentWidth,
|
|
104
114
|
'parent-height': parentHeight,
|
|
105
115
|
direction = 'none',
|
|
116
|
+
'disable-event-passthrough': disableEventPassthrough = false,
|
|
106
117
|
'simultaneous-handlers': originSimultaneousHandlers = [],
|
|
107
118
|
'wait-for': waitFor = [],
|
|
108
119
|
style = {},
|
|
@@ -117,7 +128,9 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
117
128
|
catchtouchmove,
|
|
118
129
|
bindtouchend,
|
|
119
130
|
catchtouchend,
|
|
120
|
-
|
|
131
|
+
bindscale,
|
|
132
|
+
bindchange,
|
|
133
|
+
onLayout: propsOnLayout
|
|
121
134
|
} = props
|
|
122
135
|
|
|
123
136
|
const {
|
|
@@ -127,7 +140,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
127
140
|
varContextRef,
|
|
128
141
|
setWidth,
|
|
129
142
|
setHeight
|
|
130
|
-
} = useTransformStyle(Object.assign({},
|
|
143
|
+
} = useTransformStyle(Object.assign({}, styles.container, style), { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
|
|
131
144
|
|
|
132
145
|
const navigation = useNavigation()
|
|
133
146
|
|
|
@@ -138,6 +151,8 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
138
151
|
|
|
139
152
|
const offsetX = useSharedValue(x)
|
|
140
153
|
const offsetY = useSharedValue(y)
|
|
154
|
+
const currentScale = useSharedValue(1)
|
|
155
|
+
const layoutValue = useSharedValue<any>({})
|
|
141
156
|
|
|
142
157
|
const startPosition = useSharedValue({
|
|
143
158
|
x: 0,
|
|
@@ -200,6 +215,41 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
200
215
|
)
|
|
201
216
|
}, [])
|
|
202
217
|
|
|
218
|
+
const handleTriggerScale = useCallback(({ x, y, scale }: { x: number; y: number; scale: number }) => {
|
|
219
|
+
const { bindscale } = propsRef.current
|
|
220
|
+
if (!bindscale) return
|
|
221
|
+
bindscale(
|
|
222
|
+
getCustomEvent('scale', {}, {
|
|
223
|
+
detail: {
|
|
224
|
+
x,
|
|
225
|
+
y,
|
|
226
|
+
scale
|
|
227
|
+
},
|
|
228
|
+
layoutRef
|
|
229
|
+
}, propsRef.current)
|
|
230
|
+
)
|
|
231
|
+
}, [])
|
|
232
|
+
|
|
233
|
+
const checkBoundaryPosition = useCallback(({ positionX, positionY }: { positionX: number; positionY: number }) => {
|
|
234
|
+
'worklet'
|
|
235
|
+
let x = positionX
|
|
236
|
+
let y = positionY
|
|
237
|
+
// 计算边界限制
|
|
238
|
+
if (x > draggableXRange.value[1]) {
|
|
239
|
+
x = draggableXRange.value[1]
|
|
240
|
+
} else if (x < draggableXRange.value[0]) {
|
|
241
|
+
x = draggableXRange.value[0]
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (y > draggableYRange.value[1]) {
|
|
245
|
+
y = draggableYRange.value[1]
|
|
246
|
+
} else if (y < draggableYRange.value[0]) {
|
|
247
|
+
y = draggableYRange.value[0]
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return { x, y }
|
|
251
|
+
}, [])
|
|
252
|
+
|
|
203
253
|
// 节流版本的 change 事件触发
|
|
204
254
|
const handleTriggerChangeThrottled = useCallback(({ x, y, type }: { x: number; y: number; type?: string }) => {
|
|
205
255
|
'worklet'
|
|
@@ -241,13 +291,224 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
241
291
|
})()
|
|
242
292
|
}, [x, y])
|
|
243
293
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
294
|
+
// 提取通用的缩放边界计算函数
|
|
295
|
+
const calculateScaleBoundaryPosition = useCallback(({
|
|
296
|
+
currentOffsetX,
|
|
297
|
+
currentOffsetY,
|
|
298
|
+
newScale,
|
|
299
|
+
width,
|
|
300
|
+
height
|
|
301
|
+
}: {
|
|
302
|
+
currentOffsetX: number
|
|
303
|
+
currentOffsetY: number
|
|
304
|
+
newScale: number
|
|
305
|
+
width: number
|
|
306
|
+
height: number
|
|
307
|
+
}) => {
|
|
308
|
+
'worklet'
|
|
309
|
+
const prevScale = currentScale.value
|
|
310
|
+
|
|
311
|
+
// 计算元素当前中心点(在屏幕上的位置)
|
|
312
|
+
const currentCenterX = currentOffsetX + (width * prevScale) / 2
|
|
313
|
+
const currentCenterY = currentOffsetY + (height * prevScale) / 2
|
|
314
|
+
|
|
315
|
+
// 实现中心缩放:保持元素中心点不变
|
|
316
|
+
// 计算缩放后为了保持中心点不变需要的新offset位置
|
|
317
|
+
let newOffsetX = currentCenterX - (width * newScale) / 2
|
|
318
|
+
let newOffsetY = currentCenterY - (height * newScale) / 2
|
|
319
|
+
|
|
320
|
+
// 缩放过程中实时边界检测
|
|
321
|
+
// 计算新的边界范围
|
|
322
|
+
const top = (style.position === 'absolute' && style.top) || 0
|
|
323
|
+
const left = (style.position === 'absolute' && style.left) || 0
|
|
324
|
+
const scaledWidth = width * newScale
|
|
325
|
+
const scaledHeight = height * newScale
|
|
326
|
+
|
|
327
|
+
// 计算新缩放值下的边界限制
|
|
328
|
+
const maxOffsetY = MovableAreaLayout.height - scaledHeight - top
|
|
329
|
+
const maxOffsetX = MovableAreaLayout.width - scaledWidth - left
|
|
330
|
+
|
|
331
|
+
let xMin, xMax, yMin, yMax
|
|
332
|
+
|
|
333
|
+
if (MovableAreaLayout.width < scaledWidth) {
|
|
334
|
+
xMin = maxOffsetX
|
|
335
|
+
xMax = -left
|
|
336
|
+
} else {
|
|
337
|
+
xMin = -left
|
|
338
|
+
xMax = maxOffsetX < 0 ? -left : maxOffsetX
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (MovableAreaLayout.height < scaledHeight) {
|
|
342
|
+
yMin = maxOffsetY
|
|
343
|
+
yMax = -top
|
|
344
|
+
} else {
|
|
345
|
+
yMin = -top
|
|
346
|
+
yMax = maxOffsetY < 0 ? -top : maxOffsetY
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// 应用边界限制
|
|
350
|
+
if (newOffsetX > xMax) {
|
|
351
|
+
newOffsetX = xMax
|
|
352
|
+
} else if (newOffsetX < xMin) {
|
|
353
|
+
newOffsetX = xMin
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (newOffsetY > yMax) {
|
|
357
|
+
newOffsetY = yMax
|
|
358
|
+
} else if (newOffsetY < yMin) {
|
|
359
|
+
newOffsetY = yMin
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return { x: newOffsetX, y: newOffsetY }
|
|
363
|
+
}, [MovableAreaLayout.height, MovableAreaLayout.width, style.position, style.top, style.left])
|
|
364
|
+
|
|
365
|
+
// 提取通用的缩放处理函数
|
|
366
|
+
const handleScaleUpdate = useCallback((scaleInfo: { scale: number }) => {
|
|
367
|
+
'worklet'
|
|
368
|
+
if (disabled) return
|
|
369
|
+
|
|
370
|
+
// 判断缩放方向并计算新的缩放值
|
|
371
|
+
const isZoomingIn = scaleInfo.scale > 1
|
|
372
|
+
const isZoomingOut = scaleInfo.scale < 1
|
|
373
|
+
|
|
374
|
+
let newScale
|
|
375
|
+
if (isZoomingIn) {
|
|
376
|
+
// 放大:增加缩放值
|
|
377
|
+
newScale = currentScale.value + (scaleInfo.scale - 1) * 0.5
|
|
378
|
+
} else if (isZoomingOut) {
|
|
379
|
+
// 缩小:减少缩放值
|
|
380
|
+
newScale = currentScale.value - (1 - scaleInfo.scale) * 0.5
|
|
381
|
+
} else {
|
|
382
|
+
// 没有缩放变化
|
|
383
|
+
newScale = currentScale.value
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// 限制缩放值在 scaleMin 和 scaleMax 之间
|
|
387
|
+
newScale = Math.max(scaleMin, Math.min(scaleMax, newScale))
|
|
388
|
+
|
|
389
|
+
// 只有当缩放值真正改变时才调整位置
|
|
390
|
+
if (Math.abs(newScale - currentScale.value) > 0.01) {
|
|
391
|
+
// 获取元素尺寸
|
|
392
|
+
const { width = 0, height = 0 } = layoutValue.value
|
|
393
|
+
|
|
394
|
+
if (width > 0 && height > 0) {
|
|
395
|
+
// 使用通用的边界计算函数
|
|
396
|
+
const { x: newOffsetX, y: newOffsetY } = calculateScaleBoundaryPosition({
|
|
397
|
+
currentOffsetX: offsetX.value,
|
|
398
|
+
currentOffsetY: offsetY.value,
|
|
399
|
+
newScale,
|
|
400
|
+
width,
|
|
401
|
+
height
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
offsetX.value = newOffsetX
|
|
405
|
+
offsetY.value = newOffsetY
|
|
406
|
+
|
|
407
|
+
// 更新缩放值
|
|
408
|
+
currentScale.value = newScale
|
|
409
|
+
}
|
|
410
|
+
} else {
|
|
411
|
+
currentScale.value = newScale
|
|
248
412
|
}
|
|
413
|
+
|
|
414
|
+
if (bindscale) {
|
|
415
|
+
runOnJS(handleTriggerScale)({
|
|
416
|
+
x: offsetX.value,
|
|
417
|
+
y: offsetY.value,
|
|
418
|
+
scale: newScale
|
|
419
|
+
})
|
|
420
|
+
}
|
|
421
|
+
}, [disabled, scaleMin, scaleMax, bindscale, handleTriggerScale, calculateScaleBoundaryPosition, style.position, style.top, style.left, MovableAreaLayout.height, MovableAreaLayout.width])
|
|
422
|
+
|
|
423
|
+
useEffect(() => {
|
|
424
|
+
runOnUI(() => {
|
|
425
|
+
if (currentScale.value !== scaleValue) {
|
|
426
|
+
// 限制缩放值在 scaleMin 和 scaleMax 之间
|
|
427
|
+
const clampedScale = Math.max(scaleMin, Math.min(scaleMax, scaleValue))
|
|
428
|
+
|
|
429
|
+
// 实现中心缩放的位置补偿
|
|
430
|
+
const { width = 0, height = 0 } = layoutValue.value
|
|
431
|
+
if (width > 0 && height > 0) {
|
|
432
|
+
// 使用通用的边界计算函数
|
|
433
|
+
const { x: newOffsetX, y: newOffsetY } = calculateScaleBoundaryPosition({
|
|
434
|
+
currentOffsetX: offsetX.value,
|
|
435
|
+
currentOffsetY: offsetY.value,
|
|
436
|
+
newScale: clampedScale,
|
|
437
|
+
width,
|
|
438
|
+
height
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
// 同时更新缩放值和位置
|
|
442
|
+
if (animation) {
|
|
443
|
+
currentScale.value = withTiming(clampedScale, {
|
|
444
|
+
duration: 1000
|
|
445
|
+
}, () => {
|
|
446
|
+
handleRestBoundaryAndCheck()
|
|
447
|
+
})
|
|
448
|
+
offsetX.value = withTiming(newOffsetX, { duration: 1000 })
|
|
449
|
+
offsetY.value = withTiming(newOffsetY, { duration: 1000 })
|
|
450
|
+
} else {
|
|
451
|
+
currentScale.value = clampedScale
|
|
452
|
+
offsetX.value = newOffsetX
|
|
453
|
+
offsetY.value = newOffsetY
|
|
454
|
+
handleRestBoundaryAndCheck()
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
// 如果还没有尺寸信息,只更新缩放值
|
|
458
|
+
if (animation) {
|
|
459
|
+
currentScale.value = withTiming(clampedScale, {
|
|
460
|
+
duration: 1000
|
|
461
|
+
}, () => {
|
|
462
|
+
handleRestBoundaryAndCheck()
|
|
463
|
+
})
|
|
464
|
+
} else {
|
|
465
|
+
currentScale.value = clampedScale
|
|
466
|
+
handleRestBoundaryAndCheck()
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (bindscale) {
|
|
471
|
+
runOnJS(handleTriggerScale)({
|
|
472
|
+
x: offsetX.value,
|
|
473
|
+
y: offsetY.value,
|
|
474
|
+
scale: clampedScale
|
|
475
|
+
})
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
})()
|
|
479
|
+
}, [scaleValue, scaleMin, scaleMax, animation])
|
|
480
|
+
|
|
481
|
+
useEffect(() => {
|
|
482
|
+
runOnUI(handleRestBoundaryAndCheck)()
|
|
249
483
|
}, [MovableAreaLayout.height, MovableAreaLayout.width])
|
|
250
484
|
|
|
485
|
+
// 生成唯一 ID
|
|
486
|
+
const viewId = useMemo(() => `movable-view-${Date.now()}-${Math.random()}`, [])
|
|
487
|
+
|
|
488
|
+
// 注册到 MovableArea(如果启用了 scale-area)
|
|
489
|
+
useEffect(() => {
|
|
490
|
+
if (MovableAreaLayout.scaleArea && MovableAreaLayout.registerMovableView && MovableAreaLayout.unregisterMovableView) {
|
|
491
|
+
const handleAreaScale = (scaleInfo: { scale: number }) => {
|
|
492
|
+
'worklet'
|
|
493
|
+
handleScaleUpdate({ scale: scaleInfo.scale })
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const handleAreaScaleEnd = () => {
|
|
497
|
+
'worklet'
|
|
498
|
+
handleRestBoundaryAndCheck()
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
MovableAreaLayout.registerMovableView?.(viewId, {
|
|
502
|
+
onScale: scale ? handleAreaScale : noop,
|
|
503
|
+
onScaleEnd: scale ? handleAreaScaleEnd : noop
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
return () => {
|
|
507
|
+
MovableAreaLayout.unregisterMovableView?.(viewId)
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}, [MovableAreaLayout.scaleArea, viewId, scale, handleScaleUpdate])
|
|
511
|
+
|
|
251
512
|
const getTouchSource = useCallback((offsetX: number, offsetY: number) => {
|
|
252
513
|
const hasOverBoundary = offsetX < draggableXRange.value[0] || offsetX > draggableXRange.value[1] ||
|
|
253
514
|
offsetY < draggableYRange.value[0] || offsetY > draggableYRange.value[1]
|
|
@@ -270,66 +531,50 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
270
531
|
}, [])
|
|
271
532
|
|
|
272
533
|
const setBoundary = useCallback(({ width, height }: { width: number; height: number }) => {
|
|
534
|
+
'worklet'
|
|
273
535
|
const top = (style.position === 'absolute' && style.top) || 0
|
|
274
536
|
const left = (style.position === 'absolute' && style.left) || 0
|
|
275
537
|
|
|
276
|
-
|
|
277
|
-
const
|
|
538
|
+
// 使用左上角缩放,计算offset位置的边界范围
|
|
539
|
+
const currentScaleVal = currentScale.value
|
|
540
|
+
const scaledWidth = (width || 0) * currentScaleVal
|
|
541
|
+
const scaledHeight = (height || 0) * currentScaleVal
|
|
278
542
|
|
|
279
|
-
|
|
280
|
-
const
|
|
543
|
+
// offset位置的边界:左上角可以移动的范围
|
|
544
|
+
const maxOffsetY = MovableAreaLayout.height - scaledHeight - top
|
|
545
|
+
const maxOffsetX = MovableAreaLayout.width - scaledWidth - left
|
|
281
546
|
|
|
282
547
|
let xRange: [min: number, max: number]
|
|
283
548
|
let yRange: [min: number, max: number]
|
|
284
549
|
|
|
285
550
|
if (MovableAreaLayout.width < scaledWidth) {
|
|
286
|
-
xRange = [
|
|
551
|
+
xRange = [maxOffsetX, -left]
|
|
287
552
|
} else {
|
|
288
|
-
xRange = [
|
|
553
|
+
xRange = [-left, maxOffsetX < 0 ? -left : maxOffsetX]
|
|
289
554
|
}
|
|
290
555
|
|
|
291
556
|
if (MovableAreaLayout.height < scaledHeight) {
|
|
292
|
-
yRange = [
|
|
557
|
+
yRange = [maxOffsetY, -top]
|
|
293
558
|
} else {
|
|
294
|
-
yRange = [
|
|
559
|
+
yRange = [-top, maxOffsetY < 0 ? -top : maxOffsetY]
|
|
295
560
|
}
|
|
561
|
+
|
|
296
562
|
draggableXRange.value = xRange
|
|
297
563
|
draggableYRange.value = yRange
|
|
298
564
|
}, [MovableAreaLayout.height, MovableAreaLayout.width, style.position, style.top, style.left])
|
|
299
565
|
|
|
300
|
-
const
|
|
566
|
+
const resetBoundaryAndCheck = ({ width, height }: { width: number; height: number }) => {
|
|
301
567
|
'worklet'
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
x = draggableXRange.value[0]
|
|
568
|
+
setBoundary({ width, height })
|
|
569
|
+
const positionX = offsetX.value
|
|
570
|
+
const positionY = offsetY.value
|
|
571
|
+
const { x: newX, y: newY } = checkBoundaryPosition({ positionX, positionY })
|
|
572
|
+
if (positionX !== newX) {
|
|
573
|
+
offsetX.value = newX
|
|
309
574
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
y = draggableYRange.value[1]
|
|
313
|
-
} else if (y < draggableYRange.value[0]) {
|
|
314
|
-
y = draggableYRange.value[0]
|
|
575
|
+
if (positionY !== newY) {
|
|
576
|
+
offsetY.value = newY
|
|
315
577
|
}
|
|
316
|
-
|
|
317
|
-
return { x, y }
|
|
318
|
-
}, [])
|
|
319
|
-
|
|
320
|
-
const resetBoundaryAndCheck = ({ width, height }: { width: number; height: number }) => {
|
|
321
|
-
setBoundary({ width, height })
|
|
322
|
-
runOnUI(() => {
|
|
323
|
-
const positionX = offsetX.value
|
|
324
|
-
const positionY = offsetY.value
|
|
325
|
-
const { x: newX, y: newY } = checkBoundaryPosition({ positionX, positionY })
|
|
326
|
-
if (positionX !== newX) {
|
|
327
|
-
offsetX.value = newX
|
|
328
|
-
}
|
|
329
|
-
if (positionY !== newY) {
|
|
330
|
-
offsetY.value = newY
|
|
331
|
-
}
|
|
332
|
-
})()
|
|
333
578
|
}
|
|
334
579
|
|
|
335
580
|
const onLayout = (e: LayoutChangeEvent) => {
|
|
@@ -342,14 +587,19 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
342
587
|
nodeRef.current?.measure((x: number, y: number, width: number, height: number) => {
|
|
343
588
|
const { y: navigationY = 0 } = navigation?.layout || {}
|
|
344
589
|
layoutRef.current = { x, y: y - navigationY, width, height, offsetLeft: 0, offsetTop: 0 }
|
|
345
|
-
|
|
590
|
+
// 同时更新 layoutValue,供缩放逻辑使用
|
|
591
|
+
runOnUI(() => {
|
|
592
|
+
layoutValue.value = { width, height }
|
|
593
|
+
resetBoundaryAndCheck({ width: width, height: height })
|
|
594
|
+
})()
|
|
346
595
|
})
|
|
347
|
-
|
|
596
|
+
propsOnLayout && propsOnLayout(e)
|
|
348
597
|
}
|
|
349
598
|
|
|
350
599
|
const extendEvent = useCallback((e: any, type: 'start' | 'move' | 'end') => {
|
|
351
600
|
const { y: navigationY = 0 } = navigation?.layout || {}
|
|
352
601
|
const touchArr = [e.changedTouches, e.allTouches]
|
|
602
|
+
const currentProps = propsRef.current
|
|
353
603
|
touchArr.forEach(touches => {
|
|
354
604
|
touches && touches.forEach((item: { absoluteX: number; absoluteY: number; pageX: number; pageY: number; clientX: number; clientY: number }) => {
|
|
355
605
|
item.pageX = item.absoluteX
|
|
@@ -361,8 +611,8 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
361
611
|
Object.assign(e, {
|
|
362
612
|
touches: type === 'end' ? [] : e.allTouches,
|
|
363
613
|
currentTarget: {
|
|
364
|
-
id:
|
|
365
|
-
dataset: collectDataset(
|
|
614
|
+
id: currentProps.id || '',
|
|
615
|
+
dataset: collectDataset(currentProps),
|
|
366
616
|
offsetLeft: 0,
|
|
367
617
|
offsetTop: 0
|
|
368
618
|
},
|
|
@@ -406,6 +656,14 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
406
656
|
catchtouchend && catchtouchend(e)
|
|
407
657
|
}
|
|
408
658
|
|
|
659
|
+
const handleRestBoundaryAndCheck = () => {
|
|
660
|
+
'worklet'
|
|
661
|
+
const { width, height } = layoutValue.value
|
|
662
|
+
if (width && height) {
|
|
663
|
+
resetBoundaryAndCheck({ width, height })
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
409
667
|
const gesture = useMemo(() => {
|
|
410
668
|
const handleTriggerMove = (e: GestureTouchEvent) => {
|
|
411
669
|
'worklet'
|
|
@@ -422,6 +680,8 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
422
680
|
}
|
|
423
681
|
|
|
424
682
|
const gesturePan = Gesture.Pan()
|
|
683
|
+
.minPointers(1)
|
|
684
|
+
.maxPointers(1)
|
|
425
685
|
.onTouchesDown((e: GestureTouchEvent) => {
|
|
426
686
|
'worklet'
|
|
427
687
|
const changedTouches = e.changedTouches[0] || { x: 0, y: 0 }
|
|
@@ -557,10 +817,12 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
557
817
|
})
|
|
558
818
|
.withRef(movableGestureRef)
|
|
559
819
|
|
|
560
|
-
if (
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
820
|
+
if (!disableEventPassthrough) {
|
|
821
|
+
if (direction === 'horizontal') {
|
|
822
|
+
gesturePan.activeOffsetX([-5, 5]).failOffsetY([-5, 5])
|
|
823
|
+
} else if (direction === 'vertical') {
|
|
824
|
+
gesturePan.activeOffsetY([-5, 5]).failOffsetX([-5, 5])
|
|
825
|
+
}
|
|
564
826
|
}
|
|
565
827
|
|
|
566
828
|
if (simultaneousHandlers && simultaneousHandlers.length) {
|
|
@@ -570,14 +832,46 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
570
832
|
if (waitForHandlers && waitForHandlers.length) {
|
|
571
833
|
gesturePan.requireExternalGestureToFail(...waitForHandlers)
|
|
572
834
|
}
|
|
835
|
+
|
|
836
|
+
// 添加缩放手势支持
|
|
837
|
+
if (scale && !MovableAreaLayout.scaleArea) {
|
|
838
|
+
const gesturePinch = Gesture.Pinch()
|
|
839
|
+
.onUpdate((e: any) => {
|
|
840
|
+
'worklet'
|
|
841
|
+
handleScaleUpdate({ scale: e.scale })
|
|
842
|
+
})
|
|
843
|
+
.onEnd((e: any) => {
|
|
844
|
+
'worklet'
|
|
845
|
+
if (disabled) return
|
|
846
|
+
// 确保最终缩放值在有效范围内
|
|
847
|
+
const finalScale = Math.max(scaleMin, Math.min(scaleMax, currentScale.value))
|
|
848
|
+
if (finalScale !== currentScale.value) {
|
|
849
|
+
currentScale.value = finalScale
|
|
850
|
+
if (bindscale) {
|
|
851
|
+
runOnJS(handleTriggerScale)({
|
|
852
|
+
x: offsetX.value,
|
|
853
|
+
y: offsetY.value,
|
|
854
|
+
scale: finalScale
|
|
855
|
+
})
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
// 缩放结束后重新检查边界
|
|
859
|
+
handleRestBoundaryAndCheck()
|
|
860
|
+
})
|
|
861
|
+
|
|
862
|
+
// 根据手指数量自动区分手势:一指移动,两指缩放
|
|
863
|
+
return Gesture.Exclusive(gesturePan, gesturePinch)
|
|
864
|
+
}
|
|
865
|
+
|
|
573
866
|
return gesturePan
|
|
574
|
-
}, [disabled, direction, inertia, outOfBounds, gestureSwitch.current])
|
|
867
|
+
}, [disabled, direction, inertia, outOfBounds, scale, scaleMin, scaleMax, animation, gestureSwitch.current, handleScaleUpdate, MovableAreaLayout.scaleArea])
|
|
575
868
|
|
|
576
869
|
const animatedStyles = useAnimatedStyle(() => {
|
|
577
870
|
return {
|
|
578
871
|
transform: [
|
|
579
872
|
{ translateX: offsetX.value },
|
|
580
|
-
{ translateY: offsetY.value }
|
|
873
|
+
{ translateY: offsetY.value },
|
|
874
|
+
{ scale: currentScale.value }
|
|
581
875
|
]
|
|
582
876
|
}
|
|
583
877
|
})
|
|
@@ -624,7 +918,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
624
918
|
{
|
|
625
919
|
ref: nodeRef,
|
|
626
920
|
onLayout: onLayout,
|
|
627
|
-
style: [innerStyle, animatedStyles, layoutStyle]
|
|
921
|
+
style: [{ transformOrigin: 'top left' }, innerStyle, animatedStyles, layoutStyle]
|
|
628
922
|
},
|
|
629
923
|
rewriteCatchEvent()
|
|
630
924
|
)
|
|
@@ -74,6 +74,7 @@ interface ScrollViewProps {
|
|
|
74
74
|
'wait-for'?: Array<GestureHandler>;
|
|
75
75
|
'simultaneous-handlers'?: Array<GestureHandler>;
|
|
76
76
|
'scroll-event-throttle'?:number;
|
|
77
|
+
'scroll-into-view-offset'?: number;
|
|
77
78
|
bindscrolltoupper?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
|
78
79
|
bindscrolltolower?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
|
79
80
|
bindscroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
|
@@ -149,6 +150,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
149
150
|
'wait-for': waitFor,
|
|
150
151
|
'enable-sticky': enableSticky,
|
|
151
152
|
'scroll-event-throttle': scrollEventThrottle = 0,
|
|
153
|
+
'scroll-into-view-offset': scrollIntoViewOffset = 0,
|
|
152
154
|
__selectRef
|
|
153
155
|
} = props
|
|
154
156
|
|
|
@@ -185,7 +187,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
185
187
|
const initialTimeout = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
186
188
|
const intersectionObservers = useContext(IntersectionObserverContext)
|
|
187
189
|
|
|
188
|
-
const firstScrollIntoViewChange = useRef<boolean>(
|
|
190
|
+
const firstScrollIntoViewChange = useRef<boolean>(true)
|
|
189
191
|
|
|
190
192
|
const refreshColor = {
|
|
191
193
|
black: ['#000'],
|
|
@@ -218,7 +220,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
218
220
|
pagingEnabled,
|
|
219
221
|
fastDeceleration: false,
|
|
220
222
|
decelerationDisabled: false,
|
|
221
|
-
scrollTo
|
|
223
|
+
scrollTo,
|
|
224
|
+
scrollIntoView: handleScrollIntoView
|
|
222
225
|
},
|
|
223
226
|
gestureRef: scrollViewRef
|
|
224
227
|
})
|
|
@@ -257,13 +260,15 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
257
260
|
|
|
258
261
|
useEffect(() => {
|
|
259
262
|
if (scrollIntoView && __selectRef) {
|
|
260
|
-
if (
|
|
261
|
-
setTimeout(
|
|
263
|
+
if (firstScrollIntoViewChange.current) {
|
|
264
|
+
setTimeout(() => {
|
|
265
|
+
handleScrollIntoView(scrollIntoView, { offset: scrollIntoViewOffset, animated: scrollWithAnimation })
|
|
266
|
+
})
|
|
262
267
|
} else {
|
|
263
|
-
handleScrollIntoView()
|
|
268
|
+
handleScrollIntoView(scrollIntoView, { offset: scrollIntoViewOffset, animated: scrollWithAnimation })
|
|
264
269
|
}
|
|
265
270
|
}
|
|
266
|
-
firstScrollIntoViewChange.current =
|
|
271
|
+
firstScrollIntoViewChange.current = false
|
|
267
272
|
}, [scrollIntoView])
|
|
268
273
|
|
|
269
274
|
useEffect(() => {
|
|
@@ -286,14 +291,16 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
286
291
|
scrollToOffset(left, top, animated)
|
|
287
292
|
}
|
|
288
293
|
|
|
289
|
-
function handleScrollIntoView () {
|
|
290
|
-
const refs = __selectRef!(`#${
|
|
294
|
+
function handleScrollIntoView (selector = '', { offset = 0, animated = true } = {}) {
|
|
295
|
+
const refs = __selectRef!(`#${selector}`, 'node')
|
|
291
296
|
if (!refs) return
|
|
292
297
|
const { nodeRef } = refs.getNodeInstance()
|
|
293
298
|
nodeRef.current?.measureLayout(
|
|
294
299
|
scrollViewRef.current,
|
|
295
300
|
(left: number, top: number) => {
|
|
296
|
-
|
|
301
|
+
const adjustedLeft = scrollX ? left + offset : left
|
|
302
|
+
const adjustedTop = scrollY ? top + offset : top
|
|
303
|
+
scrollToOffset(adjustedLeft, adjustedTop, animated)
|
|
297
304
|
}
|
|
298
305
|
)
|
|
299
306
|
}
|
|
@@ -171,7 +171,9 @@ const _StickyHeader = forwardRef<HandlerRef<View, StickyHeaderProps>, StickyHead
|
|
|
171
171
|
const styles = StyleSheet.create({
|
|
172
172
|
content: {
|
|
173
173
|
width: '100%',
|
|
174
|
-
zIndex: 10
|
|
174
|
+
zIndex: 10,
|
|
175
|
+
// harmony 需要手动设置 relative, zIndex 才生效
|
|
176
|
+
position: 'relative'
|
|
175
177
|
}
|
|
176
178
|
})
|
|
177
179
|
|