Skip to content

Commit a0b7a77

Browse files
committed
Improve touch navigation behavior
- Detect multi-touch events and suppress swipe gestures when more than one touch point is active during the gesture. - Tweak parameters of swipe detection to take longer horizontal movement and accept slightly more vertical movement. - Introduce a maximum duration for swipe gestures (300 ms). - Increment version to 2.1.1.
1 parent 9ff8f08 commit a0b7a77

File tree

3 files changed

+50
-13
lines changed

3 files changed

+50
-13
lines changed

β€Žpackage-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žpackage.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tma-react-gallery",
3-
"version": "2.1.0",
3+
"version": "2.1.1",
44
"license": "AGPL-3.0-or-later",
55
"private": true,
66
"dependencies": {

β€Žsrc/Gallery.js

+47-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import imgNewtab from './img/newtab-white.png';
1414
* (β€’) Add (pop-out?) list view for images/videos in bucket.
1515
* (β€’) Add sorting options/controls.
1616
* (β€’) Add an info icon to show tooltip in mouseover (keyboard shortcuts, etc).
17-
* (β€’) Show a loading spinner during loading of each image/video
17+
* (β€’) Show a loading spinner during loading of each image/video.
1818
* (?) Create a "demo" version of the app using copies of all AWS components and a demo bucket with stock images.
1919
*
2020
* β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
@@ -632,51 +632,88 @@ function useEventListener(eventName, handler, element = window) {
632632
// Touch interface horizontal swipe functionality
633633
// [Adapted from https://stackoverflow.com/questions/2264072/detect-a-finger-swipe-through-javascript-on-the-iphone-and-android]
634634
// TODO: Use React Native to do this instead?
635-
function useHorizonalSwipeNav(updateIndex, minChangeX = 60, maxChangeY = 120) {
636-
// Variables to hold the start and end coords for each axis
635+
function useHorizonalSwipeNav(updateIndex, minChangeX = 150, maxChangeY = 150, debug = false) {
636+
// Variables to hold the start and end coords for each axis, and a flag to indicate that more than one touch was registered.
637637
const [touchCoords, setTouchCoords] = useState({
638638
startX: null,
639639
startY: null,
640640
});
641+
const [touchState, setTouchState] = useState({
642+
multiTouch: false, // whether the current touch event has seen more than a single touch
643+
initialTouchTime: 0, // the timestamp of an initial `touchStart` event
644+
});
645+
const MAX_SWIPE_DURATION_MS = 300;
641646

647+
642648
// Handler callbacks for the touchstart and touchend events
643649
const touchStartHandler = useCallback(
644650
(e) => {
645-
setTouchCoords({ startX: e.changedTouches[0].screenX, startY: e.changedTouches[0].screenY })
651+
setTouchCoords({ startX: e.changedTouches[0].screenX, startY: e.changedTouches[0].screenY });
652+
653+
// Set `multiTouch` flag if more than one touch is registered at any point during an overall touch event.
654+
// This will lock out the `touchEndHandler` from responding to swipe events until the touch event ends.
655+
// Also record the timestamp of this `touchStart` event to measure the duration of the touch event later.
656+
if (e.touches.length > 1) {
657+
setTouchState({ multiTouch: true, initialTouchTime: 0 });
658+
}
659+
else {
660+
setTouchState({ multiTouch: false, initialTouchTime: Date.now() });
661+
}
662+
646663
}, []
647664
);
648665
const touchEndHandler = useCallback(
649666
(e) => {
650667
if (!touchCoords.startX || !touchCoords.startY) {
651-
console.log("[DEBUG]: Attempted to handle a swipe with 'null' touch coords.");
668+
if (debug) console.log("[DEBUG]: Attempted to handle a swipe with 'null' touch coords.");
669+
return;
670+
}
671+
672+
// If any touch points are still active, this is a multi-touch event which hasn't ended yet.
673+
if (e.touches.length > 0) {
652674
return;
653675
}
654676

677+
// If no touch points remain and `multiTouch` is `true` here, this is the end of the last touch in a multi-touch event, so skip.
678+
// If no touch points remain and `multiTouch` is `false` here, this is a single-touch event, so handle the swipe gesture.
679+
if (touchState.multiTouch) {
680+
setTouchState({ multiTouch: false, initialTouchTime: 0 }); // Reset `multiTouch` now that the touch event has ended.
681+
return;
682+
}
683+
684+
// Check that the single-touch event has finished within the allowed duration.
685+
if ((Date.now() - touchState.initialTouchTime) > MAX_SWIPE_DURATION_MS) {
686+
if (debug) console.log("[DEBUG]: Swipe gesture took too long and was ignored... | " + `${Date.now()} - ${touchState.initialTouchTime} = ${Date.now() - touchState.initialTouchTime}`);
687+
return;
688+
}
689+
690+
655691
// Get current state of touch coords (at 'touchend')
656692
let endX = e.changedTouches[0].screenX, endY = e.changedTouches[0].screenY;
657693

658694
// Check that the swipe didn't go past the vertical tolerance (maxChangeY)
659695
// HACK: Should check that the y-coords never go outside of tolerance during the swipe, not just at endpoints?
660-
if (Math.abs(endY - touchCoords.startY) > maxChangeY)
661-
console.log("[DEBUG]: Checked for swipe -> No swipe (too far on y-axis).");
696+
if (Math.abs(endY - touchCoords.startY) > maxChangeY) {
697+
if (debug) console.log("[DEBUG]: Checked for swipe -> No swipe (too far on y-axis).");
698+
}
662699

663700
// πŸ‘ˆ Swiped left? πŸ‘ˆ
664701
else if (endX < (touchCoords.startX - minChangeX)) {
665-
console.log("[DEBUG]: Checked for swipe -> Left swipe (navigating forward).");
702+
if (debug) console.log("[DEBUG]: Checked for swipe -> Left swipe (navigating forward).");
666703
// Swipe left = Move right
667704
updateIndex(1);
668705
}
669706

670707
// πŸ‘‰ Swiped right? πŸ‘‰
671708
else if (endX > (touchCoords.startX + minChangeX)) {
672-
console.log("[DEBUG]: Checked for swipe -> Right swipe (navigating backward).");
709+
if (debug) console.log("[DEBUG]: Checked for swipe -> Right swipe (navigating backward).");
673710
// Swipe right = Move left
674711
updateIndex(-1);
675712
}
676713

677714
// βœ– No swipe βœ–
678715
else {
679-
console.log("[DEBUG]: Checked for swipe -> No swipe (not far enough on x-axis).");
716+
if (debug) console.log("[DEBUG]: Checked for swipe -> No swipe (not far enough on x-axis).");
680717
}
681718

682719
// Reset starting touch coords

0 commit comments

Comments
Β (0)