Skip to content

Commit e82f1c2

Browse files
authoredMay 16, 2022
Fix prevent scroll (#495)
* type: make window mandatory * feat: setEventIds now returns the relevant id set * docs: fix typo * chore: track the gestureKey in EventStore so that proper eventOptions config is passed * fix: fix drag detection * ci
1 parent 1a85765 commit e82f1c2

File tree

5 files changed

+47
-15
lines changed

5 files changed

+47
-15
lines changed
 

‎.changeset/swift-colts-return.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@use-gesture/core': patch
3+
---
4+
5+
fix: improve detection for drag gesture, also fixes #494

‎packages/core/src/Controller.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ export class Controller {
4444
setEventIds(event: TouchEvent | PointerEvent) {
4545
if (isTouch(event)) {
4646
this.touchIds = new Set(touchIds(event as TouchEvent))
47+
return this.touchIds
4748
} else if ('pointerId' in event) {
4849
if (event.type === 'pointerup' || event.type === 'pointercancel') this.pointerIds.delete(event.pointerId)
4950
else if (event.type === 'pointerdown') this.pointerIds.add(event.pointerId)
51+
return this.pointerIds
5052
}
5153
}
5254
/**
@@ -78,7 +80,7 @@ export class Controller {
7880
}
7981
}
8082
/**
81-
* Executes side effects (attaching listeneds to a `config.target`). Ran on
83+
* Executes side effects (attaching listeners to a `config.target`). Ran on
8284
* each render.
8385
*/
8486
effect() {
@@ -147,7 +149,7 @@ export class Controller {
147149

148150
function setupGesture(ctrl: Controller, gestureKey: GestureKey) {
149151
ctrl.gestures.add(gestureKey)
150-
ctrl.gestureEventStores[gestureKey] = new EventStore(ctrl)
152+
ctrl.gestureEventStores[gestureKey] = new EventStore(ctrl, gestureKey)
151153
ctrl.gestureTimeoutStores[gestureKey] = new TimeoutStore()
152154
}
153155

‎packages/core/src/EventStore.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import type { Controller } from './Controller'
2+
import { GestureKey } from './types'
23
import { toDomEventType } from './utils/events'
34

45
export class EventStore {
5-
private _listeners: (() => void)[] = []
6+
private _listeners = new Set<() => void>()
67
private _ctrl: Controller
7-
constructor(ctrl: Controller) {
8+
private _gestureKey?: GestureKey
9+
constructor(ctrl: Controller, gestureKey?: GestureKey) {
810
this._ctrl = ctrl
11+
this._gestureKey = gestureKey
912
}
1013

1114
add(
@@ -15,16 +18,21 @@ export class EventStore {
1518
handler: (event: any) => void,
1619
options?: AddEventListenerOptions
1720
) {
21+
const listeners = this._listeners
1822
const type = toDomEventType(device, action)
19-
const eventOptions = { ...this._ctrl.config.shared.eventOptions, ...options }
23+
const _options = this._gestureKey ? this._ctrl.config[this._gestureKey]!.eventOptions : {}
24+
const eventOptions = { ..._options, ...options }
2025
element.addEventListener(type, handler, eventOptions)
21-
this._listeners.push(() => {
26+
const remove = () => {
2227
element.removeEventListener(type, handler, eventOptions)
23-
})
28+
listeners.delete(remove)
29+
}
30+
listeners.add(remove)
31+
return remove
2432
}
2533

2634
clean() {
2735
this._listeners.forEach((remove) => remove())
28-
this._listeners = []
36+
this._listeners.clear() // just for safety
2937
}
3038
}

‎packages/core/src/engines/DragEngine.ts

+23-6
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,27 @@ export class DragEngine extends CoordinatesEngine<'drag'> {
8888
)
8989
return
9090

91-
this.ctrl.setEventIds(event)
91+
const ctrlIds = this.ctrl.setEventIds(event)
9292
// We need to capture all pointer ids so that we can keep track of them when
9393
// they're released off the target
9494
if (config.pointerCapture) {
9595
;(event.target as HTMLElement).setPointerCapture(event.pointerId)
9696
}
9797

98-
if (state._pointerActive) return
98+
if (
99+
// in some situations (https://github.com/pmndrs/use-gesture/issues/494#issuecomment-1127584116)
100+
// like when a new browser tab is opened during a drag gesture, the drag
101+
// can be interrupted mid-way, and can stall. This happens because the
102+
// pointerId that initiated the gesture is lost, and since the drag
103+
// persists until that pointerId is lifted with pointerup, it never ends.
104+
//
105+
// Therefore, when we detect that only one pointer is pressing the screen,
106+
// we consider that the gesture can proceed.
107+
ctrlIds &&
108+
ctrlIds.size > 1 &&
109+
state._pointerActive
110+
)
111+
return
99112

100113
this.start(event)
101114
this.setupPointer(event)
@@ -271,9 +284,9 @@ export class DragEngine extends CoordinatesEngine<'drag'> {
271284
}
272285

273286
if (!config.pointerCapture) {
274-
this.eventStore.add(this.sharedConfig.window!, device, 'change', this.pointerMove.bind(this))
275-
this.eventStore.add(this.sharedConfig.window!, device, 'end', this.pointerUp.bind(this))
276-
this.eventStore.add(this.sharedConfig.window!, device, 'cancel', this.pointerUp.bind(this))
287+
this.eventStore.add(this.sharedConfig.window, device, 'change', this.pointerMove.bind(this))
288+
this.eventStore.add(this.sharedConfig.window, device, 'end', this.pointerUp.bind(this))
289+
this.eventStore.add(this.sharedConfig.window, device, 'cancel', this.pointerUp.bind(this))
277290
}
278291
}
279292

@@ -292,7 +305,11 @@ export class DragEngine extends CoordinatesEngine<'drag'> {
292305
setupScrollPrevention(event: PointerEvent) {
293306
persistEvent(event)
294307
// we add window listeners that will prevent the scroll when the user has started dragging
295-
this.eventStore.add(this.sharedConfig.window!, 'touch', 'change', this.preventScroll.bind(this), { passive: false })
308+
const remove = this.eventStore.add(this.sharedConfig.window, 'touch', 'change', this.preventScroll.bind(this), {
309+
passive: false
310+
})
311+
this.eventStore.add(this.sharedConfig.window, 'touch', 'end', remove)
312+
this.eventStore.add(this.sharedConfig.window, 'touch', 'cancel', remove)
296313
this.timeoutStore.add('startPointerDrag', this.startPointerDrag.bind(this), this.config.preventScrollDelay!, event)
297314
}
298315

‎packages/core/src/types/internalConfig.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { PointerType, Vector2 } from './utils'
55
export type InternalGenericOptions = {
66
target?: () => EventTarget
77
eventOptions: AddEventListenerOptions
8-
window?: EventTarget
8+
window: EventTarget
99
enabled: boolean
1010
transform?: (v: Vector2) => Vector2
1111
}

0 commit comments

Comments
 (0)
Please sign in to comment.