简体   繁体   English

canvas 上的鼠标事件 position 按 CSS 规则重新缩放

[英]Mouse event position on a canvas rescaled by a CSS rule

When a HTML canvas is in "standard" 1:1 scale (neither enlarged nor shrinked by a CSS rule), it is simple to get the mouse position in the canvas coordinates:当 HTML canvas 处于“标准”1:1 比例时(既不放大也不缩小 CSS 规则),很容易在 canvas 坐标中获取鼠标 position:

c2.onpointermove = (e) => {
    var mousemove_coords = { x: e.offsetX, y: e.offsetY };

Here is an example where the canvas' size gets modified by flex + max-width CSS rules.这是一个示例,其中画布的大小通过flex + max-width CSS 规则进行了修改。 The cursor should change only on top of the green square, which is not the case here, the cursor changes on bottom right of the square. cursor 应该只在绿色方块的顶部发生变化,这里不是这种情况,cursor 在方块的右下角发生变化。

How to get the coordinates of a mouse event over a canvas when it is resized by a CSS rule?当通过 CSS 规则调整大小时,如何在 canvas 上获取鼠标事件的坐标?

 var c1 = document.getElementById("canvas1"), ctx1 = c1.getContext("2d"); var c2 = document.getElementById("canvas2"), ctx2 = c2.getContext("2d"); var w = 2000, h = 1000; var x0 = 50, y0 = 75, x1 = 100, y1 = 200; ctx1.canvas.width = w; ctx1.canvas.height = h; ctx1.rect(0, 0, w, h); ctx1.fill(); ctx2.canvas.width = w; ctx2.canvas.height = h; ctx2.rect(x0, y0, x1, y1); ctx2.fillStyle = "green"; ctx2.fill(); c2.onpointermove = (e) => { var mousemove_coords = { x: e.offsetX, y: e.offsetY }; var hovered = mousemove_coords.x >= x0 && mousemove_coords.x <= x1 && mousemove_coords.y >= y0 && mousemove_coords.y <= y1; c2.style.cursor = hovered? "move": "crosshair"; };
 body, .container { height: 100%; width: 100%; margin: 0; padding: 0; }.container { display: flex; align-items: center; justify-content: center; background-color: yellow; }.left-column { margin: 1rem; flex: 1; display: flex; align-items: center; justify-content: center; }.canvas-wrapper { position: relative; } #canvas1 { max-width: 100%; max-height: 100%; } #canvas2 { position: absolute; top: 0; left: 0; max-width: 100%; max-height: 100%; }.right-column { width: 300px; }
 <div class="container"> <div class="left-column column"> <div class="canvas-wrapper"> <canvas id="canvas1"></canvas> <canvas id="canvas2"></canvas> </div> </div> <div class="right-column column"> Hello world </div> </div>

Edit: in my real code, I'm drawing on the canvas from frames received by HTTP requests with:编辑:在我的真实代码中,我从 HTTP 请求收到的帧中绘制 canvas :

update_frame_task = setInterval(() => {
        fetch("get_frame")
            .then((r) => r.arrayBuffer())
            .then((arr) => {
                if (size === null) {
                    size = { w: 2000, h: 1000 };
                    ctx.canvas.width = size.w;
                    ctx.canvas.height = size.h;
                    ctx2.canvas.width = size.w;
                    ctx2.canvas.height = size.h;
                }
                var byteArray = new Uint8ClampedArray(arr);
                var imgData = new ImageData(byteArray, size.w, size.h);
                ctx.putImageData(imgData, 0, 0);
            });
    }, 200);

You can scale the mouse co-ordinates to the CSS resize using getComputedStyle .您可以使用getComputedStyle将鼠标坐标缩放到 CSS 调整大小。

Also, canvas rect uses width and height.此外,canvas rect使用宽度和高度。

 var c1 = document.getElementById("canvas1"), ctx1 = c1.getContext("2d"); var c2 = document.getElementById("canvas2"), ctx2 = c2.getContext("2d"); var w = 2000, h = 1000; var x0 = 50, y0 = 75, x1 = 100, y1 = 200; ctx1.canvas.width = w; ctx1.canvas.height = h; ctx1.rect(0, 0, w, h); ctx1.fill(); ctx2.canvas.width = w; ctx2.canvas.height = h; ctx2.rect(x0, y0, x1, y1); ctx2.fillStyle = "green"; ctx2.fill(); ratio=w/parseFloat(window.getComputedStyle(c2).width); c2.onpointermove = (e) => { var mousemove_coords = { x: e.offsetX*ratio, y: e.offsetY*ratio }; k.innerHTML=mousemove_coords.x+'<br>'+mousemove_coords.y; // scaled coords var hovered = mousemove_coords.x >= x0 && mousemove_coords.x <= x0+x1 && mousemove_coords.y >= y0 && mousemove_coords.y <= y0+y1; c2.style.cursor = hovered? "move": "crosshair"; };
 body, .container { height: 100%; width: 100%; margin: 0; padding: 0; }.container { display: flex; align-items: center; justify-content: center; background-color: yellow; }.left-column { margin: 1rem; flex: 1; display: flex; align-items: center; justify-content: center; }.canvas-wrapper { position: relative; } #canvas1 { max-width: 100%; max-height: 100%; } #canvas2 { position: absolute; top: 0; left: 0; max-width: 100%; max-height: 100%; }.right-column { width: 300px; }
 <div class="container"> <div class="left-column column"> <div class="canvas-wrapper"> <canvas id="canvas1"></canvas> <canvas id="canvas2"></canvas> </div> </div> <div id='k' class="right-column column"> Hello world </div> </div>

Don't resize the CSS size of the canvas, the image in it only gets blurry.不要调整 canvas 的 CSS 大小,其中的图像只会变得模糊。 Instead, scale the image.相反,缩放图像。 As putImageData doesn't support scaling, use drawImage to draw the image into the canvas, it has the ability to scale the image.由于putImageData不支持缩放,使用drawImage将图像绘制到canvas中,它具有缩放图像的能力。 Since ImageData object can't be passed to drawImage , you've to convert it to ImageBitmap using createImageBitmap .由于无法将 ImageData object 传递给drawImage ,因此您必须使用createImageBitmap将其转换为 ImageBitmap 。 With these changes your fetch should look something like this:通过这些更改,您的fetch应该如下所示:

fetch("get_frame")
  .then((r) => r.arrayBuffer())
  .then(async (arr) => {
    if (size === null) {
      size = {
        w: 2000,
        h: 1000
      };
     }
    var byteArray = new Uint8ClampedArray(arr);
    var imgData = new ImageData(byteArray, size.w, size.h);
    var image = await createImageBitmap(imageData);
    ctx.drawImage(image, 0, 0, 800, 400);
  });

This might not fix all the issues, as you've already might have changed the size of the canvas with CSS. To fix that you could remove all the CSS of the canvas, and apply JMP's answer, and set the dimensions with width and height attributes of the canvas when the layout of the page is created/changes.这可能无法解决所有问题,因为您可能已经将 canvas 的大小更改为 CSS。要解决此问题,您可以删除 canvas 的所有 CSS,并应用 JMP 的答案,并使用widthheight设置尺寸创建/更改页面布局时 canvas 的属性。 Responsive canvases are a bit tricky to design.响应式画布的设计有点棘手。

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

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