为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

喵喵时光机 2021-04-20 16:13:58
只是为了好玩,我试图在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;    }现在当我在右边的黄色画布上绘制时,一切都关闭了。有什么想法我做错了吗?
查看完整描述

2 回答

?
慕哥6287543

TA贡献1831条经验 获得超10个赞

igh ...尚不是肯定的答案,但显然event.offsetX,event.offsetY即使根据MDN,它们仍不是标准值。


测试它似乎可以在Chrome和Firefox中使用。Safari在某些测试中处于关闭状态。同样不幸的是,在触摸事件上不存在offsetX和offsetY。它们确实存在于指针事件上,但自2019/05起Safari不支持指针事件


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

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

  let count = 0;


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

    const pos = {

      x: e.offsetX * ctx.canvas.width / ctx.canvas.clientWidth,

      y: e.offsetY * ctx.canvas.height / ctx.canvas.clientHeight,

    };

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

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

  });

});


function hsl(h, s, l) {

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

}

canvas { 

  display: block;

  background: yellow;

  transform: scale(0.75);

}

#c1 {

  margin: 20px;

  background: red;

  transform: translateX(-50px);

  display: inline-block;

}

#c2 {

  margin: 20px;

  background: green;

  transform: rotate(45deg);

  display: inline-block;

}

#c3 {

  margin: 20px;

  background: blue;

  display: inline-block;

}


#c4 {

  position: absolute;

  top: 0;

  background: cyan;

  transform: translateX(-250px) rotate(55deg);

  display: inline-block;

}

#c5 {

  background: magenta;

  transform: translate(50px);

  display: inline-block;

}

#c6 {

  background: pink;

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

  display: inline-block;

}

<p>

foo

</p>

<div id="c1">

  <div id="c2">

    <div id="c3">

      <canvas></canvas>

    </div>

  </div>

</div>

<div id="c4">

  <div id="c5">

    <div id="c6">

      <canvas></canvas>

    </div>

  </div>

</div>

不幸的是,我们仍然有一个问题,有时我们想要事件外部的画布相对位置。在下面的示例中,即使指针不移动,我们也希望将其保持在鼠标指针下方。


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

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

  ctx.canvas.width  = ctx.canvas.clientWidth;

  ctx.canvas.height = ctx.canvas.clientHeight;

  let count = 0;


  function draw(e, radius = 1) {

    const pos = {

      x: e.offsetX * ctx.canvas.width / ctx.canvas.clientWidth,

      y: e.offsetY * ctx.canvas.height / ctx.canvas.clientHeight,

    };

    document.querySelector('#debug').textContent = count;

    ctx.beginPath();

    ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);

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

    ctx.fill();

  }


  function preventDefault(e) {

    e.preventDefault();

  }


  if (window.PointerEvent) {

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

      draw(e, Math.max(Math.max(e.width, e.height) / 2, 1));

    });

    canvas.addEventListener('touchstart', preventDefault, {passive: false});

    canvas.addEventListener('touchmove', preventDefault, {passive: false});

  } else {

    canvas.addEventListener('mousemove', draw);

    canvas.addEventListener('mousedown', preventDefault);

  }

});


function hsl(h, s, l) {

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

}

.scene {

  width: 200px;

  height: 200px;

  perspective: 600px;

}


.cube {

  width: 100%;

  height: 100%;

  position: relative;

  transform-style: preserve-3d;

  animation-duration: 16s;

  animation-name: rotate;

  animation-iteration-count: infinite;

  animation-timing-function: linear;

}


@keyframes rotate {

  from { transform: translateZ(-100px) rotateX(  0deg) rotateY(  0deg); }

  to   { transform: translateZ(-100px) rotateX(360deg) rotateY(720deg); }

}


.cube__face {

  position: absolute;

  width: 200px;

  height: 200px;

  display: block;

}


.cube__face--front  { background: rgba(255, 0, 0, 0.2); transform: rotateY(  0deg) translateZ(100px); }

.cube__face--right  { background: rgba(0, 255, 0, 0.2); transform: rotateY( 90deg) translateZ(100px); }

.cube__face--back   { background: rgba(0, 0, 255, 0.2); transform: rotateY(180deg) translateZ(100px); }

.cube__face--left   { background: rgba(255, 255, 0, 0.2); transform: rotateY(-90deg) translateZ(100px); }

.cube__face--top    { 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">

  <div class="cube">

    <canvas class="cube__face cube__face--front"></canvas>

    <canvas class="cube__face cube__face--back"></canvas>

    <canvas class="cube__face cube__face--right"></canvas>

    <canvas class="cube__face cube__face--left"></canvas>

    <canvas class="cube__face cube__face--top"></canvas>

    <canvas class="cube__face cube__face--bottom"></canvas>

  </div>

</div>

<pre id="debug"></pre>


查看完整回答
反对 回复 2021-04-29
  • 2 回答
  • 0 关注
  • 171 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信