I'm trying to come up with a cross-device code that handles pointer events.
I'm running this code successfully on Chrome/Android (using USB debugging), but the Chrome desktop just acts up and keeps firing pointermove
after the mouse has been released.
(Another problem is that the moves are not as smooth as on the mobile)
These SO posts don't solve my problem:
The "pointerup" Event is assigned to the #canvas, but such event will never occur because the mouse is actually above the generated DIV circle.
Since your circles are just visual helpers, set in CSS
.dot {
/* ... */
pointer-events: none;
}
Also, make sure to use Event.preventDefault()
on "pointerdown"
.
Regarding the other strategies for a seamless experience, both on desktop and on mobile (touch):
"pointerdown"
Event to a desired Element (canvas
in your case)window
object for all the other eventsEdited example:
const canvas = document.getElementById('canvas');
function startTouch(ev) {
ev.preventDefault();
const dot = document.createElement('div');
dot.classList.add('dot');
dot.id = ev.pointerId;
dot.style.left = `${ev.pageX}px`;
dot.style.top = `${ev.pageY}px`;
dot.style.backgroundColor = `hsl(${ev.pointerId * 50 % 360}, 80%, 50%)`;
document.body.append(dot);
canvas.setPointerCapture(ev.pointerId);
}
function moveTouch(ev) {
const dot = document.getElementById(ev.pointerId);
if (!dot) return;
dot.style.left = `${ev.pageX}px`;
dot.style.top = `${ev.pageY}px`;
}
function endTouch(ev) {
const dot = document.getElementById(ev.pointerId);
if (!dot) return;
canvas.releasePointerCapture(ev.pointerId);
removeDot(dot);
}
function removeDot(dot) {
dot.remove();
}
canvas.addEventListener("pointerdown", startTouch);
canvas.addEventListener("pointermove", moveTouch);
canvas.addEventListener("pointerup", endTouch);
canvas.addEventListener("pointercancel", endTouch);
* {
margin: 0;
box-sizing: border-box;
}
body {
margin: 10vh;
}
.dot {
pointer-events: none;
position: absolute;
width: 4rem;
height: 4rem;
border-radius: 50%;
background-color: deeppink;
translate: -50% -50%;
left: calc(var(--x) * 1px);
top: calc(var(--y) * 1px);
}
#canvas {
height: 80vh;
background-color: black;
touch-action: none;
}
<div id="canvas"></div>
The code needs also this improvement:
As per the comments section here's a viable solution that uses custom properties CSS variables and JS's CSSStyleDeclaration.setProperty() method.
Basically the --x
and --y
CSS properties values are updated from the pointerdown/move event handlers to reflect the current clientX
and clientY
values:
const el = (sel, par) => (par || document).querySelector(sel);
const elNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
const canvas = document.getElementById("canvas");
const pointersDots = (parent) => {
const elParent = typeof parent === "string" ? el(parent) : parent;
const dots = new Map();
const moveDot = (elDot, {clientX: x,clientY: y}) => {
elDot.style.setProperty("--x", x);
elDot.style.setProperty("--y", y);
};
const onDown = (ev) => {
ev.preventDefault();
const elDot = elNew("div", { className: "dot" });
elDot.style.backgroundColor = `hsl(${ev.pointerId * 50 % 360}, 80%, 50%)`;
moveDot(elDot, ev);
elParent.append(elDot);
dots.set(ev.pointerId, elDot);
canvas.setPointerCapture(ev.pointerId);
};
const onMove = (ev) => {
if (dots.size === 0) return;
const elDot = dots.get(ev.pointerId);
moveDot(elDot, ev);
};
const onUp = (ev) => {
if (dots.size === 0) return;
const elDot = dots.get(ev.pointerId);
elDot.remove();
dots.delete(ev.pointerId);
canvas.releasePointerCapture(ev.pointerId);
};
canvas.addEventListener("pointerdown", onDown);
canvas.addEventListener("pointermove", onMove);
canvas.addEventListener("pointerup", onUp);
canvas.addEventListener("pointercancel", onUp);
};
// Init: Pointers helpers
pointersDots("#canvas");
* {
margin: 0;
}
.dot {
--x: 0;
--y: 0;
pointer-events: none;
position: absolute;
width: 2rem;
height: 2rem;
border-radius: 50%;
background-color: deeppink;
transform: translate(-50%, -50%);
left: calc(var(--x) * 1px);
top: calc(var(--y) * 1px);
}
#canvas {
margin: 10vh;
height: 80vh;
background-color: black;
touch-action: none;
}
<div id="canvas"></div>