4

I apologise that this question has been asked before

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...

  • Thank you for taking the time to reply @Kaiido, I hope my comment on your solution didn't seem offensive, it wasn't my intention. I've edited in a fiddle using `offsetX` & `offsetY` to illustrate how I didn't manage to get it to work - maybe it has something to do with compensating using a CSS transform on the canvas? but I think I tried that – slartibartfast Oct 15 '19 at 01:56
  • No no it wasn't offensive at all no worries, I just really wondered what was your issue, I can now see that indeed in Firefox there is something odd with your snippet and I can't seem to understand what it is right now btw. Thanks for the edit, that's really what I was asking for and now your question clearly explains how it is not a duplicate of these linked Questions. – Kaiido Oct 15 '19 at 02:01
  • Sigh... I now realize [Firefox is broken](https://bugzilla.mozilla.org/show_bug.cgi?id=1261645) and if it did seem to work in the other answer, it's only because of a fluck in the translateZ values... Sounds like you'll need browser spoofing and then use offsetXXX on Blink and layerXXX on Gecko... – Kaiido Oct 15 '19 at 12:22
  • ooh... there is a link to [this SO question](https://stackoverflow.com/questions/36373114/getting-the-touched-position-in-local-coordinates-of-a-transformed-element) and it's related [fiddle](https://jsfiddle.net/gq1vLaxk/6/) in that thread though - I'll try and look at later and if I get anything working I'll post back here. Thank you so much for finding the source of the problem though @Kaiido, I feel a little bit less silly, and you're right that spoofing is an option. – slartibartfast Oct 15 '19 at 14:23

0 Answers0