Better mouse event handling (#552)

* Use learnings from touch events in Blockly and apply to the Ev3 sim. Register move and up events on the document rather than the individual element allowing users greater flexibility where they move their mouse once it's down.

* Add mouse leave events
This commit is contained in:
Sam El-Husseini 2018-05-02 13:54:06 -07:00 committed by GitHub
parent 6e1a613798
commit b69af383a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 30 deletions

View File

@ -93,9 +93,6 @@ namespace pxsim.visuals {
}, () => {
captured = false;
rect.setAttribute('cursor', '-webkit-grab');
}, () => {
captured = false;
rect.setAttribute('cursor', '-webkit-grab');
})
return this.group;

View File

@ -67,9 +67,6 @@ namespace pxsim.visuals {
}, () => {
captured = false;
dragSurface.setAttribute('cursor', '-webkit-grab');
}, () => {
captured = false;
dragSurface.setAttribute('cursor', '-webkit-grab');
})
return this.group;

View File

@ -66,9 +66,6 @@ namespace pxsim.visuals {
}, () => {
captured = false;
this.handleSliderUp();
}, () => {
captured = false;
this.handleSliderUp();
})
return this.group;

View File

@ -66,9 +66,6 @@ namespace pxsim.visuals {
}, () => {
captured = false;
dragSurface.setAttribute('cursor', '-webkit-grab');
}, () => {
captured = false;
dragSurface.setAttribute('cursor', '-webkit-grab');
})
return this.group;

View File

@ -47,8 +47,6 @@ namespace pxsim.visuals {
}
}, () => {
captured = false;
}, () => {
captured = false;
})
return this.group;

View File

@ -10,33 +10,85 @@ namespace pxsim.visuals {
export type TouchCallback = (event: MouseEvent | TouchEvent | PointerEvent) => void;
export function touchEvents(e: SVGElement | SVGElement[], move?: TouchCallback, down?: TouchCallback, up?: TouchCallback, leave?: TouchCallback) {
export function touchEvents(e: SVGElement | SVGElement[], move?: TouchCallback, down?: TouchCallback, up?: TouchCallback) {
if (Array.isArray(e)) {
e.forEach(el => bindEvents(el, move, down, up, leave));
e.forEach(el => bindEvents(el, move, down, up));
}
else {
bindEvents(e, move, down, up, leave);
bindEvents(e, move, down, up);
}
}
function bindEvents(e: SVGElement, move?: TouchCallback, down?: TouchCallback, up?: TouchCallback, leave?: TouchCallback) {
if ((window as any).PointerEvent) {
if (down) e.addEventListener("pointerdown", down);
if (up) e.addEventListener("pointerup", up);
if (leave) e.addEventListener("pointerleave", leave);
if (move) e.addEventListener("pointermove", move);
function bindEvents(e: SVGElement, move?: TouchCallback, down?: TouchCallback, up?: TouchCallback) {
const moveEvent = move ? (ev: MouseEvent) => {
move.call(this, ev);
ev.preventDefault();
ev.stopPropagation();
} : undefined;
const enterEvent = move ? (ev: MouseEvent) => {
if (ev.buttons != 1) {
// cancel all events when we re-enter without a button down
upEvent(ev);
}
else {
if (down) e.addEventListener("mousedown", down);
if (up) e.addEventListener("mouseup", up);
if (leave) e.addEventListener("mouseleave", leave);
if (move) e.addEventListener("mousemove", move);
} : undefined;
const upEvent = up ? (ev: MouseEvent) => {
up.call(this, ev);
ev.preventDefault();
ev.stopPropagation();
// Unregister document up and move events
if ((window as any).PointerEvent) {
if (moveEvent) document.removeEventListener("pointermove", moveEvent);
if (upEvent) document.removeEventListener("pointerup", upEvent);
if (upEvent) document.removeEventListener("pointercancel", upEvent);
if (moveEvent) document.removeEventListener("pointerenter", enterEvent);
} else {
if (moveEvent) document.removeEventListener("mousemove", moveEvent);
if (upEvent) document.removeEventListener("mouseup", upEvent);
if (moveEvent) document.removeEventListener("mouseenter", enterEvent);
if (pxsim.svg.isTouchEnabled()) {
if (moveEvent) document.removeEventListener("touchmove", moveEvent);
if (upEvent) document.removeEventListener("touchend", upEvent);
if (upEvent) document.removeEventListener("touchcancel", upEvent);
}
}
} : undefined;
const downEvent = down ? (ev: MouseEvent) => {
down.call(this, ev);
ev.preventDefault();
ev.stopPropagation();
// Register document up and move events
if ((window as any).PointerEvent) {
if (moveEvent) document.addEventListener("pointermove", moveEvent);
if (upEvent) document.addEventListener("pointerup", upEvent);
if (upEvent) document.addEventListener("pointercancel", upEvent);
if (moveEvent) document.addEventListener("pointerenter", enterEvent);
} else {
if (moveEvent) document.addEventListener("mousemove", moveEvent);
if (upEvent) document.addEventListener("mouseup", upEvent);
if (moveEvent) document.addEventListener("mouseenter", enterEvent);
if (pxsim.svg.isTouchEnabled()) {
if (down) e.addEventListener("touchstart", down);
if (up) e.addEventListener("touchend", up);
if (leave) e.addEventListener("touchcancel", leave);
if (move) e.addEventListener("touchmove", move);
if (moveEvent) document.addEventListener("touchmove", moveEvent);
if (upEvent) document.addEventListener("touchend", upEvent);
if (upEvent) document.addEventListener("touchcancel", upEvent);
}
}
} : undefined;
if ((window as any).PointerEvent) {
if (downEvent) e.addEventListener("pointerdown", downEvent);
}
else {
if (downEvent) e.addEventListener("mousedown", downEvent);
if (pxsim.svg.isTouchEnabled()) {
if (downEvent) e.addEventListener("touchstart", downEvent);
}
}
}