-
Notifications
You must be signed in to change notification settings - Fork 26
Refactor: replaced mannual calculation to library** =@use-gesture/react library
#43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
+172
−357
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
eca54c5
added-some-track-pad-guestures
aniket866 697f135
migrate-to-gesture-library
aniket866 115ba76
Merge branch 'AOSSIE-Org:main' into added-more-guestures
aniket866 10d1a4f
fixing-2-finger-scroll
aniket866 934d0f2
Code rabbit follow up
aniket866 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
aniket866 marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
aniket866 marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,225 +1,84 @@ | ||
| import { useRef, useState } from 'react'; | ||
| import { TOUCH_MOVE_THRESHOLD, TOUCH_TIMEOUT, PINCH_THRESHOLD, calculateAccelerationMult } from '../utils/math'; | ||
| import { useState, useRef } from 'react'; | ||
| import { useGesture } from '@use-gesture/react'; | ||
|
|
||
| interface TrackedTouch { | ||
| identifier: number; | ||
| pageX: number; | ||
| pageY: number; | ||
| pageXStart: number; | ||
| pageYStart: number; | ||
| timeStamp: number; | ||
| } | ||
| const MOVE_MULTIPLIER = 1.2; | ||
| const SCROLL_MULTIPLIER = 3.0; | ||
|
|
||
| const getTouchDistance = (a: TrackedTouch, b: TrackedTouch): number => { | ||
| const dx = a.pageX - b.pageX; | ||
| const dy = a.pageY - b.pageY; | ||
| return Math.sqrt(dx * dx + dy * dy); | ||
| }; | ||
| const TAP_TIME = 200; | ||
| const TAP_MOVE_THRESHOLD = 6; | ||
|
|
||
| export const useTrackpadGesture = ( | ||
| send: (msg: any) => void, | ||
| scrollMode: boolean, | ||
| sensitivity: number = 1.5 | ||
| scrollMode: boolean | ||
| ) => { | ||
| const [isTracking, setIsTracking] = useState(false); | ||
|
|
||
| // Refs for tracking state (avoids re-renders during rapid movement) | ||
| const ongoingTouches = useRef<TrackedTouch[]>([]); | ||
| const moved = useRef(false); | ||
| const startTimeStamp = useRef(0); | ||
| const lastEndTimeStamp = useRef(0); | ||
| const releasedCount = useRef(0); | ||
| const dragging = useRef(false); | ||
| const draggingTimeout = useRef<NodeJS.Timeout | null>(null); | ||
| const lastPinchDist = useRef<number | null>(null); | ||
| const pinching = useRef(false); | ||
|
|
||
| // Helpers | ||
| const findTouchIndex = (id: number) => ongoingTouches.current.findIndex(t => t.identifier === id); | ||
|
|
||
| const handleDraggingTimeout = () => { | ||
| draggingTimeout.current = null; | ||
| send({ type: 'click', button: 'left', press: false }); | ||
| }; | ||
|
|
||
| const handleTouchStart = (e: React.TouchEvent) => { | ||
| if (ongoingTouches.current.length === 0) { | ||
| startTimeStamp.current = e.timeStamp; | ||
| moved.current = false; | ||
| } | ||
|
|
||
| const touches = e.changedTouches; | ||
| for (let i = 0; i < touches.length; i++) { | ||
| const touch = touches[i]; | ||
| const tracked: TrackedTouch = { | ||
| identifier: touch.identifier, | ||
| pageX: touch.pageX, | ||
| pageY: touch.pageY, | ||
| pageXStart: touch.pageX, | ||
| pageYStart: touch.pageY, | ||
| timeStamp: e.timeStamp, | ||
| }; | ||
| const idx = findTouchIndex(touch.identifier); | ||
| if (idx < 0) { | ||
| ongoingTouches.current.push(tracked); | ||
| } else { | ||
| ongoingTouches.current[idx] = tracked; | ||
| } | ||
| } | ||
|
|
||
| if (ongoingTouches.current.length === 2) { | ||
| lastPinchDist.current = getTouchDistance(ongoingTouches.current[0], ongoingTouches.current[1]); | ||
| pinching.current = false; | ||
| } | ||
|
|
||
| setIsTracking(true); | ||
| lastEndTimeStamp.current = 0; | ||
|
|
||
| // If we're in dragging timeout, convert to actual drag | ||
| if (draggingTimeout.current) { | ||
| clearTimeout(draggingTimeout.current); | ||
| draggingTimeout.current = null; | ||
| dragging.current = true; | ||
| } | ||
| }; | ||
|
|
||
| const handleTouchMove = (e: React.TouchEvent) => { | ||
|
|
||
| const touches = e.changedTouches; | ||
| let sumX = 0; | ||
| let sumY = 0; | ||
|
|
||
| for (let i = 0; i < touches.length; i++) { | ||
| const touch = touches[i]; | ||
| const idx = findTouchIndex(touch.identifier); | ||
| if (idx < 0) continue; | ||
|
|
||
| const tracked = ongoingTouches.current[idx]; | ||
|
|
||
| // Check if we've moved enough to consider this a "move" gesture | ||
| if (!moved.current) { | ||
| const dist = Math.sqrt( | ||
| Math.pow(touch.pageX - tracked.pageXStart, 2) + | ||
| Math.pow(touch.pageY - tracked.pageYStart, 2) | ||
| ); | ||
| const threshold = ongoingTouches.current.length > TOUCH_MOVE_THRESHOLD.length | ||
| ? TOUCH_MOVE_THRESHOLD[TOUCH_MOVE_THRESHOLD.length - 1] | ||
| : TOUCH_MOVE_THRESHOLD[ongoingTouches.current.length - 1]; | ||
|
|
||
| if (dist > threshold || e.timeStamp - startTimeStamp.current >= TOUCH_TIMEOUT) { | ||
| moved.current = true; | ||
| const startTimeRef = useRef(0); | ||
| const movedRef = useRef(false); | ||
| const touchCountRef = useRef(1); | ||
|
|
||
| const bind = useGesture( | ||
| { | ||
| onPointerDown: () => { | ||
| startTimeRef.current = Date.now(); | ||
| movedRef.current = false; | ||
| setIsTracking(true); | ||
| }, | ||
|
|
||
| onDrag: ({ delta, touches }) => { | ||
| const [dx, dy] = delta; | ||
| touchCountRef.current = touches; | ||
|
|
||
| if ( | ||
| Math.abs(dx) > TAP_MOVE_THRESHOLD || | ||
| Math.abs(dy) > TAP_MOVE_THRESHOLD | ||
| ) { | ||
| movedRef.current = true; | ||
| } | ||
| } | ||
|
|
||
| // Calculate delta with acceleration | ||
| const dx = touch.pageX - tracked.pageX; | ||
| const dy = touch.pageY - tracked.pageY; | ||
| const timeDelta = e.timeStamp - tracked.timeStamp; | ||
|
|
||
| if (timeDelta > 0) { | ||
| const speedX = Math.abs(dx) / timeDelta * 1000; | ||
| const speedY = Math.abs(dy) / timeDelta * 1000; | ||
| sumX += dx * calculateAccelerationMult(speedX); | ||
| sumY += dy * calculateAccelerationMult(speedY); | ||
| } | ||
|
|
||
| // Update tracked position | ||
| tracked.pageX = touch.pageX; | ||
| tracked.pageY = touch.pageY; | ||
| tracked.timeStamp = e.timeStamp; | ||
| } | ||
|
|
||
| // Send movement if we've moved and not in timeout period | ||
| if (moved.current && e.timeStamp - lastEndTimeStamp.current >= TOUCH_TIMEOUT) { | ||
| if (!scrollMode && ongoingTouches.current.length === 2) { | ||
| const dist = getTouchDistance(ongoingTouches.current[0], ongoingTouches.current[1]); | ||
| const delta = lastPinchDist.current !== null ? dist - lastPinchDist.current : 0; | ||
| if (pinching.current || Math.abs(delta) > PINCH_THRESHOLD) { | ||
| pinching.current = true; | ||
| lastPinchDist.current = dist; | ||
| send({ type: 'zoom', delta: delta * sensitivity }); | ||
| if (touches === 2 || scrollMode) { | ||
| send({ | ||
| type: 'scroll', | ||
| dx: dx * SCROLL_MULTIPLIER, | ||
| dy: dy * SCROLL_MULTIPLIER | ||
| }); | ||
| } else { | ||
| lastPinchDist.current = dist; | ||
| send({ type: 'scroll', dx: -sumX * sensitivity, dy: -sumY * sensitivity }); | ||
| send({ | ||
| type: 'move', | ||
| dx: dx * MOVE_MULTIPLIER, | ||
| dy: dy * MOVE_MULTIPLIER | ||
| }); | ||
| } | ||
| } else if (scrollMode) { | ||
| // Scroll mode: single finger scrolls, or two-finger scroll in cursor mode | ||
| send({ type: 'scroll', dx: -sumX * sensitivity, dy: -sumY * sensitivity }); | ||
| } else if (ongoingTouches.current.length === 1 || dragging.current) { | ||
| // Cursor movement (only in cursor mode with 1 finger, or when dragging) | ||
| send({ type: 'move', dx: sumX * sensitivity, dy: sumY * sensitivity }); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| const handleTouchEnd = (e: React.TouchEvent) => { | ||
|
|
||
| const touches = e.changedTouches; | ||
|
|
||
| for (let i = 0; i < touches.length; i++) { | ||
| const idx = findTouchIndex(touches[i].identifier); | ||
| if (idx >= 0) { | ||
| ongoingTouches.current.splice(idx, 1); | ||
| releasedCount.current += 1; | ||
| } | ||
| } | ||
|
|
||
| lastEndTimeStamp.current = e.timeStamp; | ||
|
|
||
| if (ongoingTouches.current.length < 2) { | ||
| lastPinchDist.current = null; | ||
| pinching.current = false; | ||
| } | ||
|
|
||
| // Mark as moved if too many fingers | ||
| if (releasedCount.current > TOUCH_MOVE_THRESHOLD.length) { | ||
| moved.current = true; | ||
| } | ||
| }, | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // All fingers lifted | ||
| if (ongoingTouches.current.length === 0 && releasedCount.current >= 1) { | ||
| setIsTracking(false); | ||
| onPointerUp: () => { | ||
| setIsTracking(false); | ||
| const duration = Date.now() - startTimeRef.current; | ||
|
|
||
| // Release drag if active | ||
| if (dragging.current) { | ||
| dragging.current = false; | ||
| send({ type: 'click', button: 'left', press: false }); | ||
| } | ||
|
|
||
| // Handle tap/click if not moved and within timeout | ||
| if (!moved.current && e.timeStamp - startTimeStamp.current < TOUCH_TIMEOUT) { | ||
| let button: 'left' | 'right' | 'middle' | null = null; | ||
| if (!movedRef.current && duration <= TAP_TIME) { | ||
| const button = | ||
| touchCountRef.current === 2 ? 'right' : 'left'; | ||
|
|
||
| if (releasedCount.current === 1) { | ||
| button = 'left'; | ||
| } else if (releasedCount.current === 2) { | ||
| button = 'right'; | ||
| } else if (releasedCount.current === 3) { | ||
| button = 'middle'; | ||
| } | ||
|
|
||
| if (button) { | ||
| send({ type: 'click', button, press: true }); | ||
|
|
||
| // For left click, set up drag timeout | ||
| if (button === 'left') { | ||
| draggingTimeout.current = setTimeout(handleDraggingTimeout, TOUCH_TIMEOUT); | ||
| } else { | ||
| send({ type: 'click', button, press: false }); | ||
| } | ||
| setTimeout( | ||
| () => | ||
| send({ | ||
| type: 'click', | ||
| button, | ||
| press: false | ||
| }), | ||
| 40 | ||
| ); | ||
| } | ||
aniket866 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| releasedCount.current = 0; | ||
| }, | ||
| { | ||
| drag: { | ||
| pointer: { touch: true }, | ||
| threshold: 0 | ||
| } | ||
| } | ||
| }; | ||
| ); | ||
|
|
||
| return { | ||
| isTracking, | ||
| handlers: { | ||
| onTouchStart: handleTouchStart, | ||
| onTouchMove: handleTouchMove, | ||
| onTouchEnd: handleTouchEnd | ||
| } | ||
| }; | ||
| return { handlers: bind(), isTracking }; | ||
| }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.