Fix web carousel horizontal swipe triggering browser back/forward
On web, a horizontal trackpad swipe over the in-feed image carousel could trigger the browser's back/forward navigation instead of scrolling the carousel. - Add overscroll-behavior-x: contain to the carousel scroller, which stops Chrome and Firefox from chaining horizontal overscroll into history nav. - Safari ignores overscroll-behavior, so add a non-passive wheel handler that intercepts horizontal-dominant wheel events, scrolls the carousel manually, and preventDefaults the history-nav gesture. Vertical-dominant events are left untouched so normal page scroll still works. APP-2287
This commit is contained in:
@@ -340,6 +340,11 @@ export function Gallery({
|
|||||||
marginLeft: -insetLeft,
|
marginLeft: -insetLeft,
|
||||||
width,
|
width,
|
||||||
},
|
},
|
||||||
|
// Prevent horizontal trackpad/wheel swipes from triggering the
|
||||||
|
// browser's back/forward overscroll-navigation gesture. Handles
|
||||||
|
// Chrome and Firefox; Safari is handled via the wheel listener in
|
||||||
|
// usePointerHandlers.web.ts since it ignores overscroll-behavior.
|
||||||
|
web({overscrollBehaviorX: 'contain'}),
|
||||||
]}
|
]}
|
||||||
contentContainerStyle={{
|
contentContainerStyle={{
|
||||||
gap: ITEM_GAP,
|
gap: ITEM_GAP,
|
||||||
|
|||||||
@@ -246,12 +246,63 @@ export function usePointerHandlers({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Safari does not support `overscroll-behavior`, so a horizontal trackpad
|
||||||
|
* swipe over the carousel can trigger the browser's back/forward
|
||||||
|
* navigation gesture. We intercept predominantly-horizontal wheel events
|
||||||
|
* and apply the scroll ourselves, calling preventDefault to suppress the
|
||||||
|
* history-nav gesture. Vertical-dominant wheel events are left untouched so
|
||||||
|
* normal page scroll still works. Chrome/Firefox are covered by the
|
||||||
|
* `overscrollBehaviorX: 'contain'` style on the FlatList.
|
||||||
|
*
|
||||||
|
* Listener must be non-passive so preventDefault is honored.
|
||||||
|
*/
|
||||||
|
const onWheel = (e: WheelEvent) => {
|
||||||
|
// Only act on predominantly-horizontal scrolls. Vertical-dominant events
|
||||||
|
// are page scroll and must not be swallowed.
|
||||||
|
if (Math.abs(e.deltaX) <= Math.abs(e.deltaY)) return
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
// Cancel any in-progress settle tween so manual scrolling feels direct.
|
||||||
|
if (stopTween) {
|
||||||
|
stopTween()
|
||||||
|
stopTween = null
|
||||||
|
}
|
||||||
|
if (overscrollX !== 0) clearOverscroll()
|
||||||
|
|
||||||
|
const maxScroll = el.scrollWidth - el.clientWidth
|
||||||
|
const next = Math.max(0, Math.min(el.scrollLeft + e.deltaX, maxScroll))
|
||||||
|
scrollTo(next)
|
||||||
|
|
||||||
|
// Keep the active index in sync so keyboard/lightbox stay correct, but
|
||||||
|
// only settle when it actually changes - onSettle moves focus, which we
|
||||||
|
// don't want to thrash on every wheel tick.
|
||||||
|
let accumulated = 0
|
||||||
|
let index = 0
|
||||||
|
for (let i = 0; i < imageCount; i++) {
|
||||||
|
const w = (itemWidthsRef.current.get(i) ?? 0) + ITEM_GAP
|
||||||
|
if (next < accumulated + w / 2) {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
accumulated += w
|
||||||
|
if (i === imageCount - 1) index = i
|
||||||
|
}
|
||||||
|
if (index !== localIndex) {
|
||||||
|
localIndex = index
|
||||||
|
onSettle(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
el.addEventListener('mousedown', onMouseDown)
|
el.addEventListener('mousedown', onMouseDown)
|
||||||
|
el.addEventListener('wheel', onWheel, {passive: false})
|
||||||
window.addEventListener('mousemove', onMouseMove)
|
window.addEventListener('mousemove', onMouseMove)
|
||||||
window.addEventListener('mouseup', onMouseUp)
|
window.addEventListener('mouseup', onMouseUp)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
el.removeEventListener('mousedown', onMouseDown)
|
el.removeEventListener('mousedown', onMouseDown)
|
||||||
|
el.removeEventListener('wheel', onWheel)
|
||||||
window.removeEventListener('mousemove', onMouseMove)
|
window.removeEventListener('mousemove', onMouseMove)
|
||||||
window.removeEventListener('mouseup', onMouseUp)
|
window.removeEventListener('mouseup', onMouseUp)
|
||||||
if (stopTween) stopTween()
|
if (stopTween) stopTween()
|
||||||
|
|||||||
Reference in New Issue
Block a user