mydeate/js/pointer.js

112 lines
4.0 KiB
JavaScript

import { Base } from "./base.js";
import { add, sub } from "./util.js";
export class Pointer extends Base {
// Let's set up pointer tracking. We can use the main div as the pointer target.
// Note that we may later add pointer handlers on layered elements
position = { x: undefined, y: undefined };
// Trace the path of the cursor
history = [];
maxHistory = 50;
dropTargetMaxRange = 20;
drag = {
start: { x: undefined, y: undefined, t: undefined },
element: undefined,
placeholder: undefined,
}
startDrag({ element, x, y }) {
this.drag.element = element;
this.drag.start = { x, y, t: this.main.currentTime };
// We use a placeholder to represent the new position
const { x: ox, y: oy, width, height } = element.el.getBoundingClientRect();
const o = { x: ox, y: oy };
this.drag.placeholder = this.main.elements.add({
...element,
id: `${this.id}-placeholder`,
classes: [...Array.from(element.el.classList.values()), "moving", "placeholder"],
position: o,
size: { x: width, y: height },
});
this.drag.element.el.classList.add("moving");
this.main.div.classList.add("dragging");
}
initialize() {
this.main.div.addEventListener("pointermove", (e) => {
const { clientX: x, clientY: y } = e;
Object.assign(this.position, { x, y });
if (this.drag.start.t) {
const displacement = {
x: x - this.drag.start.x,
y: y - this.drag.start.y,
};
const { x: ox, y: oy } = this.drag.element.el.getBoundingClientRect();
this.drag.placeholder.setPosition({
x: ox + displacement.x,
y: oy + displacement.y,
});
// We can check here if we're near one or more drop targets to offer.
// We should indicate which available drop target is currently active.
// If there are multiple options, we can show them.
// const placeholder;
// const nearbyTargets = Array.from(elements.values()).filter(({el}) => {
// const {x, y, width, height} = el.getBoundingClientRect();
// const linearDist = minLinearDist(el.getBoundingClientRect(), );
// if (linearDist <= dropTargetMaxRange) {
// Visually activate the drop target
// }
//})
}
});
this.main.div.addEventListener("pointerup", (e) => {
if (!this.drag.start.t) return;
const { clientX: x, clientY: y } = e;
// Displacement
const d = sub({ x, y }, this.drag.start);
const { x: ox, y: oy, width, height } = this.drag.element.el.getBoundingClientRect();
const o = { x: ox, y: oy };
const size = { x: width, y: height };
this.drag.element.setPosition(add(o, d)).setSize(size);
this.drag.element.el.classList.remove("moving");
this.main.elements.remove(this.drag.placeholder.id);
this.drag.start = {};
this.drag.placeholder = undefined;
this.drag.element = undefined;
this.main.div.classList.remove("dragging");
});
}
updateHistory({ decay } = { decay: true }) {
if (this.position.x === undefined || this.position.y === undefined) return;
if (!this.history.length) {
this.history.push({ ...this.position, t: this.main.currentTime });
return;
}
const lastPointer = this.history[this.history.length - 1];
if (decay || this.position.x !== lastPointer.x || this.position.y !== lastPointer.y) {
this.history.push({ ...this.position, t: this.main.currentTime });
}
while (this.history.length > this.maxHistory) {
this.history.shift();
}
}
drawHistory() {
const { fgCtx } = this.main.canvases;
if (this.history.length < 2) return;
for (let i = 1; i < this.history.length; i++) {
fgCtx.beginPath();
const opacity = i / this.history.length;
fgCtx.lineWidth = "0.2";
fgCtx.strokeStyle = `rgba(128, 0, 0, ${opacity})`;
fgCtx.moveTo(this.history[i - 1].x, this.history[i - 1].y);
fgCtx.lineTo(this.history[i].x, this.history[i].y);
fgCtx.stroke();
}
}
}