在画布转换后,如何获取要在画布2d上进行命中测试的对象绘制的2D尺寸? [英] How to get the 2d dimensions of the object being drawn for hit test on canvas 2d after canvas transformations?

查看:96
本文介绍了在画布转换后,如何获取要在画布2d上进行命中测试的对象绘制的2D尺寸?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在2d画布上绘制简单的形状,同时在形状上应用转换,例如:

I draw simple shapes on 2d canvas, while applying transformations on shapes like so:

  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 });
     });
  };

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

Now I need to know the dimensions of this rectangle in the canvas space so that I can hit test against the mouse click position.

我早些时候问过这个问题如何在模型视图转换后获取在webgl上进行命中测试的待绘制对象的二维尺寸关于webgl命中测试检测。但是该解决方案在这里不适用,因为我没有转换矩阵。

Earlier I asked this question How to get the 2d dimensions of the object being drawn for hit test on webgl after model view transform about webgl hit test detection. But the solution doesn't apply here because I don't have a transformation matrix.

一种可能的解决方案是,我将同一对象画在另一个称为碰撞的画布上具有与对象相关的特定颜色的画布,稍后当我想对画布上的某个位置进行测试时,我查询该位置上的碰撞画布颜色,看看该颜色是否与对象的特定颜色匹配,这是一个好主意吗?

One possible solution is, I draw the same object on a different canvas called a collision canvas, with a specific color related to object, later when I want to hit test against a position on canvas, I query the collision canvas color on that position and see if the color matches the object specific color, would that be a good idea?

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

I see best solution is to use ctx.currentTransform method. Per the object's dimensions are known, the transformed dimensions can be found by this function:

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;
}


推荐答案

这实际上取决于您的问题是。您写道:

this really depends on what your question is. You wrote:


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

How to get the 2d dimensions of the object being drawn

然后您写了


用于命中测试。

for hit testing.

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

Which do you want. You want the 2d dimensions or you want hit testing?

要进行尺寸转换,您需要自己知道形状的尺寸。然后,您可以使用 获取当前转换ctx.currentTransform

For the dimensions you'd need to know the size of your shape on your own before being transformed. Then you can get the current transform with ctx.currentTransform

不幸的是, currentTransform 仅在Chrome上受支持,因为日期为2019年8月,因此您需要某种形式的polyfill,但是如果您搜索 currentTransform polyfill 那里有几个。

Unfortunately currentTransform is only supported on Chrome as of August 2019 so you need some kind of polyfill but if you search for "currentTransform polyfill" there are several out there.

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

For hit testing you can use ctx.isPointInPath

您定义路径。它不一定要与您要绘制的东西相同,尽管当然可以。然后您可以调用

You define a path. It does not have to be the same as the thing you're drawing though of course it make some sense if it is. Then you can call

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>

这篇关于在画布转换后,如何获取要在画布2d上进行命中测试的对象绘制的2D尺寸?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆