mydeate/js/pointer.js

105 lines
3.4 KiB
JavaScript

// 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
const pointer = {x: undefined, y: undefined};
// Trace the path of the cursor
const pointerHistory = [];
const maxHistory = 50;
const dropTargetMaxRange = 20;
const drag = {
start: {x: undefined, y: undefined, t: undefined},
element: undefined,
placeholder: undefined,
}
function startDrag({element, x, y}) {
drag.element = element;
drag.start = {x, y, t: currentTime};
// We use a placeholder to represent the new position
const {x: ox, y: oy, width, height} = element.el.getBoundingClientRect();
drag.placeholder = elements.add({
...element,
id: `${this.id}-placeholder`,
classes: [...Array.from(element.el.classList.values()), "moving", "placeholder"],
width, height, x, y,
});
drag.element.el.classList.add("moving");
mainDiv.classList.add("dragging");
}
function initializePointer() {
mainDiv.addEventListener("pointermove", (e) => {
const {clientX: x, clientY: y} = e;
Object.assign(pointer, {x, y});
if (drag.start.t) {
const displacement = {
x: x - drag.start.x,
y: y - drag.start.y,
};
const {x: ox, y: oy} = drag.element.el.getBoundingClientRect();
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
// }
//})
}
});
mainDiv.addEventListener("pointerup", (e) => {
if (!drag.start.t) return;
const {clientX: x, clientY: y} = e;
// Displacement
const d = {
x: x - drag.start.x,
y: y - drag.start.y,
};
const {x: ox, y: oy, width, height} = drag.element.el.getBoundingClientRect();
drag.element.setPosition({ x: ox + d.x, y: oy + d.y}).setSize(width, height);
drag.element.el.classList.remove("moving");
elements.remove(drag.placeholder.id);
drag.start = {};
drag.placeholder = undefined;
drag.element = undefined;
mainDiv.classList.remove("dragging");
});
}
function updatePointerHistory({decay} = {decay: true}) {
if (pointer.x === undefined || pointer.y === undefined) return;
if (!pointerHistory.length) {
pointerHistory.push({...pointer, t: currentTime});
return;
}
const lastPointer = pointerHistory[pointerHistory.length - 1];
if (decay || pointer.x !== lastPointer.x || pointer.y !== lastPointer.y) {
pointerHistory.push({...pointer, t: currentTime});
}
while (pointerHistory.length > maxHistory) {
pointerHistory.shift();
}
}
function drawPointerHistory() {
if (pointerHistory.length < 2) return;
for (let i = 1; i < pointerHistory.length; i++) {
fgCtx.beginPath();
const opacity = i / pointerHistory.length;
fgCtx.strokeStyle = `rgba(128, 0, 0, ${opacity})`;
fgCtx.moveTo(pointerHistory[i - 1].x, pointerHistory[i - 1].y);
fgCtx.lineTo(pointerHistory[i].x, pointerHistory[i].y);
fgCtx.stroke();
}
}