@@ -81,6 +81,11 @@ type NumericScrubState = {
81
81
cursor ?: SVGElement ;
82
82
direction : NumericScrubDirection ;
83
83
status : "idle" | "scrubbing" ;
84
+ /**
85
+ * On windows it's possible that requestPointerLock is already called,
86
+ * but document.pointerLockElement is not updated yet.
87
+ */
88
+ pointerCaptureRequested : boolean ;
84
89
} ;
85
90
86
91
const getValueDefault = (
@@ -168,6 +173,7 @@ export const numericScrubControl = (
168
173
cursor : undefined ,
169
174
direction,
170
175
status : "idle" ,
176
+ pointerCaptureRequested : false ,
171
177
} ;
172
178
173
179
// The appearance of the custom cursor is delayed, so we need to track the mouse position
@@ -208,9 +214,7 @@ export const numericScrubControl = (
208
214
if ( ! ( event instanceof PointerEvent ) ) {
209
215
return ;
210
216
}
211
-
212
- const { type, movementY, movementX } = event ;
213
- const movement = direction === "horizontal" ? movementX : - movementY ;
217
+ const { type } = event ;
214
218
215
219
switch ( type ) {
216
220
case "pointerup" : {
@@ -296,6 +300,11 @@ export const numericScrubControl = (
296
300
break ;
297
301
}
298
302
case "pointermove" : {
303
+ const { movementY, movementX } = event ;
304
+
305
+ const movement = direction === "horizontal" ? movementX : - movementY ;
306
+
307
+ // console.log("movement", movement, event);
299
308
mouseState . x = event . clientX ;
300
309
mouseState . y = event . clientY ;
301
310
@@ -328,12 +337,12 @@ export const numericScrubControl = (
328
337
// When cursor moves out of the browser window
329
338
// we want it to come back from the other side
330
339
const top = wrapAround (
331
- Number . parseFloat ( state . cursor . style . top ) + event . movementY ,
340
+ Number . parseFloat ( state . cursor . style . top ) + movementY ,
332
341
0 ,
333
342
globalThis . innerHeight
334
343
) ;
335
344
const left = wrapAround (
336
- Number . parseFloat ( state . cursor . style . left ) + event . movementX ,
345
+ Number . parseFloat ( state . cursor . style . left ) + movementX ,
337
346
0 ,
338
347
globalThis . innerWidth
339
348
) ;
@@ -345,11 +354,17 @@ export const numericScrubControl = (
345
354
}
346
355
break ;
347
356
}
357
+
348
358
case "pointercancel" : {
349
359
cleanup ( ) ;
350
360
break ;
351
361
}
362
+
352
363
case "lostpointercapture" : {
364
+ if ( state . pointerCaptureRequested ) {
365
+ break ;
366
+ }
367
+
353
368
if ( document . pointerLockElement === null ) {
354
369
cleanup ( ) ;
355
370
}
@@ -389,6 +404,8 @@ export const numericScrubControl = (
389
404
390
405
const requestPointerLockSafe = async ( targetNode : HTMLElement | SVGElement ) => {
391
406
try {
407
+ // await targetNode.requestPointerLock();
408
+
392
409
return await targetNode . requestPointerLock ( {
393
410
unadjustedMovement : true ,
394
411
} ) ;
@@ -417,18 +434,27 @@ const requestPointerLock = (
417
434
disposeOnCleanup ( ( ) => {
418
435
targetNode . setPointerCapture ( pointerId ) ;
419
436
return ( ) => {
420
- targetNode . releasePointerCapture ( pointerId ) ;
437
+ if ( targetNode . hasPointerCapture ( pointerId ) ) {
438
+ targetNode . releasePointerCapture ( pointerId ) ;
439
+ }
421
440
} ;
422
441
} ) ;
423
442
424
443
const isSafari = / ^ ( (? ! c h r o m e | a n d r o i d ) .) * s a f a r i / i. test ( navigator . userAgent ) ;
444
+
425
445
// Safari supports pointer lock well, but the issue lies with the pointer lock banner.
426
446
// It shifts the entire page down, which creates a poor user experience.
427
447
if ( ! isSafari ) {
428
448
disposeOnCleanup ( ( ) => {
429
449
const timerId = window . setTimeout ( ( ) => {
450
+ state . pointerCaptureRequested = true ;
430
451
requestPointerLockSafe ( targetNode )
431
452
. then ( ( ) => {
453
+ if ( targetNode . hasPointerCapture ( pointerId ) ) {
454
+ targetNode . releasePointerCapture ( pointerId ) ;
455
+ }
456
+
457
+ state . pointerCaptureRequested = false ;
432
458
const cursorNode =
433
459
( targetNode . ownerDocument . querySelector (
434
460
"#numeric-guesture-control-cursor"
@@ -466,17 +492,23 @@ const requestPointerLock = (
466
492
}
467
493
} )
468
494
. catch ( ( error ) => {
495
+ state . pointerCaptureRequested = false ;
469
496
console . error ( "requestPointerLock" , error ) ;
470
497
} ) ;
471
498
} , scrubTimeout ) ;
472
499
473
500
return ( ) => {
501
+ state . pointerCaptureRequested = false ;
502
+
474
503
if ( state . cursor ) {
475
504
state . cursor . remove ( ) ;
476
505
state . cursor = undefined ;
477
506
}
478
507
479
- targetNode . ownerDocument . exitPointerLock ( ) ;
508
+ if ( targetNode . ownerDocument . pointerLockElement === targetNode ) {
509
+ targetNode . ownerDocument . exitPointerLock ( ) ;
510
+ }
511
+
480
512
clearTimeout ( timerId ) ;
481
513
} ;
482
514
} ) ;
0 commit comments