如何获得CSS 3D转换画布的画布相对鼠标位置?

只是为了好玩,我试图在3D变换的画布上绘制。我写了一些代码,它的工作原理


const m4 = twgl.m4;


[...document.querySelectorAll('canvas')].forEach((canvas) => {

  const ctx = canvas.getContext('2d');

  let count = 0;


  canvas.addEventListener('mousemove', (e) => {

    const pos = getElementRelativeMousePosition(e, canvas);

    ctx.fillStyle = hsl((count++ % 10) / 10, 1, 0.5);

    ctx.fillRect(pos.x - 1, pos.y - 1, 3, 3);

  });

});


function getElementRelativeMousePosition(e, elem) {

  const pos = convertPointFromPageToNode(elem, e.pageX, e.pageY); 

  

  return {

    x: pos[0],

    y: pos[1],

  };

}


function hsl(h, s, l) {

  return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;

}


function convertPointFromPageToNode(elem, pageX, pageY) {

  const mat = m4.inverse(getTransformationMatrix(elem));

  return m4.transformPoint(mat, [pageX, pageY, 0]);

};


function getTransformationMatrix(elem) {

  let matrix = m4.identity();

  let currentElem = elem;


  while (currentElem !== undefined && 

         currentElem !== currentElem.ownerDocument.documentElement) {

    const style = window.getComputedStyle(currentElem);

    const localMatrix = parseMatrix(style.transform);

    matrix = m4.multiply(localMatrix, matrix);

    currentElem = currentElem.parentElement;

  }


  const w = elem.offsetWidth;

  const h = elem.offsetHeight;

  let i = 4;

  let left = +Infinity;

  let top = +Infinity;

  for (let i = 0; i < 4; ++i) {

    const p = m4.transformPoint(matrix, [w * (i & 1), h * ((i & 2) >> 1), 0]);

    left = Math.min(p[0], left);

    top = Math.min(p[1], top);

  }

  const rect = elem.getBoundingClientRect()

  document.querySelector('p').textContent =

    `${w}x${h}`;

  matrix =  m4.multiply(m4.translation([

     window.pageXOffset + rect.left - left, 

     window.pageYOffset + rect.top - top,

     0]), matrix);

  return matrix;

}

上面的代码有效。将鼠标移到两个黄色画布元素上,您会看到它正确绘制了。


但是,一旦我添加了一些3D变换,它就会失败。


将“#c6”的CSS更改为


    #c6 {

      background: pink;

      transform: rotate(45deg) rotateX(45deg);  /* changed */

      display: inline-block;

    }

现在当我在右边的黄色画布上绘制时,一切都关闭了。

有什么想法我做错了吗?


喵喵时光机
浏览 172回答 2
2回答

慕哥6287543

igh ...尚不是肯定的答案,但显然event.offsetX,event.offsetY即使根据MDN,它们仍不是标准值。测试它似乎可以在Chrome和Firefox中使用。Safari在某些测试中处于关闭状态。同样不幸的是,在触摸事件上不存在offsetX和offsetY。它们确实存在于指针事件上,但自2019/05起Safari不支持指针事件[...document.querySelectorAll('canvas')].forEach((canvas) => {&nbsp; const ctx = canvas.getContext('2d');&nbsp; let count = 0;&nbsp; canvas.addEventListener('mousemove', (e) => {&nbsp; &nbsp; const pos = {&nbsp; &nbsp; &nbsp; x: e.offsetX * ctx.canvas.width / ctx.canvas.clientWidth,&nbsp; &nbsp; &nbsp; y: e.offsetY * ctx.canvas.height / ctx.canvas.clientHeight,&nbsp; &nbsp; };&nbsp; &nbsp; ctx.fillStyle = hsl((count++ % 10) / 10, 1, 0.5);&nbsp; &nbsp; ctx.fillRect(pos.x - 1, pos.y - 1, 3, 3);&nbsp; });});function hsl(h, s, l) {&nbsp; return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;}canvas {&nbsp;&nbsp; display: block;&nbsp; background: yellow;&nbsp; transform: scale(0.75);}#c1 {&nbsp; margin: 20px;&nbsp; background: red;&nbsp; transform: translateX(-50px);&nbsp; display: inline-block;}#c2 {&nbsp; margin: 20px;&nbsp; background: green;&nbsp; transform: rotate(45deg);&nbsp; display: inline-block;}#c3 {&nbsp; margin: 20px;&nbsp; background: blue;&nbsp; display: inline-block;}#c4 {&nbsp; position: absolute;&nbsp; top: 0;&nbsp; background: cyan;&nbsp; transform: translateX(-250px) rotate(55deg);&nbsp; display: inline-block;}#c5 {&nbsp; background: magenta;&nbsp; transform: translate(50px);&nbsp; display: inline-block;}#c6 {&nbsp; background: pink;&nbsp; transform: rotate(45deg) rotateX(45deg);&nbsp; /* changed */&nbsp; display: inline-block;}<p>foo</p><div id="c1">&nbsp; <div id="c2">&nbsp; &nbsp; <div id="c3">&nbsp; &nbsp; &nbsp; <canvas></canvas>&nbsp; &nbsp; </div>&nbsp; </div></div><div id="c4">&nbsp; <div id="c5">&nbsp; &nbsp; <div id="c6">&nbsp; &nbsp; &nbsp; <canvas></canvas>&nbsp; &nbsp; </div>&nbsp; </div></div>不幸的是,我们仍然有一个问题,有时我们想要事件外部的画布相对位置。在下面的示例中,即使指针不移动,我们也希望将其保持在鼠标指针下方。[...document.querySelectorAll('canvas')].forEach((canvas) => {&nbsp; const ctx = canvas.getContext('2d');&nbsp; ctx.canvas.width&nbsp; = ctx.canvas.clientWidth;&nbsp; ctx.canvas.height = ctx.canvas.clientHeight;&nbsp; let count = 0;&nbsp; function draw(e, radius = 1) {&nbsp; &nbsp; const pos = {&nbsp; &nbsp; &nbsp; x: e.offsetX * ctx.canvas.width / ctx.canvas.clientWidth,&nbsp; &nbsp; &nbsp; y: e.offsetY * ctx.canvas.height / ctx.canvas.clientHeight,&nbsp; &nbsp; };&nbsp; &nbsp; document.querySelector('#debug').textContent = count;&nbsp; &nbsp; ctx.beginPath();&nbsp; &nbsp; ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);&nbsp; &nbsp; ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5);&nbsp; &nbsp; ctx.fill();&nbsp; }&nbsp; function preventDefault(e) {&nbsp; &nbsp; e.preventDefault();&nbsp; }&nbsp; if (window.PointerEvent) {&nbsp; &nbsp; canvas.addEventListener('pointermove', (e) => {&nbsp; &nbsp; &nbsp; draw(e, Math.max(Math.max(e.width, e.height) / 2, 1));&nbsp; &nbsp; });&nbsp; &nbsp; canvas.addEventListener('touchstart', preventDefault, {passive: false});&nbsp; &nbsp; canvas.addEventListener('touchmove', preventDefault, {passive: false});&nbsp; } else {&nbsp; &nbsp; canvas.addEventListener('mousemove', draw);&nbsp; &nbsp; canvas.addEventListener('mousedown', preventDefault);&nbsp; }});function hsl(h, s, l) {&nbsp; return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;}.scene {&nbsp; width: 200px;&nbsp; height: 200px;&nbsp; perspective: 600px;}.cube {&nbsp; width: 100%;&nbsp; height: 100%;&nbsp; position: relative;&nbsp; transform-style: preserve-3d;&nbsp; animation-duration: 16s;&nbsp; animation-name: rotate;&nbsp; animation-iteration-count: infinite;&nbsp; animation-timing-function: linear;}@keyframes rotate {&nbsp; from { transform: translateZ(-100px) rotateX(&nbsp; 0deg) rotateY(&nbsp; 0deg); }&nbsp; to&nbsp; &nbsp;{ transform: translateZ(-100px) rotateX(360deg) rotateY(720deg); }}.cube__face {&nbsp; position: absolute;&nbsp; width: 200px;&nbsp; height: 200px;&nbsp; display: block;}.cube__face--front&nbsp; { background: rgba(255, 0, 0, 0.2); transform: rotateY(&nbsp; 0deg) translateZ(100px); }.cube__face--right&nbsp; { background: rgba(0, 255, 0, 0.2); transform: rotateY( 90deg) translateZ(100px); }.cube__face--back&nbsp; &nbsp;{ background: rgba(0, 0, 255, 0.2); transform: rotateY(180deg) translateZ(100px); }.cube__face--left&nbsp; &nbsp;{ background: rgba(255, 255, 0, 0.2); transform: rotateY(-90deg) translateZ(100px); }.cube__face--top&nbsp; &nbsp; { background: rgba(0, 255, 255, 0.2); transform: rotateX( 90deg) translateZ(100px); }.cube__face--bottom { background: rgba(255, 0, 255, 0.2); transform: rotateX(-90deg) translateZ(100px); }<div class="scene">&nbsp; <div class="cube">&nbsp; &nbsp; <canvas class="cube__face cube__face--front"></canvas>&nbsp; &nbsp; <canvas class="cube__face cube__face--back"></canvas>&nbsp; &nbsp; <canvas class="cube__face cube__face--right"></canvas>&nbsp; &nbsp; <canvas class="cube__face cube__face--left"></canvas>&nbsp; &nbsp; <canvas class="cube__face cube__face--top"></canvas>&nbsp; &nbsp; <canvas class="cube__face cube__face--bottom"></canvas>&nbsp; </div></div><pre id="debug"></pre>
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript