This is gonna need some cleanup, but it now correctly positions, repositions, reports, and maintains the position of the target *and* the pointer.
This commit is contained in:
parent
93fb8f1c2e
commit
2b2959febb
|
@ -0,0 +1,148 @@
|
|||
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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
export class LitDragStart extends Event {
|
||||
static readonly eventName = "lit-drag-start";
|
||||
x: number;
|
||||
y: number;
|
||||
constructor(x: number, y: number) {
|
||||
super(LitDragStart.eventName, { bubbles: true, composed: true });
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
export class LitDrag extends Event {
|
||||
static readonly eventName = "lit-drag";
|
||||
x: number;
|
||||
y: number;
|
||||
constructor(x: number, y: number) {
|
||||
super(LitDrag.eventName, { bubbles: true, composed: true });
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
export class LitDragEnd extends Event {
|
||||
static readonly eventName = "lit-drag-end";
|
||||
constructor() {
|
||||
super(LitDragEnd.eventName, { bubbles: true, composed: true });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface GlobalEventHandlersEventMap {
|
||||
[LitDragStart.eventName]: LitDragStart;
|
||||
[LitDrag.eventName]: LitDrag;
|
||||
[LitDragEnd.eventName]: LitDragEnd;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { LitDraggable, LitDragEvent } from "./types";
|
||||
|
||||
export class LitDragStart extends Event implements LitDragEvent {
|
||||
static readonly eventName = "lit-drag-start";
|
||||
offsetX: number = 0;
|
||||
offsetY: number = 0;
|
||||
node: HTMLElement;
|
||||
// container: HTMLElement;
|
||||
constructor(source: LitDraggable) {
|
||||
super(LitDragStart.eventName, { bubbles: true, composed: true });
|
||||
this.offsetX = source.translateX;
|
||||
this.offsetY = source.translateY;
|
||||
this.node = source.host;
|
||||
// this.container = source.container;
|
||||
}
|
||||
}
|
||||
|
||||
export class LitDragging extends Event implements LitDragEvent {
|
||||
static readonly eventName = "lit-dragging";
|
||||
offsetX: number = 0;
|
||||
offsetY: number = 0;
|
||||
node: HTMLElement;
|
||||
// container: HTMLElement;
|
||||
constructor(source: LitDraggable) {
|
||||
super(LitDragging.eventName, { bubbles: true, composed: true });
|
||||
this.offsetX = source.translateX;
|
||||
this.offsetY = source.translateY;
|
||||
this.node = source.host;
|
||||
// this.container = source.container;
|
||||
}
|
||||
}
|
||||
|
||||
export class LitDragEnd extends Event implements LitDragEvent {
|
||||
static readonly eventName = "lit-drag-end";
|
||||
offsetX: number = 0;
|
||||
offsetY: number = 0;
|
||||
node: HTMLElement;
|
||||
// container: HTMLElement;
|
||||
constructor(source: LitDraggable) {
|
||||
super(LitDragEnd.eventName, { bubbles: true, composed: true });
|
||||
this.offsetX = source.initialX;
|
||||
this.offsetY = source.initialY;
|
||||
this.node = source.host;
|
||||
// this.container = source.container;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface GlobalEventHandlersEventMap {
|
||||
[LitDragStart.eventName]: LitDragStart;
|
||||
[LitDragging.eventName]: LitDragging;
|
||||
[LitDragEnd.eventName]: LitDragEnd;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { LitElement, html, css } from "lit";
|
||||
import { customElement } from "lit/decorators/custom-element.js";
|
||||
|
||||
@customElement("screen-size")
|
||||
export class ScreenSize extends LitElement {
|
||||
static get styles() {
|
||||
return css`
|
||||
div#indicator::before {
|
||||
font-family: "Cinzel", serif;
|
||||
font-optical-sizing: auto;
|
||||
--w: tan(atan2(var(--w_raw), 1px));
|
||||
--h: tan(atan2(var(--h_raw), 1px));
|
||||
font-size: 3rem;
|
||||
font-weight: 900;
|
||||
content: counter(w) "x" counter(h);
|
||||
counter-reset: h var(--h) w var(--w);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<div id="indicator"></div>`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
export interface LitDraggable {
|
||||
translateX: number;
|
||||
translateY: number;
|
||||
initialX: number;
|
||||
initialY: number;
|
||||
host: HTMLElement;
|
||||
}
|
||||
|
||||
export interface LitDragEvent extends Event {
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
node: HTMLElement;
|
||||
}
|
Loading…
Reference in New Issue