如何清除画布以进行重绘

Posted

技术标签:

【中文标题】如何清除画布以进行重绘【英文标题】:How to clear the canvas for redrawing 【发布时间】:2011-01-09 16:42:16 【问题描述】:

在尝试了合成操作并在画布上绘制图像之后,我现在尝试删除图像并进行合成。我该怎么做?

我需要清除画布以重绘其他图像;这可能会持续一段时间,所以我不认为每次都绘制一个新矩形是最有效的选择。

【问题讨论】:

没有答案提到这个,但清晰的颜色是这个:canvas.style.backgroundColor = "lime"; 这里提到的所有方法的基准:measurethat.net/Benchmarks/Show/14723/2/… 【参考方案1】:

鉴于canvas 是一个画布元素或OffscreenCanvas object,

const context = canvas.getContext('2d');

context.clearRect(0, 0, canvas.width, canvas.height);

【讨论】:

请注意,对于clearRect,您需要有一个未转换的上下文,或者跟踪您的实际边界。 进一步注意,设置画布宽度 a) 需要将其设置为不同的值,然后再返回 for Webkit compatibility,b) 这将是 reset your context transformation。 不要使用宽度技巧。这是一个肮脏的黑客。在诸如 javascript 之类的高级语言中,您想要的是最好地说明您的意图,然后让较低级别的代码实现它们。将宽度设置为自身并说明您的真实意图,即清除画布。 一个完全次要的建议,但我建议context.clearRect(0, 0, context.canvas.width, context.canvas.height)。它实际上是一回事,但依赖项少了一个(1 个变量而不是 2 个) 对于此答案的最新读者:请注意,此答案已被修改,并且上述某些 cmets 不再适用【参考方案2】:

使用:context.clearRect(0, 0, canvas.width, canvas.height);

这是清除整个画布的最快、最具描述性的方法。

请勿使用:canvas.width = canvas.width;

重置canvas.width 重置所有画布状态(例如transformations、lineWidth、strokeStyle等),它非常慢(与clearRect相比),它不适用于所有浏览器,也不能描述你的确实在尝试做。

处理转换后的坐标

如果您修改了转换矩阵(例如使用scalerotatetranslate),那么context.clearRect(0,0,canvas.width,canvas.height) 可能不会清除画布的整个可见部分。

解决方案?在清除画布之前重置转换矩阵:

// Store the current transformation matrix
context.save();

// Use the identity matrix while clearing the canvas
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);

// Restore the transform
context.restore();

编辑: 我刚刚进行了一些分析,并且(在 Chrome 中)在不重置转换的情况下清除 300x150(默认大小)画布的速度大约快 10%。随着画布尺寸的增加,这种差异会减小。

这已经相对无关紧要了,但在大多数情况下,你会绘制比你清除更多的东西,我相信这种性能差异是无关紧要的。

100000 iterations averaged 10 times:
1885ms to clear
2112ms to reset and clear

【讨论】:

@YumYumYum:clear() 是标准函数吗?好像没有。 请注意,您可以通过使用 ctx.canvas 来消除对本地 canvas 变量的需求。 @DrewNoakes,好点,但我几乎总是选择单独的变量来提高速度。它非常小,但我尝试通过对常用属性(尤其是在动画循环内部)进行别名来避免取消引用时间。 AlexanderN 的演示由于损坏的图像链接不再起作用,已修复:jsfiddle.net/Ktqfs/50 在转换矩阵修改的情况下清除整个画布的另一种方法是简单地跟踪转换并在清除时将它们自己应用到canvas.widthcanvas.height。与重置转换相比,我没有对运行时差异进行任何数值分析,但我怀疑它会获得更好的性能。【参考方案3】:

如果你在画线,请确保不要忘记:

context.beginPath();

否则线条将不会被清除。

【讨论】:

我知道这已经有一段时间了,在这之后我发表评论可能很愚蠢,但这已经困扰了我一年... 致电beginPath当您开始一条新路径时,不要以为您正在清除画布就想清除现有路径!这将是一种可怕的做法,并且是一种倒退的方式来看待绘画。 如果您只想清除画布上的所有内容怎么办。假设您正在创建一个游戏,并且您需要每隔百分之一秒重绘一次屏幕。 @JoseQuinones, beginPath 不会清除画布上的任何内容,它会重置路径,以便在绘制之前删除先前的路径条目。您可能确实需要它,但作为绘图操作,而不是清除操作。清除,然后开始路径并绘制,不清除并开始路径,然后绘制。区别有意义吗?看这里的例子:w3schools.com/tags/canvas_beginpath.asp 这解决了我的动画随着时间的推移而变慢的问题。我确实在每一步都重新绘制了网格线,但没有清除上一步的线。【参考方案4】:

其他人已经很好地回答了这个问题,但是如果上下文对象上的一个简单的clear() 方法对你有用(对我来说),这是我根据这里的答案使用的实现:

CanvasRenderingContext2D.prototype.clear = 
  CanvasRenderingContext2D.prototype.clear || function (preserveTransform) 
    if (preserveTransform) 
      this.save();
      this.setTransform(1, 0, 0, 1, 0, 0);
    

    this.clearRect(0, 0, this.canvas.width, this.canvas.height);

    if (preserveTransform) 
      this.restore();
               
;

用法:

window.onload = function () 
  var canvas = document.getElementById('canvasId');
  var context = canvas.getContext('2d');

  // do some drawing
  context.clear();

  // do some more drawing
  context.setTransform(-1, 0, 0, 1, 200, 200);
  // do some drawing with the new transform
  context.clear(true);
  // draw more, still using the preserved transform
;

【讨论】:

这是一个很棒的实现。我完全支持增强本机原型,但您可能希望确保在分配之前未定义“清晰”——我仍然希望有一天能实现本机。 :) 你知道浏览器有多广泛地支持 CanvasRenderingContext2D 并让它“可写”吗? 感谢@Prestaul 的反馈 - 另外,没有浏览器会阻止您以这种方式扩展 javascript 对象。 @JonathanK,我很想看到一些关于清除和不重置转换之间的性能差异的分析。我猜如果你画的很少,差异会很明显,但是如果你画的不是微不足道的,那么清晰的步骤可以忽略不计......我可能需要稍后再测试一下,当我有更多时间时:) 好的,我进行了分析,我将在我的帖子中添加详细信息。【参考方案5】:

这是 2018 年,仍然没有本地方法可以完全清除画布以进行重绘。 clearRect() 完全清除画布。非填充类型的图纸不会被清除(例如rect()

1.无论您如何绘制,完全清除画布:

context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.beginPath();

优点:保留 strokeStyle、fillStyle 等;没有滞后;

缺点:如果您在绘制任何内容之前已经使用 beginPath,则没有必要

2.使用宽度/高度技巧:

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

context.canvas.height = context.canvas.height;

优点:适用于 IE 缺点:将 strokeStyle、fillStyle 重置为黑色;迟钝的;

我想知道为什么本地解决方案不存在。实际上,clearRect() 被认为是单行解决方案,因为大多数用户在绘制任何新路径之前都会执行beginPath()。虽然 beginPath 只在画线时使用,而不是像rect().这样的封闭路径

这就是为什么接受的答案没有解决我的问题,我最终浪费了数小时尝试不同的技巧。诅咒你的Mozilla

【讨论】:

我认为这是正确的答案,尤其是对于画布。无论我如何调用 clearRect,我都会遇到剩余的 context.stroke,而 beginPath 有帮助!【参考方案6】: Chrome 响应良好:@Pentium10 建议的context.clearRect ( x , y , w , h );,但 IE9 似乎完全忽略了此指令。 IE9 似乎响应:canvas.width = canvas.width; 但它不会清除线条,只是形状、图片和其他对象,除非您还使用@John Allsopp 的首先更改宽度的解决方案。

因此,如果您有这样创建的画布和上下文:

var canvas = document.getElementById('my-canvas');
var context = canvas.getContext('2d');

你可以使用这样的方法:

function clearCanvas(context, canvas) 
  context.clearRect(0, 0, canvas.width, canvas.height);
  var w = canvas.width;
  canvas.width = 1;
  canvas.width = w;

【讨论】:

请注意,该方法可以通过仅传入上下文并使用context.clearRect(0,0,context.canvas.width,context.canvas.height)来简化。 IE 9 应该绝对响应 clearRect 调用...(参见:msdn.microsoft.com/en-us/library/ff975407(v=vs.85).aspx)与更改 canvas.width 一样慢,唯一能让你变慢的方法是更改​​两次并调用clearRect 也是如此。 对不起,我应该更清楚...这种方法可以工作(只要您没有应用转换),但它是一种 [慢] 蛮力技术,没有必要.只需使用 clearRect ,因为它是最快的,并且应该可以在每个具有画布实现的浏览器中工作。 我相信 IE 正在重新绘制线条,因为它们仍在路径缓冲区中。我使用这种格式,效果很好。 function clearCanvas(ctx) ctx.beginPath(); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 在此之前我尝试了每一种方法......这是唯一有效的方法。我正在使用带有线条、矩形和文本的 Chrome……没想到做一些应该内置的东西会这么难!谢谢!【参考方案7】:

通过传递 x,y 坐标以及画布的高度和宽度来使用 clearRect 方法。 ClearRect 会将整个画布清除为:

canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);

【讨论】:

你可以把它放在一个方法中,每次你想刷新屏幕时调用它,正确的。 @XXX.xxx 请检查 html 中画布的 id 并在第一行使用相同的(通过 id 获取元素)【参考方案8】:

一个快速的方法是做

canvas.width = canvas.width

我知道它是如何工作的,但确实如此!

【讨论】:

哇。这确实有效,你是怎么找到这个的? @zipit 在正常清除未呈现后,我在 medium.com 上发现的快速技巧 :) 我正在拔头发,想弄清楚为什么会这样!感谢分享! 谁能解释一下为什么它会起作用,甚至 canvas.width+=0 也能起作用,这背后的科学原理是什么? 设置任何尺寸时,画布都会被重置。更具体地说,它们变成透明的黑色。【参考方案9】:

这里有很多很好的答案。 另一个注意事项是,有时只部分清除画布很有趣。 也就是说,“淡出”之前的图像,而不是完全擦除它。 这可以产生很好的轨迹效果。

这很容易。假设你的背景颜色是白色:

// assuming background color = white and "eraseAlpha" is a value from 0 to 1.
myContext.fillStyle = "rgba(255, 255, 255, " + eraseAlpha + ")";
myContext.fillRect(0, 0, w, h);

【讨论】:

我知道这是可能的,我对你的回答没有任何问题,我只是想知道,如果你有 2 个正在移动的物体,而你只想跟踪其中一个,你会怎么做那个? 这要复杂得多。在这种情况下,您应该创建一个屏幕外画布,并且每一帧都使用此处描述的部分擦除方法将您想要拖到该画布上的对象绘制出来,并且每一帧都将主画布清除 100%,绘制非拖尾对象,然后使用 drawImage() 将屏幕外画布合成到主画布上。您还需要将 globalCompositeOperation 设置为适合图像的内容。例如,“乘法”适用于浅色背景上的深色物体。【参考方案10】:

这是我使用的,不管边界和矩阵变换:

function clearCanvas(canvas) 
  const ctx = canvas.getContext('2d');
  ctx.save();
  ctx.globalCompositeOperation = 'copy';
  ctx.strokeStyle = 'transparent';
  ctx.beginPath();
  ctx.lineTo(0, 0);
  ctx.stroke();
  ctx.restore();

基本上,它保存上下文的当前状态,并以copyglobalCompositeOperation 绘制一个透明像素。然后,恢复之前的上下文状态。

【讨论】:

【参考方案11】:

这适用于我在 chart.js 中的饼图

<div class="pie_nut" id="pieChartContainer">
    <canvas id="pieChart"  ></canvas> 
</div>

$('#pieChartContainer').html(''); //remove canvas from container
$('#pieChartContainer').html('<canvas id="pieChart"  ></canvas>'); //add it back to the container

【讨论】:

【参考方案12】:

我发现在我测试的所有浏览器中,最快的方法是用白色或任何你想要的颜色填充矩形。我有一个非常大的显示器,在全屏模式下 clearRect 非常慢,但 fillRect 是合理的。

context.fillStyle = "#ffffff";
context.fillRect(0,0,canvas.width, canvas.height);

缺点是画布不再透明。

【讨论】:

【参考方案13】:

最短路径:

canvas.width += 0

【讨论】:

确定它的简短但也有点混乱,如果我阅读那行代码我不会想到它会清除画布 最短或最有效的方法不一定是最好的方法【参考方案14】:

我一直用

ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillRect(0, 0, canvas.width, canvas.height);

对于自定义颜色,并且

ctx.clearRect(0, 0, canvas.width, canvas.height);

用于在清除时使画布透明

【讨论】:

这是最简单的方法!好吧,至少对我来说。 不,它不起作用。 &lt;canvas&gt; 默认是透明的。它应该显示底层内容,以防它与其他 HTML 元素重叠。使用上面的代码,您假设画布的父级(很可能是&lt;body&gt;)具有白色背景。另一方面,ctx.clearRect(0, 0, canvas.width, canvas.height) 正确地完成了这项工作。 你是对的,我会编辑答案。完全忘记了那个方法! 我发现在您的示例中混合使用 cxtctx 有点令人沮丧。 @Wyck 编辑了它【参考方案15】:

在webkit中你需要将宽度设置为不同的值,然后你可以将它设置回初始值

【讨论】:

【参考方案16】:
function clear(context, color)

    var tmp = context.fillStyle;
    context.fillStyle = color;
    context.fillRect(0, 0, context.canvas.width, context.canvas.height);
    context.fillStyle = tmp;

【讨论】:

这比 clearRect(0,0,canvas.width,canvas.height) 慢【参考方案17】:

一个简单但不可读的方法是这样写:

var canvas = document.getElementId('canvas');

// after doing some rendering

canvas.width = canvas.width;  // clear the whole canvas

【讨论】:

【参考方案18】:
context.clearRect(0,0,w,h)   

用 RGBA 值填充给定的矩形: 0 0 0 0 : 使用 Chrome 0 0 0 255 : 带 FF 和 Safari

但是

context.clearRect(0,0,w,h);    
context.fillStyle = 'rgba(0,0,0,1)';  
context.fillRect(0,0,w,h);  

让矩形填充 0 0 0 255 不管是什么浏览器!

【讨论】:

【参考方案19】:
Context.clearRect(starting width, starting height, ending width, ending height);

示例:context.clearRect(0, 0, canvas.width, canvas.height);

【讨论】:

【参考方案20】:

我一直用这个

ctx.clearRect(0, 0, canvas.width, canvas.height)
window.requestAnimationFrame(functionToBeCalled)

注意

结合 clearRect 和 requestAnimationFrame 可以实现更流畅的动画,如果这是你想要的

【讨论】:

【参考方案21】:

最快的方式:

canvas = document.getElementById("canvas");
c = canvas.getContext("2d");

//... some drawing here

i = c.createImageData(canvas.width, canvas.height);
c.putImageData(i, 0, 0); // clear context by putting empty image data

【讨论】:

哇...在哪个浏览器中?在我的(OS X 上的 Chrome 22 和 Firefox 14)中,您的方法是迄今为止最慢的选择。 jsperf.com/canvas-clear-speed/9 >5 年后,这仍然是最慢的方法,比clearRect 慢约700 倍。【参考方案22】:

如果你只使用 clearRect,如果你有它在一个表单中提交你的绘图,你会得到一个提交而不是清除,或者可以先清除它然后上传一个无效的绘图,所以你需要在函数的开头添加 preventDefault:

   function clearCanvas(canvas,ctx) 
        event.preventDefault();
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    


<input type="button" value="Clear Sketchpad" id="clearbutton" onclick="clearCanvas(canvas,ctx);">

希望对某人有所帮助。

【讨论】:

【参考方案23】:

这是一个带有透明画布按钮的手绘画布。 查看这个 live example 可以在其上绘制的画布,并在需要时清除它以重新绘制 clearRect() 用于删除当前画布,fillRect() 用于再次绘制初始画布很干净,上面没有图纸。

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d"),
    painting = false,
    lastX = 0,
    lastY = 0,
    lineThickness = 1;

canvas.width=canvas.height = 250;
ctx.fillRect(0, 0, 250, 250);

canvas.onmousedown = function(e) 
    painting = true;
    ctx.fillStyle = "#ffffff";
    lastX = e.pageX - this.offsetLeft;
    lastY = e.pageY - this.offsetTop;
;

canvas.onmouseup = function(e)
    painting = false;


canvas.onmousemove = function(e) 
    if (painting) 
        mouseX = e.pageX - this.offsetLeft;
        mouseY = e.pageY - this.offsetTop;

        // find all points between        
        var x1 = mouseX,
            x2 = lastX,
            y1 = mouseY,
            y2 = lastY;


        var steep = (Math.abs(y2 - y1) > Math.abs(x2 - x1));
        if (steep)
            var x = x1;
            x1 = y1;
            y1 = x;

            var y = y2;
            y2 = x2;
            x2 = y;
        
        if (x1 > x2) 
            var x = x1;
            x1 = x2;
            x2 = x;

            var y = y1;
            y1 = y2;
            y2 = y;
        

        var dx = x2 - x1,
            dy = Math.abs(y2 - y1),
            error = 0,
            de = dy / dx,
            yStep = -1,
            y = y1;

        if (y1 < y2) 
            yStep = 1;
        

        lineThickness = 4;

        for (var x = x1; x < x2; x++) 
            if (steep) 
                ctx.fillRect(y, x, lineThickness , lineThickness );
             else 
                ctx.fillRect(x, y, lineThickness , lineThickness );
            

            error += de;
            if (error >= 0.5) 
                y += yStep;
                error -= 1.0;
            
        



        lastX = mouseX;
        lastY = mouseY;

    

var button=document.getElementById("clear");
button.onclick=function clearcanvas()
  canvas=document.getElementById("canvas"),
  ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, 250, 250);
canvas.width=canvas.height = 250;
ctx.fillRect(0, 0, 250, 250);
#clearborder-radius:10px;
font-size:8px !important;
position:absolute;
top:1px;
#canvasborder-radius:10px
<link href="https://www.w3schools.com/w3css/4/w3.css" rel="stylesheet"/>
<button id="clear" class="w3-padding w3-xxlarge w3-pink" type="button">Clear Canvas</button>
<canvas id="canvas"></canvas>

【讨论】:

【参考方案24】:
private clearCanvas() 
  const canvas: HTMLCanvasElement = this.ctx.canvas
  this.ctx.save()
  this.ctx.setTransform(1, 0, 0, 1, 0, 0)
  this.ctx.clearRect(0, 0, canvas.width, canvas.height)
  this.ctx.restore()

【讨论】:

【参考方案25】:

这些都是如何清除标准画布的好例子,但如果你使用的是 paperjs,那么这将起作用:

在 JavaScript 中定义一个全局变量:

var clearCanvas = false;

根据您的 PaperScript 定义:

function onFrame(event)
    if(clearCanvas && project.activeLayer.hasChildren())
        project.activeLayer.removeChildren();
        clearCanvas = false;
    

现在无论您将 clearCanvas 设置为 true,它都会清除屏幕上的所有项目。

【讨论】:

以上是关于如何清除画布以进行重绘的主要内容,如果未能解决你的问题,请参考以下文章

Web前端性能优化-重绘与回流

Android 4+ html5画布不重绘

vue基本知识点

如何及时重绘画布?

实现没有饥饿的while循环以在wpf中重绘图像

JPanel 图形清除和重绘?