Skip to content

Instantly share code, notes, and snippets.

@Jimbly
Last active July 11, 2019 21:41
Show Gist options
  • Save Jimbly/07ea52f8380f7de3a592567d50dde47c to your computer and use it in GitHub Desktop.
Save Jimbly/07ea52f8380f7de3a592567d50dde47c to your computer and use it in GitHub Desktop.
Deal with pointerlockerror on Safari
<!DOCTYPE html>
<!-- Modified version of pointer lock code to lock on click from the main loop, works on Safari -->
<html lang="en"><head>
<title>Pointer Lock Test</title>
</head>
<body onload="startup()">
<div id="status-element" style="background-color: #FEE; white-space: pre-wrap;">dragx=0, dragy=0
double clicks: 0</div>
<script>
/* eslint no-bitwise:off, no-unused-expressions:off, vars-on-top:off, no-implicit-coercion:off */
// The element we'll make pointer locked.
var dragx=0;
var dragy=0;
var doubleclicks=0;
var down_mask = 0;
var pointerLock;
// An immediate-mode, frame-based engine, like GLOV.js
var engine = { global_frame_index: 0 };
function updateStatus() {
document.getElementById('status-element').textContent = 'dragx=' + dragx + ', dragy=' + dragy +
'\ndouble clicks: ' + doubleclicks + ' down=' + down_mask;
}
function assert(exp) {
if (!exp) {
throw new Error('Assertion failed');
}
}
function pointerLockCreate(elem) {
var deferred_lock_id = 0;
var want_lock_async = false;
var trying_direct_lock = 0;
var direct_lock_works = false; // unknown at start
var direct_lock_disabled = false;
var async_pointer_lock_start_frame = 0;
function isLocked() {
return !!document.pointerLockElement;
}
function exitLock() {
document.exitPointerLock();
}
function onPointerLockChange() {
if (document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement) {
console.log('Pointer Lock was successful.');
if (trying_direct_lock) {
console.log('Direct lock works!');
direct_lock_works = true;
trying_direct_lock = 0;
}
} else {
console.log('Pointer Lock was lost.');
}
}
function onPointerLockError() {
console.log('Error while locking pointer.');
if (trying_direct_lock) {
direct_lock_disabled = true;
want_lock_async = true;
trying_direct_lock = 0;
}
}
function asyncPointerLockCheck() {
deferred_lock_id = 0;
// If we asked for a lock, and direct locks are disabled, lock it
if (want_lock_async) {
want_lock_async = false;
console.log('Async pointer lock watcher executing lock');
elem.requestPointerLock();
return;
}
// If we successfully locked through any means, stop
if (direct_lock_works) {
console.log('Async pointer lock watcher canceled, direct lock worked');
return;
}
if (trying_direct_lock) {
// If we tried a direct lock 2+ frames ago, lock it, disable direct locks
if (engine.global_frame_index - trying_direct_lock >= 1) {
console.log('2 frames since attempting a direct pointer lock, assuming failed');
direct_lock_disabled = true;
trying_direct_lock = 0;
elem.requestPointerLock();
return;
}
} else if (engine.global_frame_index !== async_pointer_lock_start_frame) {
// tick has happened, no request for lock, stop watching
console.log('Async pointer lock watcher done, no lock request');
return;
}
deferred_lock_id = setTimeout(asyncPointerLockCheck, 1);
}
function enterLock() {
// Assert that we got a mouse down event recently, otherwise this won't work
assert(engine.global_frame_index - async_pointer_lock_start_frame <= 2);
// If direct locks are disabled, just ask for async lock
if (direct_lock_disabled) {
console.log('Requesting async pointer lock');
want_lock_async = true;
return;
}
console.log('Trying direct pointer lock');
if (!direct_lock_works) { // if we don't know yet
trying_direct_lock = engine.global_frame_index;
}
elem.requestPointerLock();
}
function allowAsync() {
async_pointer_lock_start_frame = engine.global_frame_index;
// If we know direct locks work, do nothing, otherwise start watcher
if (!direct_lock_works) {
console.log('Starting async pointer lock watcher');
if (deferred_lock_id) {
clearTimeout(deferred_lock_id);
}
deferred_lock_id = setTimeout(asyncPointerLockCheck, 1);
} else {
console.log('Direct lock works, not starting async watcher');
}
}
elem.requestPointerLock = elem.requestPointerLock || elem.mozRequestPointerLock ||
elem.webkitRequestPointerLock || function () { /* nop */ };
document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock ||
document.webkitExitPointerLock || function () { /* nop */ };
document.addEventListener('pointerlockchange', onPointerLockChange, false);
document.addEventListener('mozpointerlockchange', onPointerLockChange, false);
document.addEventListener('webkitpointerlockchange', onPointerLockChange, false);
document.addEventListener('pointerlockerror', onPointerLockError, false);
document.addEventListener('mozpointerlockerror', onPointerLockError, false);
document.addEventListener('webkitpointerlockerror', onPointerLockError, false);
return {
isLocked: isLocked,
allowAsync: allowAsync,
enter: enterLock,
exit: exitLock,
};
}
document.addEventListener('mousemove', function (e) {
var movementX = e.movementX ||
e.mozMovementX ||
e.webkitMovementX ||
0;
var movementY = e.movementY ||
e.mozMovementY ||
e.webkitMovementY ||
0;
// Print the mouse movement delta values
if (pointerLock.isLocked()) {
dragx += movementX;
dragy += movementY;
updateStatus();
//console.log('movementX=' + movementX, 'movementY=' + movementY);
}
}, false);
var had_click = false;
function ondown(event) {
had_click = event.which;
down_mask |= (1 << event.which);
updateStatus();
pointerLock.allowAsync(); // Put this in any event handler that may indirectly cause a lock
}
function onup(event) {
down_mask &= ~(1 << event.which);
// if (!down_mask) {
// exitLock();
// }
updateStatus();
pointerLock.allowAsync(); // Put this in any event handler that may indirectly cause a lock
}
document.addEventListener('mousedown', ondown, false);
document.addEventListener('mouseup', onup, false);
document.addEventListener('dblclick', function () {
++doubleclicks;
updateStatus();
}, false);
function tick() {
++engine.global_frame_index;
requestAnimationFrame(tick);
if (had_click === 1 && !pointerLock.isLocked()) {
pointerLock.enter();
} else if (had_click === 3) {
pointerLock.exit();
}
had_click = 0;
}
function startup() {
pointerLock = pointerLockCreate(document.getElementById('status-element'));
requestAnimationFrame(tick);
}
</script>
</body></html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment