mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
fix(street-level): hover skips every drag and re-picks when the camera rests
A release with a modifier held left the LEFT_DOWN/LEFT_UP flag stuck, and right, middle or modified drags picked every frame. Track pointer buttons instead, as main did, and pick a still pointer again on moveEnd. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
c18af6ad1c
commit
db9b61dc55
2 files changed
+88
-27
No files matched your search
@@ -51,10 +51,13 @@ export function createSelection({ state, parts }) {
|
||||
parts.sequences.clearSelection();
|
||||
}
|
||||
|
||||
/** Where the pointer is, until a frame picks it; a drag picks nothing. */
|
||||
let hoverPosition = null;
|
||||
/** The pointer over the globe, and whether a frame is queued to pick it. */
|
||||
let pointer = null;
|
||||
let hoverQueued = false;
|
||||
let hoverCursor = false;
|
||||
let dragging = false;
|
||||
/** Buttons held: any of them, with any modifier, is a camera drag. */
|
||||
let pointerButtons = 0;
|
||||
let removeHoverWatchers = null;
|
||||
|
||||
function setHoverCursor(canvas, hit) {
|
||||
if (hit === hoverCursor) return;
|
||||
@@ -63,14 +66,14 @@ export function createSelection({ state, parts }) {
|
||||
}
|
||||
|
||||
function pickHover() {
|
||||
const position = hoverPosition;
|
||||
hoverPosition = null;
|
||||
hoverQueued = false;
|
||||
const canvas = state.viewer?.scene?.canvas;
|
||||
// Switched off (or torn down) since the frame was queued.
|
||||
if (!canvas || !state.enabled || !state.clickHandler || dragging) return;
|
||||
// Switched off (or torn down) since the frame was queued, or dragging.
|
||||
if (!canvas || !state.enabled || !state.clickHandler || pointerButtons)
|
||||
return;
|
||||
let hit = false;
|
||||
try {
|
||||
const picked = state.viewer.scene.pick(position);
|
||||
const picked = state.viewer.scene.pick(pointer);
|
||||
hit = ownsPick(
|
||||
picking?.resolvePickId ? picking.resolvePickId(picked) : picked?.id,
|
||||
);
|
||||
@@ -80,11 +83,33 @@ export function createSelection({ state, parts }) {
|
||||
setHoverCursor(canvas, hit);
|
||||
}
|
||||
|
||||
/** Pointer cursor over anything this layer owns, picked at most once a frame. */
|
||||
/** Pick the pointer on the next frame; at most one pick a frame. */
|
||||
function queueHoverPick() {
|
||||
if (hoverQueued || !pointer || pointerButtons) return;
|
||||
hoverQueued = true;
|
||||
requestAnimationFrame(pickHover);
|
||||
}
|
||||
|
||||
/** Pointer cursor over anything this layer owns. */
|
||||
function onMove(movement) {
|
||||
if (!state.enabled || dragging) return;
|
||||
if (!hoverPosition) requestAnimationFrame(pickHover);
|
||||
hoverPosition = Cesium.Cartesian2.clone(movement.endPosition);
|
||||
if (!state.enabled) return;
|
||||
pointer = Cesium.Cartesian2.clone(movement.endPosition, pointer);
|
||||
queueHoverPick();
|
||||
}
|
||||
|
||||
/** Track held buttons, and pick again once the camera rests under a still pointer. */
|
||||
function watchHover(viewer) {
|
||||
const canvas = viewer.scene.canvas;
|
||||
const onButtons = (event) => {
|
||||
pointerButtons = event.buttons ?? 0;
|
||||
};
|
||||
const types = ['pointerdown', 'pointermove', 'pointerup'];
|
||||
for (const type of types) canvas.addEventListener?.(type, onButtons);
|
||||
const removeEnd = viewer.camera?.moveEnd?.addEventListener(queueHoverPick);
|
||||
removeHoverWatchers = () => {
|
||||
for (const type of types) canvas.removeEventListener?.(type, onButtons);
|
||||
removeEnd?.();
|
||||
};
|
||||
}
|
||||
|
||||
function install(viewer) {
|
||||
@@ -100,12 +125,7 @@ export function createSelection({ state, parts }) {
|
||||
onMove,
|
||||
Cesium.ScreenSpaceEventType.MOUSE_MOVE,
|
||||
);
|
||||
state.clickHandler.setInputAction(() => {
|
||||
dragging = true;
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
|
||||
state.clickHandler.setInputAction(() => {
|
||||
dragging = false;
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_UP);
|
||||
watchHover(viewer);
|
||||
document.addEventListener('keydown', onKeyDown);
|
||||
picking?.registerPickOwner?.(STREET_LEVEL_LAYER_ID, ownsPick);
|
||||
}
|
||||
@@ -113,8 +133,10 @@ export function createSelection({ state, parts }) {
|
||||
function uninstall() {
|
||||
// Nothing to undo for a layer that was never enabled.
|
||||
if (!state.clickHandler) return;
|
||||
hoverPosition = null;
|
||||
dragging = false;
|
||||
removeHoverWatchers?.();
|
||||
removeHoverWatchers = null;
|
||||
pointer = null;
|
||||
pointerButtons = 0;
|
||||
if (hoverCursor && state.viewer?.scene?.canvas) {
|
||||
state.viewer.scene.canvas.style.cursor = '';
|
||||
hoverCursor = false;
|
||||
|
||||
@@ -41,7 +41,9 @@ function harness({ selected = false } = {}) {
|
||||
disableRootEvents: true,
|
||||
onwheel: null,
|
||||
});
|
||||
const camera = { moveEnd: new Cesium.Event() };
|
||||
const viewer = {
|
||||
camera,
|
||||
scene: {
|
||||
canvas,
|
||||
pick(position) {
|
||||
@@ -97,8 +99,13 @@ function harness({ selected = false } = {}) {
|
||||
canvas,
|
||||
picks,
|
||||
move: (x) => onMove({ endPosition: { x, y: 10 } }),
|
||||
down: () => action('LEFT_DOWN')(),
|
||||
up: () => action('LEFT_UP')(),
|
||||
camera,
|
||||
/** A pointer event on the canvas with `buttons` held. */
|
||||
buttons(type, buttons) {
|
||||
const event = new Event(type);
|
||||
Object.defineProperty(event, 'buttons', { value: buttons });
|
||||
canvas.dispatchEvent(event);
|
||||
},
|
||||
frame() {
|
||||
for (const task of frames.splice(0)) task();
|
||||
},
|
||||
@@ -132,14 +139,46 @@ test('hover picks once a frame, at the latest position, and shows a pointer over
|
||||
}
|
||||
});
|
||||
|
||||
test('a drag, or a layer gone off before the frame, picks nothing', () => {
|
||||
test('a drag with any button picks nothing, and a release with a modifier held does not stick', () => {
|
||||
const h = harness();
|
||||
try {
|
||||
h.down();
|
||||
h.move(1);
|
||||
for (const held of [1, 2, 4]) {
|
||||
h.buttons('pointerdown', held);
|
||||
h.move(held);
|
||||
h.frame();
|
||||
}
|
||||
assert.equal(h.picks.length, 0, 'left, right and middle drags');
|
||||
// Released with Shift held: Cesium's plain LEFT_UP never fires.
|
||||
h.buttons('pointerup', 0);
|
||||
h.move(5);
|
||||
h.frame();
|
||||
assert.equal(h.picks.length, 0, 'a drag in progress');
|
||||
h.up();
|
||||
assert.equal(h.picks.length, 1, 'hover picks again');
|
||||
} finally {
|
||||
h.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test('when the camera rests, a still pointer is picked again so the cursor cannot stick', () => {
|
||||
const h = harness();
|
||||
try {
|
||||
h.scene.under = 'mly:seq:1';
|
||||
h.move(5);
|
||||
h.frame();
|
||||
assert.equal(h.canvas.style.cursor, 'pointer');
|
||||
// The camera moves the line out from under a still pointer.
|
||||
h.scene.under = null;
|
||||
h.camera.moveEnd.raiseEvent();
|
||||
h.frame();
|
||||
assert.equal(h.picks.length, 2);
|
||||
assert.equal(h.canvas.style.cursor, '');
|
||||
} finally {
|
||||
h.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test('a hover frame queued before the layer went off does not pick', () => {
|
||||
const h = harness();
|
||||
try {
|
||||
h.move(2);
|
||||
h.state.enabled = false;
|
||||
h.frame();
|
||||
|
||||
Reference in new issue
Block a user