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