Javascript 擦除之前在 HTML5 画布上绘制的线条

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7365436/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 01:58:57  来源:igfitidea点击:

Erasing previously drawn lines on an HTML5 canvas

javascripthtmlhtml5-canvas

提问by Saqib

To play around with HTML5 canvas, I decided to make an app which draws an analogue clockface. Everything's fine, except that old lines don't get erased in the way that I would expect. I've included part of the code below - DrawHands() gets called once a second:

为了玩转 HTML5 画布,我决定制作一个绘制模拟表盘的应用程序。一切都很好,除了旧的线条不会像我期望的那样被擦除。我已经包含了下面的部分代码 - DrawHands() 每秒被调用一次:

var hoursPoint = new Object();
var minutesPoint = new Object();
var secondsPoint = new Object();

function drawHands()
{
    var now = new Date();

    drawLine(centerX, centerY, secondsPoint.X, secondsPoint.Y, "white", 1);
    var seconds = now.getSeconds();
    secondsPoint = getOtherEndOfLine(centerX, centerY, 2 * Math.PI / 60 * seconds, 0.75 * radius);
    drawLine(centerX, centerY, secondsPoint.X, secondsPoint.Y, "black", 1);

    drawLine(centerX, centerY, minutesPoint.X, minutesPoint.Y, "white", 3);
    var minutes = now.getMinutes();
    minutesPoint = getOtherEndOfLine(centerX, centerY, 2 * Math.PI / 60 * minutes, 0.75 * radius);
    drawLine(centerX, centerY, minutesPoint.X, minutesPoint.Y, "black", 3);

    drawLine(centerX, centerY, hoursPoint.X, hoursPoint.Y, "white", 3);
    var hours = now.getHours();
    if (hours >= 12) { hours -= 12; } // Hours are 0-11
    hoursPoint = getOtherEndOfLine(centerX, centerY, (2 * Math.PI / 12 * hours) + (2 * Math.PI / 12 / 60 * minutes), 0.6 * radius);
    drawLine(centerX, centerY, hoursPoint.X, hoursPoint.Y, "black", 3);
}

To make sense of the above, there are two helper functions:

为了理解上述内容,有两个辅助函数:

  • drawLine(x1, y1, x2, y2, color, thickness)
  • getOtherEndOfLine(x, y, angle, length)
  • drawLine(x1, y1, x2, y2, 颜色, 粗细)
  • getOtherEndOfLine(x, y, 角度, 长度)

The problem is that while all the hands get drawn as expected in black, they never get erased. I would expect that since the same line is drawn in white (the background colour) it would effectively erase what was previously drawn at that point. But this doesn't seem to be the case.

问题是,虽然所有的手都按预期绘制成黑色,但它们永远不会被擦除。我希望因为同一条线是用白色(背景色)绘制的,所以它会有效地擦除之前在该点绘制的内容。但情况似乎并非如此。

Anything I'm missing?

我缺少什么吗?

采纳答案by ellisbben

For reasons that I could expand upon, you should consider clearing your canvas and redrawing it entirely unless there are performance or compositing reasons not to.

出于我可以扩展的原因,您应该考虑清除画布并完全重新绘制它,除非有性能或合成原因不这样做。

You want clearRect, something like this:

你想要clearRect,像这样:

//clear the canvas so we can draw a fresh clock
ctx.clearRect(0, 0, canvasWidth, canvasHeight);

//redraw your clock here
/* ... */

回答by mrmcgreg

Instead of erasing the things you don't want you can:

您可以:

  1. save the state of the canvas
  2. draw the things you don't want
  3. restore the canvas to the saved state to 'erase' them
  1. 保存画布的状态
  2. 画你不想要的东西
  3. 将画布恢复到保存状态以“擦除”它们

This can be accomplished pretty easily using ImageData:

这可以很容易地使用ImageData

var canvas = document.querySelector('canvas'),
    context = canvas.getContext('2d');

context.fillStyle = 'blue';
context.fillRect(0,0,200,200);

// save the state of  the canvas here
var imageData = context.getImageData(0,0,canvas.width,canvas.height);

// draw a red rectangle that we'll get rid of in a second
context.fillStyle = 'red';
context.fillRect(50,50,100,100);

setTimeout(function () {
    // return the canvas to the state right after we drew the blue rect
    context.putImageData(imageData, 0, 0);
}, 1000);
<canvas width=200 height=200>

回答by Simon Sarris

The reason you can't just redraw the line in white and hope for it to erase the old line is because there might be some anti-aliasing/bleeding. You'll also notice that a straight horizontal line drawn on a pixel versus a half-pixel looks very different because of this.

您不能只用白色重新绘制线条并希望它擦除旧线条的原因是因为可能存在一些抗锯齿/出血。您还会注意到,因此,在像素上绘制的水平直线与半像素上绘制的水平线看起来非常不同。

When you do your white "erase" lines, try drawing them with a larger lineWidthby about 3 or 4. That should work for your case.

当你做你的白色“擦除”线时,试着lineWidth用大约 3 或 4的大号来绘制它们。这应该适合你的情况。

You should also draw all of the white lines first, then all of the black lines, in case they intersect.

您还应该先绘制所有白线,然后再绘制所有黑线,以防它们相交。

回答by Joe

A quick and easy way to clear a canvas is to set the width:

清除画布的一种快速简便的方法是设置宽度:

context.canvas.width = context.canvas.width;

回答by Dominique Fortin

My solution is double buffering :

我的解决方案是双缓冲:

var shapes = 
  [{type:"circle", x:50, y:50, radious:40, lineWidth:2, strokeStyle:"#FF0000", fillStyle:"#800000"}
  ,{type:"rectangle", x:50, y:50, width:100, height: 100, lineWidth:2, strokeStyle:"#00FF00", fillStyle:"#008000"}
  ,{type:"line", x1:75, y1:100, x2:170, y2:75, lineWidth:3, strokeStyle:"#0000FF"}
  ];

step1();
setTimeout(function () {
  step2();
  setTimeout(function () {
    step3();
  }, 1000);
}, 1000);

function step1() {
  clearCanvas('myCanvas1');
  shapes.forEach((sh) => { drawShape('myCanvas1', sh); });
};

function step2() {
  clearCanvas('myCanvas2');
  shapes.pop();
  shapes.forEach((sh) => { drawShape('myCanvas2', sh); });
  showOtherCanvas('myCanvas2', 'myCanvas1');
};

function step3() {
  clearCanvas('myCanvas1');
  shapes.pop();
  shapes.forEach((sh) => { drawShape('myCanvas1', sh); });
  showOtherCanvas('myCanvas1', 'myCanvas2');
};

function showOtherCanvas(cnv1, cnv2) {
  var c1 = document.getElementById(cnv1);
  var c2 = document.getElementById(cnv2);
  
  c1.style['z-index'] = 3;
  c2.style['z-index'] = 1;
  c1.style['z-index'] = 2;
}

function clearCanvas(canvasID) {
  var canvas = document.getElementById(canvasID);
  var ctx = canvas.getContext('2d');
  
  ctx.fillStyle="#FFFFFF";
  ctx.fillRect(0,0,480,320);
} 

function drawShape (canvasID, info) {
  switch (info.type) {
    case "line"      : drawLine(canvasID, info);
    case "rectangle" : drawRectangle(canvasID, info);
    case "circle"    : drawCircle(canvasID, info);
  }
}

function drawLine (canvasID, info) {
  var canvas = document.getElementById(canvasID);
  var ctx = canvas.getContext('2d');
  
  ctx.strokeStyle = info.strokeStyle;
  ctx.lineWidth = info.lineWidth

  ctx.beginPath();
  ctx.moveTo(info.x1, info.y1);
  ctx.lineTo(info.x2, info.y2);
  ctx.stroke();
}

function drawRectangle (canvasID, info) {
  var canvas = document.getElementById(canvasID);
  var ctx = canvas.getContext('2d');
  
  ctx.fillStyle = info.fillStyle;
  ctx.strokeStyle = info.strokeStyle;
  ctx.lineWidth = info.lineWidth

  ctx.fillRect(info.x, info.y, info.width, info.height);
  ctx.strokeRect(info.x, info.y, info.width, info.height);
}

function drawCircle (canvasID, info) {
  var canvas = document.getElementById(canvasID);
  var ctx = canvas.getContext('2d');
  
  ctx.fillStyle = info.fillStyle;
  ctx.strokeStyle = info.strokeStyle;
  ctx.lineWidth = info.lineWidth

  ctx.beginPath();
  ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
  ctx.fill();

  ctx.beginPath();
  ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
  ctx.stroke();
}
<canvas id="myCanvas2" width="480" height="320"
 style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:1">
</canvas>
<canvas id="myCanvas1" width="480" height="320"
 style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:2">
</canvas>

The change is so fast you won't see any flicker.

变化如此之快,您将看不到任何闪烁。