I apologise that this question has been asked before
- How to get a canvas relative mouse position of a CSS 3D transformed canvas?
- Best way to transform mouse coordinates to HTML5 Canvas's transformed context
- How to get the MouseEvent coordinates for an element that has CSS3 Transform?
- Real mouse position in canvas
but unfortunately I couldn't find an answer that I could get to work for me. Although the first-most on the list does appear to contain the solution, after a fair amount of time trying to adapt it to my needs I had to admit defeat.
Essentially, what I am trying to do is draw on a <canvas>
element that has a CSS3 perspective rotation. To do that I think I need to project the mouse coordinates from the screen through the transform matrix, or rather it's inverse transform.
Interestingly (and mildly frustrating) enough, firefox's implementation of event.layerX
and event.layerY
does exactly what I'm looking for
https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/layerX
However being non-standard, it only works in Firefox. Chrome seems to treat it the same way as offsetX & offsetY.
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// events
const pointermove = (event) => {
const pointX = event.layerX;
const pointY = event.layerY;
context.fillRect(pointX - 1,pointY - 1,3,3);
}
canvas.addEventListener("pointermove",pointermove);
body {
width:100%;
height:100%;
perspective:600px;
background-color:rgb(60,60,60);
}
div {
transform-style:preserve-3d;
transform:translateZ(-400px) rotateX(-20deg) rotateY(-40deg);
}
canvas {
background-color:rgb(230,230,230);
}
<div>
<canvas></canvas>
</div>
Alternatively, this answer hinted at using DOMMatrix
and DOMPoint
which I would be happy to accept as a satisfactory solution that at least both Firefox and Chrome seem to support
https://developer.mozilla.org/en-US/docs/Web/API/DOMMatrix/DOMMatrix
https://developer.mozilla.org/en-US/docs/Web/API/DOMPoint
However despite my best efforts I am unable to get it to work, I'm sorry to say. From what I understand, I need to apply the inverse of the canvas transform matrix to get the screen coordinates of the pointer position, and this is the closest I could get to
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// events
const pointermove = (event) => {
const pointX = event.clientX * canvas.width / canvas.clientWidth;
const pointY = event.clientY * canvas.height / canvas.clientHeight;
let point = new DOMPoint(pointX,pointY);
const matrix = new DOMMatrix(window.getComputedStyle(canvas).transform);
matrix.invertSelf();
point = matrix.transformPoint(point);
context.fillRect(pointX - 1,pointY - 1,3,3);
}
canvas.addEventListener("pointermove",pointermove);
body {
width:100%;
height:100%;
perspective:600px;
background-color:rgb(60,60,60);
}
div {
transform-style:preserve-3d;
transform:translateZ(-400px) rotateX(-20deg) rotateY(-40deg);
}
canvas {
background-color:rgb(230,230,230);
}
<div>
<canvas></canvas>
</div>
By tracing around the edges of the canvas, it looks like it is reproducing the transform inside the canvas, projecting the screen coordinates to local coordinates, but I may be wrong. I'm afraid my Math skills aren't up to the task of getting my head round matrices but I'm guessing I'm missing a multiplication somewhere...
would any of you kind, smart people out there know where I'm going wrong, or have any alternative (vanilla) solutions ?
thank you for your time
Edit : here is a fiddle using offsetX & offsetY that I couldn't figure out what was happening, hoping it might shed some light on what I got wrong in trying to apply the answer to the other questions
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// events
const pointermove = (event) => {
const pointX = event.offsetX * canvas.width / canvas.clientWidth;
const pointY = event.offsetY * canvas.height / canvas.clientHeight;
context.fillRect(pointX - 1,pointY - 1,3,3);
}
canvas.addEventListener("pointermove",pointermove);
body {
width:100%;
height:100%;
perspective:600px;
background-color:rgb(60,60,60);
}
div {
transform-style:preserve-3d;
transform:translateZ(-400px) rotateX(-20deg) rotateY(-40deg);
}
canvas {
background-color:rgb(230,230,230);
}
<div>
<canvas></canvas>
</div>
I also realised that haing gone round in a circle, I ended up copying almost verbatim the DOMMatrix
post...