diff options
Diffstat (limited to 'src/view/com/util/gestures/SwipeAndZoom.tsx')
-rw-r--r-- | src/view/com/util/gestures/SwipeAndZoom.tsx | 302 |
1 files changed, 0 insertions, 302 deletions
diff --git a/src/view/com/util/gestures/SwipeAndZoom.tsx b/src/view/com/util/gestures/SwipeAndZoom.tsx deleted file mode 100644 index 75c679012..000000000 --- a/src/view/com/util/gestures/SwipeAndZoom.tsx +++ /dev/null @@ -1,302 +0,0 @@ -import React, {useState} from 'react' -import { - Animated, - GestureResponderEvent, - I18nManager, - PanResponder, - PanResponderGestureState, - useWindowDimensions, - View, -} from 'react-native' -import {clamp} from 'lodash' -import {s} from 'lib/styles' - -export enum Dir { - None, - Up, - Down, - Left, - Right, - Zoom, -} - -interface Props { - panX: Animated.Value - panY: Animated.Value - zoom: Animated.Value - canSwipeLeft?: boolean - canSwipeRight?: boolean - canSwipeUp?: boolean - canSwipeDown?: boolean - swipeEnabled?: boolean - zoomEnabled?: boolean - hasPriority?: boolean // if has priority, will not release control of the gesture to another gesture - horzDistThresholdDivisor?: number - vertDistThresholdDivisor?: number - useNativeDriver?: boolean - onSwipeStart?: () => void - onSwipeStartDirection?: (dir: Dir) => void - onSwipeEnd?: (dir: Dir) => void - children: React.ReactNode -} - -export function SwipeAndZoom({ - panX, - panY, - zoom, - canSwipeLeft = false, - canSwipeRight = false, - canSwipeUp = false, - canSwipeDown = false, - swipeEnabled = false, - zoomEnabled = false, - hasPriority = false, - horzDistThresholdDivisor = 1.75, - vertDistThresholdDivisor = 1.75, - useNativeDriver = false, - onSwipeStart, - onSwipeStartDirection, - onSwipeEnd, - children, -}: Props) { - const winDim = useWindowDimensions() - const [dir, setDir] = useState<Dir>(Dir.None) - const [initialDistance, setInitialDistance] = useState<number | undefined>( - undefined, - ) - - const swipeVelocityThreshold = 35 - const swipeHorzDistanceThreshold = winDim.width / horzDistThresholdDivisor - const swipeVertDistanceThreshold = winDim.height / vertDistThresholdDivisor - - const isMovingHorizontally = ( - _: GestureResponderEvent, - gestureState: PanResponderGestureState, - ) => { - return ( - Math.abs(gestureState.dx) > Math.abs(gestureState.dy * 1.25) && - Math.abs(gestureState.vx) > Math.abs(gestureState.vy * 1.25) - ) - } - const isMovingVertically = ( - _: GestureResponderEvent, - gestureState: PanResponderGestureState, - ) => { - return ( - Math.abs(gestureState.dy) > Math.abs(gestureState.dx * 1.25) && - Math.abs(gestureState.vy) > Math.abs(gestureState.vx * 1.25) - ) - } - - const canDir = (d: Dir) => { - if (d === Dir.Left) { - return canSwipeLeft - } - if (d === Dir.Right) { - return canSwipeRight - } - if (d === Dir.Up) { - return canSwipeUp - } - if (d === Dir.Down) { - return canSwipeDown - } - if (d === Dir.Zoom) { - return zoomEnabled - } - return false - } - const isHorz = (d: Dir) => d === Dir.Left || d === Dir.Right - const isVert = (d: Dir) => d === Dir.Up || d === Dir.Down - - const canMoveScreen = ( - event: GestureResponderEvent, - gestureState: PanResponderGestureState, - ) => { - if (zoomEnabled && gestureState.numberActiveTouches === 2) { - return true - } else if (swipeEnabled && gestureState.numberActiveTouches === 1) { - const dx = I18nManager.isRTL ? -gestureState.dx : gestureState.dx - const dy = gestureState.dy - const willHandle = - (isMovingHorizontally(event, gestureState) && - ((dx > 0 && canSwipeLeft) || (dx < 0 && canSwipeRight))) || - (isMovingVertically(event, gestureState) && - ((dy > 0 && canSwipeUp) || (dy < 0 && canSwipeDown))) - return willHandle - } - return false - } - - const startGesture = () => { - setDir(Dir.None) - onSwipeStart?.() - - // reset all state - panX.stopAnimation() - // @ts-expect-error: _value is private, but docs use it as well - panX.setOffset(panX._value) - panY.stopAnimation() - // @ts-expect-error: _value is private, but docs use it as well - panY.setOffset(panY._value) - zoom.stopAnimation() - // @ts-expect-error: _value is private, but docs use it as well - zoom.setOffset(zoom._value) - setInitialDistance(undefined) - } - - const respondToGesture = ( - e: GestureResponderEvent, - gestureState: PanResponderGestureState, - ) => { - const dx = I18nManager.isRTL ? -gestureState.dx : gestureState.dx - const dy = gestureState.dy - - let newDir = Dir.None - if (dir === Dir.None) { - // establish if the user is swiping horz or vert, or zooming - if (gestureState.numberActiveTouches === 2) { - newDir = Dir.Zoom - } else if (Math.abs(dx) > Math.abs(dy)) { - newDir = dx > 0 ? Dir.Left : Dir.Right - } else { - newDir = dy > 0 ? Dir.Up : Dir.Down - } - } else if (isHorz(dir)) { - // direction update - newDir = dx > 0 ? Dir.Left : Dir.Right - } else if (isVert(dir)) { - // direction update - newDir = dy > 0 ? Dir.Up : Dir.Down - } else { - newDir = dir - } - - if (newDir === Dir.Zoom) { - if (zoomEnabled) { - if (gestureState.numberActiveTouches === 2) { - // zoom in/out - const x0 = e.nativeEvent.touches[0].pageX - const x1 = e.nativeEvent.touches[1].pageX - const y0 = e.nativeEvent.touches[0].pageY - const y1 = e.nativeEvent.touches[1].pageY - const zoomDx = Math.abs(x0 - x1) - const zoomDy = Math.abs(y0 - y1) - const dist = Math.sqrt(zoomDx * zoomDx + zoomDy * zoomDy) / 100 - if ( - typeof initialDistance === 'undefined' || - dist - initialDistance < 0 - ) { - setInitialDistance(dist) - } else { - zoom.setValue(dist - initialDistance) - } - } else { - // pan around after zooming - panX.setValue(clamp(dx / winDim.width, -1, 1) * -1) - panY.setValue(clamp(dy / winDim.height, -1, 1) * -1) - } - } - } else if (isHorz(newDir)) { - // swipe left/right - panX.setValue( - clamp( - dx / swipeHorzDistanceThreshold, - canSwipeRight ? -1 : 0, - canSwipeLeft ? 1 : 0, - ) * -1, - ) - panY.setValue(0) - } else if (isVert(newDir)) { - // swipe up/down - panY.setValue( - clamp( - dy / swipeVertDistanceThreshold, - canSwipeDown ? -1 : 0, - canSwipeUp ? 1 : 0, - ) * -1, - ) - panX.setValue(0) - } - - if (!canDir(newDir)) { - newDir = Dir.None - } - if (newDir !== dir) { - setDir(newDir) - onSwipeStartDirection?.(newDir) - } - } - - const finishGesture = ( - _: GestureResponderEvent, - gestureState: PanResponderGestureState, - ) => { - const finish = (finalDir: Dir) => () => { - if (finalDir !== Dir.None) { - onSwipeEnd?.(finalDir) - } - setDir(Dir.None) - panX.flattenOffset() - panX.setValue(0) - panY.flattenOffset() - panY.setValue(0) - } - if ( - isHorz(dir) && - (Math.abs(gestureState.dx) > swipeHorzDistanceThreshold / 4 || - Math.abs(gestureState.vx) > swipeVelocityThreshold) - ) { - // horizontal swipe reset - Animated.timing(panX, { - toValue: dir === Dir.Left ? -1 : 1, - duration: 100, - useNativeDriver, - }).start(finish(dir)) - } else if ( - isVert(dir) && - (Math.abs(gestureState.dy) > swipeVertDistanceThreshold / 8 || - Math.abs(gestureState.vy) > swipeVelocityThreshold) - ) { - // vertical swipe reset - Animated.timing(panY, { - toValue: dir === Dir.Up ? -1 : 1, - duration: 100, - useNativeDriver, - }).start(finish(dir)) - } else { - // zoom (or no direction) reset - onSwipeEnd?.(Dir.None) - Animated.timing(panX, { - toValue: 0, - duration: 100, - useNativeDriver, - }).start() - Animated.timing(panY, { - toValue: 0, - duration: 100, - useNativeDriver, - }).start() - Animated.timing(zoom, { - toValue: 0, - duration: 100, - useNativeDriver, - }).start() - } - } - - const panResponder = PanResponder.create({ - onMoveShouldSetPanResponder: canMoveScreen, - onPanResponderGrant: startGesture, - onPanResponderMove: respondToGesture, - onPanResponderTerminate: finishGesture, - onPanResponderRelease: finishGesture, - onPanResponderTerminationRequest: () => !hasPriority, - }) - - return ( - <View {...panResponder.panHandlers} style={s.h100pct}> - {children} - </View> - ) -} |