Skip to content

Commit e73103e

Browse files
authored
Merge pull request #1770 from didi/feat-movable-event-v4
feat: movable-view 支持 tap、longpress 事件
2 parents ad8f04d + a12b910 commit e73103e

1 file changed

Lines changed: 118 additions & 73 deletions

File tree

packages/webpack-plugin/lib/runtime/components/react/mpx-movable-view.tsx

Lines changed: 118 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
*/
2020
import { useEffect, forwardRef, ReactNode, useContext, useCallback, useRef, useMemo, createElement } from 'react'
2121
import { StyleSheet, NativeSyntheticEvent, View, LayoutChangeEvent } from 'react-native'
22-
import { getCustomEvent } from './getInnerListeners'
22+
import useInnerProps, { getCustomEvent } from './getInnerListeners'
2323
import useNodesRef, { HandlerRef } from './useNodesRef'
2424
import { MovableAreaContext } from './context'
25-
import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, GestureHandler, flatGesture, extendObject } from './utils'
25+
import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, GestureHandler, flatGesture, extendObject, omit } from './utils'
2626
import { GestureDetector, Gesture, GestureTouchEvent, GestureStateChangeEvent, PanGestureHandlerEventPayload, PanGesture } from 'react-native-gesture-handler'
2727
import Animated, {
2828
useSharedValue,
@@ -33,6 +33,7 @@ import Animated, {
3333
useAnimatedReaction,
3434
withSpring
3535
} from 'react-native-reanimated'
36+
import { collectDataset, noop } from '@mpxjs/utils'
3637

3738
interface MovableViewProps {
3839
children: ReactNode;
@@ -42,17 +43,22 @@ interface MovableViewProps {
4243
y?: number;
4344
disabled?: boolean;
4445
animation?: boolean;
46+
id?: string;
4547
bindchange?: (event: unknown) => void;
46-
bindtouchstart?: (event: NativeSyntheticEvent<TouchEvent>) => void;
47-
catchtouchstart?: (event: NativeSyntheticEvent<TouchEvent>) => void;
48-
bindtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
49-
catchtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
50-
catchtouchend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
51-
bindtouchend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
52-
bindhtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
53-
bindvtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
54-
catchhtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
55-
catchvtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
48+
bindtouchstart?: (event: GestureTouchEvent) => void;
49+
catchtouchstart?: (event: GestureTouchEvent) => void;
50+
bindtouchmove?: (event: GestureTouchEvent) => void;
51+
catchtouchmove?: (event: GestureTouchEvent) => void;
52+
catchtouchend?: (event: GestureTouchEvent) => void;
53+
bindtouchend?: (event: GestureTouchEvent) => void;
54+
bindhtouchmove?: (event: GestureTouchEvent) => void;
55+
bindvtouchmove?: (event: GestureTouchEvent) => void;
56+
catchhtouchmove?: (event: GestureTouchEvent) => void;
57+
catchvtouchmove?: (event: GestureTouchEvent) => void;
58+
bindlongpress?: (event: GestureTouchEvent) => void;
59+
catchlongpress?: (event: GestureTouchEvent) => void;
60+
bindtap?: (event: GestureTouchEvent) => void;
61+
catchtap?: (event: GestureTouchEvent) => void;
5662
onLayout?: (event: LayoutChangeEvent) => void;
5763
'out-of-bounds'?: boolean;
5864
'wait-for'?: Array<GestureHandler>;
@@ -153,10 +159,10 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
153159
})
154160

155161
const hasSimultaneousHandlersChanged = prevSimultaneousHandlersRef.current.length !== (originSimultaneousHandlers?.length || 0) ||
156-
(originSimultaneousHandlers || []).some((handler, index) => handler !== prevSimultaneousHandlersRef.current[index])
162+
(originSimultaneousHandlers || []).some((handler, index) => handler !== prevSimultaneousHandlersRef.current[index])
157163

158164
const hasWaitForHandlersChanged = prevWaitForHandlersRef.current.length !== (waitFor?.length || 0) ||
159-
(waitFor || []).some((handler, index) => handler !== prevWaitForHandlersRef.current[index])
165+
(waitFor || []).some((handler, index) => handler !== prevWaitForHandlersRef.current[index])
160166

161167
if (hasSimultaneousHandlersChanged || hasWaitForHandlersChanged) {
162168
gestureSwitch.current = !gestureSwitch.current
@@ -333,57 +339,77 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
333339
props.onLayout && props.onLayout(e)
334340
}
335341

336-
const extendEvent = useCallback((e: any) => {
337-
'worklet'
342+
const extendEvent = useCallback((e: any, obj?: Record<string, any>) => {
338343
const touchArr = [e.changedTouches, e.allTouches]
339344
touchArr.forEach(touches => {
340345
touches && touches.forEach((item: { absoluteX: number; absoluteY: number; pageX: number; pageY: number }) => {
341346
item.pageX = item.absoluteX
342347
item.pageY = item.absoluteY
343348
})
344349
})
345-
e.touches = e.allTouches
350+
Object.assign(e, {
351+
touches: e.allTouches,
352+
detail: {
353+
x: e.changedTouches[0].absoluteX,
354+
y: e.changedTouches[0].absoluteY
355+
},
356+
currentTarget: {
357+
id: props.id || '',
358+
dataset: collectDataset(props),
359+
offsetLeft: 0,
360+
offsetTop: 0
361+
}
362+
}, obj)
346363
}, [])
347364

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
365+
const triggerStartOnJS = ({ e }: { e: GestureTouchEvent }) => {
366+
extendEvent(e)
367+
bindtouchstart && bindtouchstart(e)
368+
catchtouchstart && catchtouchstart(e)
369+
}
361370

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)
371+
const triggerMoveOnJS = ({ e, hasTouchmove, hasCatchTouchmove, touchEvent }: { e: GestureTouchEvent; hasTouchmove: boolean; hasCatchTouchmove: boolean; touchEvent: string }) => {
372+
extendEvent(e)
373+
if (hasTouchmove) {
374+
if (touchEvent === 'htouchmove') {
375+
bindhtouchmove && bindhtouchmove(e)
376+
} else if (touchEvent === 'vtouchmove') {
377+
bindvtouchmove && bindvtouchmove(e)
369378
}
379+
bindtouchmove && bindtouchmove(e)
380+
}
370381

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)
382+
if (hasCatchTouchmove) {
383+
if (touchEvent === 'htouchmove') {
384+
catchhtouchmove && catchhtouchmove(e)
385+
} else if (touchEvent === 'vtouchmove') {
386+
catchvtouchmove && catchvtouchmove(e)
378387
}
388+
catchtouchmove && catchtouchmove(e)
379389
}
390+
}
391+
392+
const triggerEndOnJS = ({ e }: { e: GestureTouchEvent }) => {
393+
extendEvent(e)
394+
bindtouchend && bindtouchend(e)
395+
catchtouchend && catchtouchend(e)
396+
}
380397

381-
const handleTriggerEnd = (e: any) => {
398+
const gesture = useMemo(() => {
399+
const handleTriggerMove = (e: GestureTouchEvent) => {
382400
'worklet'
383-
extendEvent(e)
384-
bindtouchend && runOnJS(bindtouchend)(e)
385-
catchtouchend && runOnJS(catchtouchend)(e)
401+
const hasTouchmove = !!bindhtouchmove || !!bindvtouchmove || !!bindtouchmove
402+
const hasCatchTouchmove = !!catchhtouchmove || !!catchvtouchmove || !!catchtouchmove
403+
if (hasTouchmove || hasCatchTouchmove) {
404+
runOnJS(triggerMoveOnJS)({
405+
e,
406+
touchEvent: touchEvent.value,
407+
hasTouchmove,
408+
hasCatchTouchmove
409+
})
410+
}
386411
}
412+
387413
const gesturePan = Gesture.Pan()
388414
.onTouchesDown((e: GestureTouchEvent) => {
389415
'worklet'
@@ -393,12 +419,14 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
393419
x: changedTouches.x,
394420
y: changedTouches.y
395421
}
396-
handleTriggerStart(e)
422+
if (bindtouchstart || catchtouchstart) {
423+
runOnJS(triggerStartOnJS)({ e })
424+
}
397425
})
398426
.onTouchesMove((e: GestureTouchEvent) => {
399427
'worklet'
400-
isMoving.value = true
401428
const changedTouches = e.changedTouches[0] || { x: 0, y: 0 }
429+
isMoving.value = true
402430
if (isFirstTouch.value) {
403431
touchEvent.value = Math.abs(changedTouches.x - startPosition.value.x) > Math.abs(changedTouches.y - startPosition.value.y) ? 'htouchmove' : 'vtouchmove'
404432
isFirstTouch.value = false
@@ -430,7 +458,9 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
430458
'worklet'
431459
isFirstTouch.value = true
432460
isMoving.value = false
433-
handleTriggerEnd(e)
461+
if (bindtouchend || catchtouchend) {
462+
runOnJS(triggerEndOnJS)({ e })
463+
}
434464
if (disabled) return
435465
if (!inertia) {
436466
const { x, y } = checkBoundaryPosition({ positionX: offsetX.value, positionY: offsetY.value })
@@ -454,8 +484,8 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
454484
})
455485
.onFinalize((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
456486
'worklet'
457-
if (!inertia || disabled || !animation) return
458487
isMoving.value = false
488+
if (!inertia || disabled || !animation) return
459489
if (direction === 'horizontal' || direction === 'all') {
460490
xInertialMotion.value = true
461491
offsetX.value = withDecay({
@@ -497,35 +527,50 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
497527
}
498528
})
499529

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'] }
530+
const rewriteCatchEvent = () => {
531+
const handlers: Record<string, typeof noop> = {}
532+
533+
const events = [
534+
{ type: 'touchstart' },
535+
{ type: 'touchmove', alias: ['vtouchmove', 'htouchmove'] },
536+
{ type: 'touchend' }
506537
]
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-
})
538+
events.forEach(({ type, alias = [] }) => {
539+
const hasCatchEvent =
540+
props[`catch${type}` as keyof typeof props] ||
541+
alias.some(name => props[`catch${name}` as keyof typeof props])
542+
if (hasCatchEvent) handlers[`catch${type}`] = noop
515543
})
516-
return eventHandlers
544+
545+
return handlers
517546
}
518547

519-
const catchEventHandlers = injectCatchEvent(props)
520548
const layoutStyle = !hasLayoutRef.current && hasSelfPercent ? HIDDEN_STYLE : {}
521549

550+
// bind 相关 touch 事件直接由 gesture 触发,无须重复挂载
551+
// catch 相关 touch 事件需要重写并通过 useInnerProps 注入阻止冒泡逻辑
552+
const filterProps = omit(props, [
553+
'bindtouchstart',
554+
'bindtouchmove',
555+
'bindvtouchmove',
556+
'bindhtouchmove',
557+
'bindtouchend',
558+
'catchtouchstart',
559+
'catchtouchmove',
560+
'catchvtouchmove',
561+
'catchhtouchmove',
562+
'catchtouchend'
563+
])
564+
565+
const innerProps = useInnerProps(filterProps, extendObject({
566+
ref: nodeRef,
567+
onLayout: onLayout,
568+
style: [innerStyle, animatedStyles, layoutStyle]
569+
}, rewriteCatchEvent()))
570+
522571
return createElement(GestureDetector, { gesture: gesture }, createElement(
523572
Animated.View,
524-
extendObject({
525-
ref: nodeRef,
526-
onLayout: onLayout,
527-
style: [innerStyle, animatedStyles, layoutStyle]
528-
}, catchEventHandlers),
573+
innerProps,
529574
wrapChildren(
530575
props,
531576
{

0 commit comments

Comments
 (0)