112 lines
4.0 KiB
JavaScript
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();
|
|
}
|
|
}
|
|
|
|
} |