【问题标题】:Canvas point plotting doesn't scale after resize调整大小后画布点绘图不缩放
【发布时间】:2017-03-01 18:26:12
【问题描述】:

在使用绘制在其上的 javascript 点动态调整画布大小后,它们不再与它们的放置位置对齐,并且弧线也从它们的渲染方式中被拉长。

下面的可运行示例演示了该问题,因为您单击光标的点会变成一个错误的大椭圆形。

var TargetWidth = 400;

var canvases = $(".hotspot-canvas")

for (i = 0; i < canvases.length; i++) { 
    canvas = $(canvases[i]);

    var src = canvas.attr("data-img");

    initilizePlotPointCanvas(canvas, src);
  
}

function initilizePlotPointCanvas(canvas, src)
{
  var my_image = new Image();
  my_image.onload = function(){
    
    var w1 = this.width;
    var w2 = TargetWidth
  
    var r = w1 / w2;
  
    var h1 = this.height;
    var h2 = this.height / r;
  
    canvas.width(TargetWidth).height(h2).css("background-image", "url("+src+")");
    setTimeout(function(){
      
      var jcanvas = canvas[0];
    
    var ctx = jcanvas.getContext('2d'),
    w = jcanvas.width,
    h = jcanvas.height;
    ctx.translate(-0.1, -0.1);
    jcanvas.onmousedown = function(e) {
      var rect = jcanvas.getBoundingClientRect();
      x3 = e.clientX - rect.left;
      y3 = e.clientY - rect.top;
  
      ctx.clearRect(0, 0, w, h)
      ctx.beginPath();
      ctx.arc(x3, y3, 5, 0, 2 * Math.PI, false);
      ctx.fillStyle = 'red';
      ctx.fill();
      ctx.stroke();
    }
      
    }, 500)
    
  }
  my_image.src = src;
}
.hotspot-canvas {
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
    cursor: crosshair;
    border: 1px solid black;
    background-repeat: no-repeat;
    background-position: center center;
    background-clip: border-box;
    background-origin: padding-box;
    -moz-background-size: cover;
    background-size: cover;
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

<canvas 
  data-id="554924"
  data-img="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQXXarydhE3CZRSMgXCProd1w0_oSwIOPd7zJN5EQmLtQtPDD21" 
  class="hotspot-canvas"
   data-responses="0" data-notes="0" data-actions="0" data-email="123" data-responder="true" data-response-id="" orig_type="14" data-tag="6bbf8f97-758f-47ca-8b8c-24a1cd3ddd55" data-formtemplatequestiontype="s" data-valueifparentna="" data-exportkey=""></canvas>

【问题讨论】:

  • 几天前我不得不解决同样的问题。我必须处理平移和缩放,边框和填充也改变了结果。稍后我将发布我的代码的答案并附上完整的解释。

标签: javascript jquery html css canvas


【解决方案1】:

显示尺寸和画布分辨率

显示尺寸和画布分辨率是两个不同的实体。

当你设置画布样式的宽度和高度时,你设置了显示大小

canvas.style.width = "100px";
canvas.style.height = "100px";

当您设置画布宽度和高度时,您将设置分辨率。

canvas.width = 100;
canvas.height = 100;

您已设置显示尺寸,但忽略了将分辨率与显示尺寸匹配。

您可以通过将分辨率设置为与显示尺寸相匹配来修复它

var bounds = jcanvas.getBoundingClientRect();
jcanvas.width = bounds.width;
jcanvas.height = bounds.height; 

var TargetWidth = 400;

var canvases = $(".hotspot-canvas")

for (i = 0; i < canvases.length; i++) { 
    canvas = $(canvases[i]);

    var src = canvas.attr("data-img");

    initilizePlotPointCanvas(canvas, src);
  
}

function initilizePlotPointCanvas(canvas, src)
{
  var my_image = new Image();
  my_image.onload = function(){
    
    var w1 = this.width;
    var w2 = TargetWidth
  
    var r = w1 / w2;
  
    var h1 = this.height;
    var h2 = this.height / r;
  
    canvas.width(TargetWidth).height(h2).css("background-image", "url("+src+")");

    setTimeout(function(){
      
      var jcanvas = canvas[0];
     var bounds = jcanvas.getBoundingClientRect();
    jcanvas.width = bounds.width;
    jcanvas.height = bounds.height;        
    var ctx = jcanvas.getContext('2d'),
    w = jcanvas.width,
    h = jcanvas.height;
    ctx.translate(-0.1, -0.1);
    jcanvas.onmousedown = function(e) {
      var rect = jcanvas.getBoundingClientRect();
      x3 = e.clientX - rect.left;
      y3 = e.clientY - rect.top;
  
      ctx.clearRect(0, 0, w, h)
      ctx.beginPath();
      ctx.arc(x3, y3, 5, 0, 2 * Math.PI, false);
      ctx.fillStyle = 'red';
      ctx.fill();
      ctx.stroke();
    }
      
    }, 500)
    
  }
  my_image.src = src;
}
.hotspot-canvas {
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
    cursor: crosshair;
    border: 1px solid black;
    background-repeat: no-repeat;
    background-position: center center;
    background-clip: border-box;
    background-origin: padding-box;
    -moz-background-size: cover;
    background-size: cover;
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

<canvas 
  data-id="554924"
  data-img="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQXXarydhE3CZRSMgXCProd1w0_oSwIOPd7zJN5EQmLtQtPDD21" 
  class="hotspot-canvas"
   data-responses="0" data-notes="0" data-actions="0" data-email="123" data-responder="true" data-response-id="" orig_type="14" data-tag="6bbf8f97-758f-47ca-8b8c-24a1cd3ddd55" data-formtemplatequestiontype="s" data-valueifparentna="" data-exportkey=""></canvas>

【讨论】:

    【解决方案2】:

    就像我在评论中提到的那样,我最近也遇到了同样的问题,所以我想分享我的解决方案。基本上发生的事情是你的画布元素的尺寸被缩放并且它的原点由于CSS而被翻译,但画布的内部分辨率仍然是相同的。此外,画布的边框和内边距也会影响getBoundingClientRect 返回的DOMRect 的属性。

    您需要从一些MouseEventTouchEvent 中获取clientXclientY 值、画布的内部分辨率以及显示的画布元素的尺寸和偏移量,包括边框和填充,以及使用它们来计算与画布内部坐标系相关的 xy 坐标。此代码需要 JQuery 和 ES6 支持,但如果您担心浏览器兼容性,可以使用 Babel 或其他工具进行编译。

    // translate coords in viewport to internal coords of canvas
    const translateCoords = ({clientX, clientY}) => {
        // dimensions of canvas element (top-left with respect to border-box)
        const bcr = canvas.getBoundingClientRect();
        // offsets from border and padding
        const sideWidth = ["top", "right", "bottom", "left"].reduce((sum, side) => {
            sum[side] = Object.values($(canvas).css([
                `border-${side}-width`,
                `padding-${side}`
            ])).reduce((a, b) => a + parseFloat(b), 0);
            return sum;
        }, {});
        // ratio of internal canvas resolution to canvas displayed dimensions
        const scaleX = canvas.width / (bcr.width - (sideWidth.left + sideWidth.right));
        const scaleY = canvas.height / (bcr.height - (sideWidth.top + sideWidth.bottom));
        // translate and scale screen coords to canvas internal coords
        const x = (clientX - (bcr.left + sideWidth.left)) * scaleX;
        const y = (clientY - (bcr.top + sideWidth.top)) * scaleY;
    
        return {x, y};
    };
    // example event listener
    canvas.addEventListener("click", (event) => {
        const {x, y} = translateCoords(event);
        // do something with translated coordinates
        console.log(x, y);
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-18
      • 2020-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多