149 lines
4.3 KiB
TypeScript
149 lines
4.3 KiB
TypeScript
import { LitElement, ReactiveController, ReactiveControllerHost } from "lit";
|
|
import { LitDragStart, LitDrag, LitDragEnd } from "./events.js";
|
|
export interface MouseTouchLocation {
|
|
x: number;
|
|
y: number;
|
|
}
|
|
|
|
function getTouch(e: TouchEvent, identifier: number): MouseTouchLocation {
|
|
const touchObj = e.targetTouches
|
|
? Array.from(e.targetTouches).find((t) => identifier === t.identifier)
|
|
: Array.from(e.changedTouches).find((t) => identifier === t.identifier);
|
|
|
|
return {
|
|
x: touchObj?.clientX ?? -1,
|
|
y: touchObj?.clientY ?? -1,
|
|
};
|
|
}
|
|
|
|
export function getMouseTouchLocation(
|
|
ev: MouseEvent | TouchEvent,
|
|
touchIdentifier: number | undefined
|
|
): MouseTouchLocation | undefined {
|
|
if (!ev.type.startsWith("touch")) {
|
|
return {
|
|
x: (ev as MouseEvent).clientX,
|
|
y: (ev as MouseEvent).clientY,
|
|
};
|
|
}
|
|
|
|
if (touchIdentifier === undefined) {
|
|
return;
|
|
}
|
|
|
|
const touchObj = getTouch(ev as TouchEvent, touchIdentifier);
|
|
return {
|
|
x: touchObj.x,
|
|
y: touchObj.y,
|
|
};
|
|
}
|
|
|
|
export const getTouchIdentifier = (e: TouchEvent): number =>
|
|
e.targetTouches && e.targetTouches[0]
|
|
? e.targetTouches[0].identifier
|
|
: e.changedTouches && e.changedTouches[0]
|
|
? e.changedTouches[0].identifier
|
|
: 0;
|
|
|
|
type LitDraggable = ReactiveControllerHost & LitElement;
|
|
|
|
export class DragController implements ReactiveController {
|
|
host: LitDraggable;
|
|
|
|
touchIdentifier?: number;
|
|
|
|
dragging = false;
|
|
|
|
startX?: number;
|
|
|
|
startY?: number;
|
|
|
|
constructor(host: LitDraggable) {
|
|
(this.host = host).addController(this);
|
|
this.dragStart = this.dragStart.bind(this);
|
|
this.drag = this.drag.bind(this);
|
|
this.dragEnd = this.dragEnd.bind(this);
|
|
}
|
|
|
|
hostConnected() {
|
|
const eventArgs = { capture: true, passive: false };
|
|
this.host.addEventListener("mousedown", this.dragStart, eventArgs);
|
|
this.host.addEventListener("touchstart", this.dragStart, eventArgs);
|
|
document.addEventListener("mousemove", this.drag, eventArgs);
|
|
document.addEventListener("touchmove", this.drag, eventArgs);
|
|
document.addEventListener("mouseup", this.dragEnd, eventArgs);
|
|
document.addEventListener("touchcancel", this.dragEnd, eventArgs);
|
|
document.addEventListener("touchend", this.dragEnd, eventArgs);
|
|
}
|
|
|
|
hostDisconnected() {
|
|
this.host.removeEventListener("mousedown", this.dragStart);
|
|
this.host.removeEventListener("touchstart", this.dragStart);
|
|
document.removeEventListener("mousemove", this.drag);
|
|
document.removeEventListener("touchmove", this.drag);
|
|
document.removeEventListener("mouseup", this.dragEnd);
|
|
document.removeEventListener("touchcancel", this.dragEnd);
|
|
document.removeEventListener("touchend", this.dragEnd);
|
|
}
|
|
|
|
private dragStart(ev: MouseEvent | TouchEvent): void {
|
|
if (ev.type.startsWith("mouse") && (ev as MouseEvent).button !== 0) return;
|
|
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
|
|
if (ev.type === "touchstart") {
|
|
this.touchIdentifier = getTouchIdentifier(ev as TouchEvent);
|
|
}
|
|
|
|
const pos = getMouseTouchLocation(ev, this.touchIdentifier);
|
|
|
|
if (!pos) {
|
|
return;
|
|
}
|
|
|
|
this.startX = pos.x;
|
|
this.startY = pos.y;
|
|
this.dragging = true;
|
|
this.host.dispatchEvent(new LitDragStart(this.startX, this.startY));
|
|
}
|
|
|
|
private drag(ev: MouseEvent | TouchEvent): void {
|
|
if (!this.dragging) {
|
|
return;
|
|
}
|
|
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
|
|
const pos = getMouseTouchLocation(ev, this.touchIdentifier);
|
|
|
|
if (!pos) {
|
|
return;
|
|
}
|
|
|
|
let deltaX = pos.x - this.startX!;
|
|
let deltaY = pos.y - this.startY!;
|
|
|
|
if (!deltaX && !deltaY) {
|
|
return;
|
|
}
|
|
|
|
this.host.dispatchEvent(new LitDrag(deltaX, deltaY));
|
|
}
|
|
|
|
private dragEnd(ev: MouseEvent | TouchEvent): void {
|
|
if (!this.dragging) {
|
|
return;
|
|
}
|
|
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
|
|
this.touchIdentifier = undefined;
|
|
this.dragging = false;
|
|
|
|
this.host.dispatchEvent(new LitDragEnd());
|
|
}
|
|
}
|