fix event listener registration & unregistration

This commit is contained in:
Grant 2024-03-08 15:38:11 -07:00
parent b07ae0406f
commit f52f7b406b
1 changed files with 248 additions and 145 deletions

View File

@ -261,57 +261,105 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
} }
registerTouchEvents() { registerTouchEvents() {
console.debug("[PanZoom] Registering touch events to $wrapper");
this.$wrapper.addEventListener( this.$wrapper.addEventListener(
"touchstart", "touchstart",
(event) => { this._touch_touchstart.bind(this),
const isDoubleTap = {
this.touch.lastTouch && +new Date() - this.touch.lastTouch < 200; passive: false,
}
if (isDoubleTap && event.touches.length === 1) {
this.emit("doubleTap", event);
} else {
this.touch.lastTouch = +new Date();
const { touches } = event;
const isPanningAction = touches.length === 1;
const isPinchAction = touches.length === 2;
if (isPanningAction) {
this.panning.start(touches[0].clientX, touches[0].clientY);
}
if (isPinchAction) {
this.onPinchStart(event);
}
}
},
{ passive: false }
); );
this.$wrapper.addEventListener("touchmove", (event) => { this.$wrapper.addEventListener(
if (this.panning.enabled && event.touches.length === 1) { "touchmove",
event.preventDefault(); this._touch_touchmove.bind(this)
event.stopPropagation(); );
const touch = event.touches[0]; this.$wrapper.addEventListener("touchend", this._touch_touchend.bind(this));
this.panning.move(touch.clientX, touch.clientY);
} else if (event.touches.length > 1) {
this.onPinch(event);
}
});
this.$wrapper.addEventListener("touchend", (event) => {
if (this.panning.enabled) {
this.panning.enabled = false;
const touch = event.changedTouches[0];
this.panning.end(touch.clientX, touch.clientY);
}
});
} }
unregisterTouchEvents() {
console.debug("[PanZoom] Unregistering touch events to $wrapper");
this.$wrapper.removeEventListener(
"touchstart",
this._touch_touchstart.bind(this)
);
this.$wrapper.removeEventListener(
"touchmove",
this._touch_touchmove.bind(this)
);
this.$wrapper.removeEventListener(
"touchend",
this._touch_touchend.bind(this)
);
}
/**
* Handle touchstart event from touch registrations
* This needs to be a variable to correctly pass this context
*
* @param e
*/
private _touch_touchstart = (event: TouchEvent) => {
const isDoubleTap =
this.touch.lastTouch && +new Date() - this.touch.lastTouch < 200;
if (isDoubleTap && event.touches.length === 1) {
this.emit("doubleTap", event);
} else {
this.touch.lastTouch = +new Date();
const { touches } = event;
const isPanningAction = touches.length === 1;
const isPinchAction = touches.length === 2;
if (isPanningAction) {
this.panning.start(touches[0].clientX, touches[0].clientY);
}
if (isPinchAction) {
this.onPinchStart(event);
}
}
};
/**
* Handle touchmove event from touch registrations
* This needs to be a variable to correctly pass this context
*
* @param e
*/
private _touch_touchmove = (event: TouchEvent) => {
if (this.panning.enabled && event.touches.length === 1) {
event.preventDefault();
event.stopPropagation();
const touch = event.touches[0];
this.panning.move(touch.clientX, touch.clientY);
} else if (event.touches.length > 1) {
this.onPinch(event);
}
};
/**
* Handle touchend event from touch registrations
* This needs to be a variable to correctly pass this context
*
* @param e
*/
private _touch_touchend = (event: TouchEvent) => {
if (this.panning.enabled) {
this.panning.enabled = false;
const touch = event.changedTouches[0];
this.panning.end(touch.clientX, touch.clientY);
}
};
/// ///// /// /////
// pinch // pinch
/// ///// /// /////
@ -396,115 +444,167 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
} }
registerMouseEvents() { registerMouseEvents() {
console.debug("[PanZoom] Registering mouse events to $wrapper & document");
// zoom // zoom
this.$wrapper.addEventListener( this.$wrapper.addEventListener("wheel", this._mouse_wheel, {
"wheel", passive: true,
(e) => { });
// if (!self.allowDrag) return;
const oldScale = this.transform.scale;
let delta = -e.deltaY; this.$wrapper.addEventListener("mousedown", this._mouse_mousedown, {
passive: false,
switch (e.deltaMode) { });
case WheelEvent.DOM_DELTA_PIXEL:
// 53 pixels is the default chrome gives for a wheel scroll.
delta /= 53;
break;
case WheelEvent.DOM_DELTA_LINE:
// default case on Firefox, three lines is default number.
delta /= 3;
break;
case WheelEvent.DOM_DELTA_PAGE:
delta = Math.sign(delta);
break;
}
// TODO: move this to settings
this.nudgeScale(delta / 2);
const scale = this.transform.scale;
if (oldScale !== scale) {
const dx = e.clientX - this.$wrapper.clientWidth / 2;
const dy = e.clientY - this.$wrapper.clientHeight / 2;
this.transform.x -= dx / oldScale;
this.transform.x += dx / scale;
this.transform.y -= dy / oldScale;
this.transform.y += dy / scale;
this.update();
// place.update();
}
},
{ passive: true }
);
this.$wrapper.addEventListener(
"mousedown",
(e) => {
e.preventDefault();
e.stopPropagation();
this.mouse.mouseDown = Date.now();
this.panning.start(e.clientX, e.clientY);
},
{ passive: false }
);
// mouse move should not be tied to the element, in case the mouse exits the window // mouse move should not be tied to the element, in case the mouse exits the window
document.addEventListener( document.addEventListener("mousemove", this._mouse_mousemove, {
"mousemove", passive: false,
(e) => { });
if (this.panning.enabled) {
e.preventDefault();
e.stopPropagation();
this.panning.move(e.clientX, e.clientY);
} else {
// not panning
this.emit("hover", {
clientX: e.clientX,
clientY: e.clientY,
});
}
},
{ passive: false }
);
// mouse up should not be tied to the element, in case the mouse releases outside of the window // mouse up should not be tied to the element, in case the mouse releases outside of the window
document.addEventListener( document.addEventListener("mouseup", this._mouse_mouseup, {
"mouseup", passive: false,
(e) => { });
if (this.mouse.mouseDown && Date.now() - this.mouse.mouseDown <= 500) {
// if the mouse was down for less than a half a second, it's a click
// this can't depend on this.panning.enabled because that'll always be true when mouse is down
const delta = [
Math.abs(this.panning.x - e.clientX),
Math.abs(this.panning.y - e.clientY),
];
if (delta[0] < 5 && delta[1] < 5) {
// difference from the start position to the up position is very very slow,
// so it's most likely intended to be a click
this.emit("click", {
clientX: e.clientX,
clientY: e.clientY,
});
}
}
if (this.panning.enabled) {
// currently panning
e.preventDefault();
e.stopPropagation();
this.panning.end(e.clientX, e.clientY);
}
},
{ passive: false }
);
} }
unregisterMouseEvents() {
console.debug(
"[PanZoom] Unregistering mouse events to $wrapper & document"
);
this.$wrapper.removeEventListener("wheel", this._mouse_wheel);
this.$wrapper.removeEventListener("mousedown", this._mouse_mousedown);
document.removeEventListener("mousemove", this._mouse_mousemove);
document.removeEventListener("mouseup", this._mouse_mouseup);
}
/**
* Handle the wheel event from the mouse event registration
* This needs to be a variable to correctly pass this context
*
* @param e
*/
private _mouse_wheel = (e: WheelEvent) => {
// if (!self.allowDrag) return;
const oldScale = this.transform.scale;
let delta = -e.deltaY;
switch (e.deltaMode) {
case WheelEvent.DOM_DELTA_PIXEL:
// 53 pixels is the default chrome gives for a wheel scroll.
delta /= 53;
break;
case WheelEvent.DOM_DELTA_LINE:
// default case on Firefox, three lines is default number.
delta /= 3;
break;
case WheelEvent.DOM_DELTA_PAGE:
delta = Math.sign(delta);
break;
}
// TODO: move this to settings
this.nudgeScale(delta / 2);
const scale = this.transform.scale;
if (oldScale !== scale) {
const dx = e.clientX - this.$wrapper.clientWidth / 2;
const dy = e.clientY - this.$wrapper.clientHeight / 2;
this.transform.x -= dx / oldScale;
this.transform.x += dx / scale;
this.transform.y -= dy / oldScale;
this.transform.y += dy / scale;
this.update();
// place.update();
}
};
/**
* Handle mousedown event from mouse registrations
* This needs to be a variable to correctly pass this context
*
* @param e
*/
private _mouse_mousedown = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
this.mouse.mouseDown = Date.now();
this.panning.start(e.clientX, e.clientY);
};
/**
* Handle mousemove event from mouse registrations
* This needs to be a variable to correctly pass this context
*
* @param e
*/
private _mouse_mousemove = (e: MouseEvent) => {
if (this.panning.enabled) {
e.preventDefault();
e.stopPropagation();
this.panning.move(e.clientX, e.clientY);
} else {
// not panning
this.emit("hover", {
clientX: e.clientX,
clientY: e.clientY,
});
}
if (this.panning.enabled) {
e.preventDefault();
e.stopPropagation();
this.panning.move(e.clientX, e.clientY);
} else {
// not panning
this.emit("hover", {
clientX: e.clientX,
clientY: e.clientY,
});
}
};
/**
* Handle mouseup event from mouse registrations
* This needs to be a variable to correctly pass this context
*
* @param e
*/
private _mouse_mouseup = (e: MouseEvent) => {
if (this.mouse.mouseDown && Date.now() - this.mouse.mouseDown <= 500) {
// if the mouse was down for less than a half a second, it's a click
// this can't depend on this.panning.enabled because that'll always be true when mouse is down
const delta = [
Math.abs(this.panning.x - e.clientX),
Math.abs(this.panning.y - e.clientY),
];
if (delta[0] < 5 && delta[1] < 5) {
// difference from the start position to the up position is very very slow,
// so it's most likely intended to be a click
this.emit("click", {
clientX: e.clientX,
clientY: e.clientY,
});
}
}
if (this.panning.enabled) {
// currently panning
e.preventDefault();
e.stopPropagation();
this.panning.end(e.clientX, e.clientY);
}
};
/** /**
* Update viewport scale and position * Update viewport scale and position
* *
@ -545,6 +645,9 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
cleanup() { cleanup() {
// remove event handlers // remove event handlers
this.unregisterTouchEvents();
this.unregisterMouseEvents();
} }
// utilities // utilities