繁体   English   中英

在画布转换后,如何获取要在画布2d上进行命中测试的对象绘制的2D尺寸?

[英]How to get the 2d dimensions of the object being drawn for hit test on canvas 2d after canvas transformations?

我在2d画布上绘制简单的形状,同时对以下形状应用变换:

  const rect = ({ x, y, width, height }) => {
    ctx.fillStyle = 'black';
    ctx.fillRect(x, y, width, height);
  };

  const transform = ({ translate, rotate, scale }, f) => {
    // ctx is a 2d canvas context
    ctx.save();

    if (translate) {
      ctx.translate(translate[0], translate[1]);
    }
    if (rotate) {
      ctx.rotate(rotate);
    }

    if (scale) {
      ctx.scale(scale[0], scale[1]);
    }

    f(ctx);

    ctx.restore();
  };
  const draw = () => {
     transform({ translate: [10, 10] }, () => {
        rect({ x: 0, y: 0, width: 10, height: 10 });
     });
  };

现在,我需要知道画布空间中此矩形的尺寸,以便可以对鼠标单击位置进行测试。

之前我曾问过这个问题, 如何在关于Webgl命中测试检测的模型视图转换之后,如何获取要在Webgl上进行命中测试的绘制对象的二维尺寸 但是该解决方案不适用于此处,因为我没有转换矩阵。

一种可能的解决方案是,我在另一个名为“碰撞画布”的画布上绘制同一对象,并使用与该对象相关的特定颜色,稍后当我想对画布上的某个位置进行测试时,我查询该位置上的碰撞画布颜色,然后看看颜色是否与对象特定的颜色匹配,那是个好主意吗?

我看到最好的解决方案是使用ctx.currentTransform方法。 根据对象的尺寸已知,可以通过以下函数找到转换后的尺寸:

function applyTransform(bounds, currentTransform) {
  bounds.x = ct.e + bounds.x * ct.a;
  bounds.y = ct.f + bounds.y * ct.d;
  bounds.width = bounds.width * ct.a;
  bounds.height = bounds.height * ct.d;
}

这真的取决于您的问题是什么。 你写了:

如何获得被绘制对象的二维尺寸

你写了

用于命中测试。

你想要哪一个。 您想要二维尺寸还是想要进行命中测试?

对于尺寸,在变换之前,您需要自己知道形状的大小。 然后您可以使用ctx.currentTransform获得当前变换

不幸的是,截至2019年8月,Chrome仅支持currentTransform ,因此您需要某种形式的polyfill,但是如果您搜索“ currentTransform polyfill”,则其中有几种。

对于命中测试,可以使用ctx.isPointInPath

您定义一个路径。 它不一定要与您要绘制的东西相同,尽管当然可以。 那你可以打电话

ctx.isPointInPath(pathToCheck, canvasRelativeX, canvasRelativeY);

 const ctx = document.querySelector('canvas').getContext('2d'); const path = new Path2D(); const points = [ [10, 0], [20, 0], [20, 10], [30, 10], [30, 20], [20, 20], [20, 30], [10, 30], [10, 20], [0, 20], [0, 10], [10, 10], ]; points.forEach(p => path.lineTo(...p)); path.closePath(); let mouseX; let mouseY; function render(time) { const t = time / 1000; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.translate( 150 + Math.sin(t * 0.1) * 100, 75 + Math.cos(t * 0.2) * 50); ctx.rotate(t * 0.3); ctx.scale( 2 + Math.sin(t * 0.4) * 0.5, 2 + Math.cos(t * 0.5) * 0.5); const inpath = ctx.isPointInPath(path, mouseX, mouseY); ctx.fillStyle = inpath ? 'red' : 'blue'; ctx.fill(path); ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform requestAnimationFrame(render); } requestAnimationFrame(render); ctx.canvas.addEventListener('mousemove', (e) => { mouseX = e.offsetX * ctx.canvas.width / ctx.canvas.clientWidth; mouseY = e.offsetY * ctx.canvas.height / ctx.canvas.clientHeight; }); 
 canvas { border: 1px solid black; } 
 <canvas></canvas> 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM