HTML5画布在调整大小时的重新绘制

10

我有两个画布元素,需要在按钮点击时调整它们的大小。

<div class="sDetails"><div>
                        <div id="canvasDiv" style="width: 310px;"><canvas id="canvasGraph"></canvas></div></div>
<div class="kDetails"><div><div>
<div id="canvasDiv" style="width: 310px; height: 240px;"><canvas id="canvasGraph"></canvas></div></div>

和脚本:

   var sketch;var sketch_sl;var onPaint;var canvas=null;var ctx=null;var tmp_ctx=null;
    function drawCanvas(div) {
        canvas = document.querySelector(div + " #canvasGraph");
        ctx = canvas.getContext('2d');
        sketch = document.querySelector(div + " #canvasDiv");
        sketch_sl = getComputedStyle(sketch);
        canvas.width = parseInt(sketch_style.getPropertyValue('width'));
        canvas.height = parseInt(sketch_style.getPropertyValue('height'));
        tmp_canvas = document.createElement('canvas');
        tmp_ctx = tmp_canvas.getContext('2d');
        tmp_canvas.id = 'tmp_canvas';
        tmp_canvas.width = canvas.width;
        tmp_canvas.height = canvas.height;
        sketch.appendChild(tmp_canvas);

重绘函数:

// here I must redraw my lines resized 2 times ( *cScale ) where cScale=2 or =1
function drawScales(ctx, canvas) 
        ctx.strokeStyle = 'green';
        ctx.fillStyle = 'green';
        ctx.beginPath();
        ctx.moveTo(5, 0);
        ctx.lineTo(0, canvas.height);
        scaleStep = 24*cScale;
由于某些原因它的表现非常糟糕,旧位置仍然存在。 有没有一种方法可以完全删除整个画布并追加它或者完全重新绘制它?
我尝试了canvas.width=canvas.width,尝试了ctx.clearRect(0, 0, canvas.width, canvas.height);tmp_ctx.clearRect(0, 0, canvas.width, canvas.height);,尝试了$(".sDetails #canvasGraph")[0].reset(); 从逻辑上讲,drawCanvas(".sDetails");drawLines(ctx, canvas);应该从头开始重绘,但实际上不会这样做。

为什么需要在调整大小时重新绘制画布?它的尺寸是否真的取决于窗口尺寸?如果是这样,那为什么要在 canvas 上使用 CSS 样式设置固定的尺寸(宽度/高度)? - hindmost
我有一个坐标系统,当按下按钮时,画布会变宽两倍,使得用户在其上绘制时可以更精确地捕捉x坐标。 - CodeGust
3个回答

11

调整画布元素的widthheight并使用context.scale以重新绘制原始图形,使其符合新缩放大小。

  • 调整画布元素将自动清除画布上所有的绘图。

  • 调整大小还会自动将所有上下文属性重置为默认值。

  • 使用context.scale是有用的,因为这样画布会自动重新缩放原始图形以适应新大小的画布。

  • 重要提示:画布不会自动重新绘制原始图形...必须重新发出原始绘图命令。

使用相同大小的两个画布进行说明(它们的大小由范围控件控制)

enter image description here

左侧画布调整尺寸变大的说明

enter image description here

右侧画布调整尺寸变大的说明

enter image description here

这里提供示例代码和演示。此演示使用范围元件来控制调整大小,但您也可以在window.onresize内部进行调整大小+重绘。

var canvas1=document.getElementById("canvas1");
var ctx1=canvas1.getContext("2d");
var canvas2=document.getElementById("canvas2");
var ctx2=canvas2.getContext("2d");
var originalWidth=canvas1.width;
var originalHeight=canvas1.height;

var scale1=1;
var scale2=1;

$myslider1=$('#myslider1');
$myslider1.attr({min:50,max:200}).val(100);
$myslider1.on('input change',function(){
  var scale=parseInt($(this).val())/100;
  scale1=scale;
  redraw(ctx1,scale);
});
$myslider2=$('#myslider2');
$myslider2.attr({min:50,max:200}).val(100);
$myslider2.on('input change',function(){
  var scale=parseInt($(this).val())/100;
  scale2=scale;
  redraw(ctx2,scale);
});

draw(ctx1);
draw(ctx2);

function redraw(ctx,scale){

  // Resizing the canvas will clear all drawings off the canvas
  // Resizing will also automatically clear the context
  // of all its current values and set default context values
  ctx.canvas.width=originalWidth*scale;
  ctx.canvas.height=originalHeight*scale;

  // context.scale will scale the original drawings to fit on
  // the newly resized canvas
  ctx.scale(scale,scale);

  draw(ctx);

  // always clean up! Reverse the scale
  ctx.scale(-scale,-scale);

}

function draw(ctx){
  // note: context.scale causes canvas to do all the rescaling
  //       math for us, so we can always just draw using the 
  //       original sizes and x,y coordinates
  ctx.beginPath();
  ctx.moveTo(150,50);
  ctx.lineTo(250,150);
  ctx.lineTo(50,150);
  ctx.closePath();
  ctx.stroke();
  ctx.fillStyle='skyblue';
  ctx.beginPath();
  ctx.arc(150,50,20,0,Math.PI*2);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(250,150,20,0,Math.PI*2);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
  ctx.beginPath();;
  ctx.arc(50,150,20,0,Math.PI*2);
  ctx.fill();
  ctx.stroke();
}

$("#canvas1, #canvas2").mousemove(function(e){handleMouseMove(e);});
var $mouse=$('#mouse');

function handleMouseMove(e){

  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();


  var bb=e.target.getBoundingClientRect();
  mouseX=parseInt(e.clientX-bb.left);
  mouseY=parseInt(e.clientY-bb.top);

  if(e.target.id=='canvas1'){
    $mouse.text('Mouse1: '+mouseX/scale1+' / '+mouseY/scale1+' (scale:'+scale1+')');
  }else{
    $mouse.text('Mouse2: '+mouseX/scale2+' / '+mouseY/scale2+' (scale:'+scale2+')');
  }

}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div>Resize left canvas</div>
<input id=myslider1 type=range><br>
<div>Resize right canvas</div>
<input id=myslider2 type=range><br>
<h4 id=mouse>Mouse coordinates:</h4>
<canvas id="canvas1" width=300 height=300></canvas>
<canvas id="canvas2" width=300 height=300></canvas>


谢谢!虽然我不确定缩放对我是否有效。我有一个坐标系统,当按下按钮时,画布变为两倍宽,使用户在其上绘制时更加精确地捕获x坐标。 如果我进行缩放,它会根据缩放后的画布采取坐标。也就是说,如果x值为3,捕获点为12px,则缩放后必须为3和24px。缩放后是否能正常工作? - CodeGust
1
当然!浏览器始终以未缩放的值报告鼠标坐标。要调整缩放画布,只需将浏览器的鼠标坐标除以当前画布缩放因子即可。这会给你以缩放画布术语表示的鼠标坐标。mouseX /= scale; and mouseY /= scale; - markE
1
@CodeGust。我在上面的评论中说错了话。我想说的是将浏览器报告的鼠标坐标除以缩放因子。我已经添加了演示,展示了如何从缩放的画布中正确计算鼠标坐标。 - markE

4
如果您需要比例无关的位置,可以使用规范化的值([0, 1]),并将画布的大小作为比例因子。这样,您就可以缩放和存储值,而不必过多考虑实际目标大小。
您还可以几乎直接使用鼠标位置,并通过仅将它们除以画布大小来规范化。
例如:
在渲染时,(1,1) 点始终会在右下角绘制,就像这样:(1 * canvas.width, 1 * canvas.height)。
当您存储一个点时,您将使用鼠标位置并将其除以画布尺寸。例如,如果我点击大小为400x200的画布的右下角,则点将是400/400 = 1,200/200 = 1。
请注意,宽度和高度将是排他性的(即 width-1 等),但为了简单起见...
示例:
在此示例中,您可以从任何大小的画布开始,绘制规范化的点,更改画布大小,并相对于原始位置按比例重新绘制点。

var rng = document.querySelector("input"),
    c = document.querySelector("canvas"),
    ctx = c.getContext("2d"),
    points = [];

// change canvas size and redraw all points
rng.onchange = function() {
  c.width = +this.value;
  render();
};

// add a new normalized point to array
c.onclick = function(e) {
  var r = this.getBoundingClientRect(),   // to adjust mouse position
      x = e.clientX - r.left,
      y = e.clientY - r.top;
  points.push({
    x: x / c.width,                       // normalize value to range [0, 1]
    y: y / c.height
  });                                     // store point
  render();                               // redraw (for demo)
};

function render() {
  ctx.clearRect(0, 0, c.width, c.height); // clear canvas
  ctx.beginPath();                        // clear path
  for(var i = 0, p; p = points[i]; i++) { // draw points as fixed-size circles
    var x = p.x * c.width,                // normalized to absolute values
        y = p.y * c.height;
    
    ctx.moveTo(x + 5, y);
    ctx.arc(x, y, 5, 0, 6.28);
    ctx.closePath();
  }
  ctx.stroke();
}
canvas {background:#ddd}
<h3>Click on canvas to add points, then resize</h3>
<label>Width: <input type="range" min=50 max=600 value=300></label><br>
<canvas></canvas>


1
我决定使用一个比例变量来调整我的比例尺。我改变画布大小 canvas.width *= 2; 然后重新绘制我的比例尺。 < p > var scaleStep;

并将其添加到代码中:ctx.lineTo(12*24*cScale+12, canvas.height-24); 需要进行缩放的地方。 当最大化画布时,scaleStep为2,返回到原始大小时为1。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接