ChatGPT解决这个技术问题 Extra ChatGPT

How to clear the canvas for redrawing

After experimenting with composite operations and drawing images on the canvas I'm now trying to remove images and compositing. How do I do this?

I need to clear the canvas for redrawing other images; this can go on for a while so I don't think drawing a new rectangle every time will be the most efficient option.

No answer mentions this, but the clear color is this one: canvas.style.backgroundColor = "lime";
Benchmarks for all mentioned methods here: measurethat.net/Benchmarks/Show/14723/2/…

D
D. Pardal

Given that canvas is a canvas element or an OffscreenCanvas object,

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

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

Note that for clearRect you need to either have an untransformed context, or keep track of your actual boundaries.
Note further that setting the canvas width a) needs to set it to a different value and then back again for Webkit compatibility, and b) this will reset your context transformation.
Don't use the width trick. It's a dirty hack. In a high level language such as JavaScript what you want is to best state your intentions, and then let the lower level code fulfill them. Setting the width to itself does not state your true intention, which is to clear the canvas.
A totally minor suggesting but I'd suggest context.clearRect(0, 0, context.canvas.width, context.canvas.height). It's effectively the same thing but one less dependency (1 variable instead of 2)
For more recent readers of this answer: be aware that this answer was modified and that some of the above comments no longer apply.
P
Prestaul

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

This is the fastest and most descriptive way to clear the entire canvas.

Do not use: canvas.width = canvas.width;

Resetting canvas.width resets all canvas state (e.g. transformations, lineWidth, strokeStyle, etc.), it is very slow (compared to clearRect), it doesn't work in all browsers, and it doesn't describe what you are actually trying to do.

Dealing with transformed coordinates

If you have modified the transformation matrix (e.g. using scale, rotate, or translate) then context.clearRect(0,0,canvas.width,canvas.height) will likely not clear the entire visible portion of the canvas.

The solution? Reset the transformation matrix prior to clearing the canvas:

// 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();

Edit: I've just done some profiling and (in Chrome) it is about 10% faster to clear a 300x150 (default size) canvas without resetting the transform. As the size of your canvas increases this difference drops.

That is already relatively insignificant, but in most cases you will be drawing considerably more than you are clearing and I believe this performance difference be irrelevant.

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

@YumYumYum: Is clear() a standard function? It doesn't seem to be.
Note that you can remove the need for a local canvas variable by using ctx.canvas instead.
@DrewNoakes, good point, but I almost always opt for separate variables for speed purposes. It is extremely minor, but I try to avoid dereferencing time by aliasing frequently used properties (especially inside of an animation loop).
AlexanderN's demo didn't work anymore due to broken image link, fixed: jsfiddle.net/Ktqfs/50
Another way to clear the entire canvas in the event of transformation matrix modification is to simply keep track of the transformations and apply them yourself to canvas.width and canvas.height when clearing. I haven't done any numerical analysis on the runtime difference compared to resetting the transform, but I suspect it would eek out a little better performance.
t
trembl

If you are drawing lines, make sure you don't forget:

context.beginPath();

Otherwise the lines won't get cleared.


I know this has been here for a while and it is probably silly for me to comment after all this time, but this has been bothering me for a year... Call beginPath when you begin a new path, do not assume that just because you are clearing the canvas you want to clear your existing path as well! This would be a horrible practice and is a backwards way of looking at drawing.
What if you just want to clear the canvas of everything. Lets say that you are creating a game and that you need to redraw the screen every so many hundredths of a second.
@JoseQuinones, beginPath does not clear anything off of your canvas, it resets the path so that previous path entries are removed before you draw. You probably did need it, but as a drawing operation, not a clearing operation. clear, then beginPath and draw, not clear and beginPath, then draw. Does the difference make sense? Look here for an example: w3schools.com/tags/canvas_beginpath.asp
This solved the slowdown of my animation I experienced over time. I did redraw grid lines on every step, but without clearing the lines from the previous step.
J
JonathanK

Others have already done an excellent job answering the question but if a simple clear() method on the context object would be useful to you (it was to me), this is the implementation I use based on answers here:

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();
    }           
};

Usage:

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
};

This is a great implementation. I fully support enhancing native prototypes, but you might want to ensure that "clear" is not defined before assigning it - I'm still hoping for a native implementation some day. :) Do you know how broadly browsers support CanvasRenderingContext2D and leave it "writable"?
Thanks for the feedback @Prestaul - also, no browser should prevent you from extending javascript objects in this manner.
@JonathanK, I'd love to see some profiling of the performance difference between clearing with and without resetting the transformation. I'm guessing that the difference will be apparent if you are doing little drawing but that if what you are drawing is not trivial then the clear step is negligible... I may have to test that later when I have more time :)
Ok, I did the profiling and I'm going to add the details to my post.
@Prestaul 2022 and still no native implementation of context.clear()... Yet, in the prototype enhancement, I wouldn't check whether clear() is available or not. Imagine one day the method is available, it then silently "overwrites" your custom method and might introduce hard-to-find bugs due to a different possible implementation.
s
sziraqui

This is 2018 and still there is no native method to completely clear canvas for redrawing. clearRect() does not clear the canvas completely. Non-fill type drawings are not cleared out (eg. rect())

1.To completely clear canvas irrespective of how you draw:

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

Pros: Preserves strokeStyle, fillStyle etc.; No lag;

Cons: Unnecessary if you are already using beginPath before drawing anything

2.Using the width/height hack:

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

OR

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

Pros: Works with IE Cons: Resets strokeStyle, fillStyle to black; Laggy;

I was wondering why a native solution does not exist. Actually, clearRect() is considered as the single line solution because most users do beginPath() before drawing any new path. Though beginPath is only to be used while drawing lines and not closed path like rect().

This is the reason why the accepted answer did not solve my problem and I ended up wasting hours trying different hacks. Curse you mozilla


I think this is the right answer, especially for drawing canvas. I suffered from remaining context.stroke no matter how I call clearRect, while beginPath helped!
g
grenade

Chrome responds well to: context.clearRect ( x , y , w , h ); as suggested by @Pentium10 but IE9 seems to completely ignore this instruction.

IE9 seems to respond to: canvas.width = canvas.width; but it doesn't clear lines, just shapes, pictures and other objects unless you also use @John Allsopp's solution of first changing the width.

So if you have a canvas and context created like this:

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

You can use a method like this:

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

Note that the method can be simplified by passing in only the context and using context.clearRect(0,0,context.canvas.width,context.canvas.height).
IE 9 should absolutely respond to a clearRect call... (See: msdn.microsoft.com/en-us/library/ff975407(v=vs.85).aspx) As slow as changing canvas.width is, the only way you could get slower is by changing it twice and calling clearRect as well.
Sorry, I should be more clear... This method will work (as long as you haven't applied a transform), but is a [slow] brute force technique where it isn't necessary. Just use clearRect as it is fastest and should work across every browser with a canvas implementation.
I believe IE is redrawing the lines due to them being in the path buffer still. I use this format and it works perfect. function clearCanvas(ctx) { ctx.beginPath(); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); }
I tried each and every method before this... and this was the only one that worked. I am using Chrome with lines, rectangles and text... wouldn't had thought it would be so hard to do something that should be built in! Thanks!
V
Vishwas

Use clearRect method by passing x,y co-ordinates and height and width of canvas. ClearRect will clear whole canvas as :

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

You could just put this in a method and call it every time that you want to refresh the screen, correct.
@ XXX.xxx plz check id of canvas in your html and use same for first line (to get element by id)
J
Jacob Morris

A quick way is to do

canvas.width = canvas.width

Idk how it works but it does!


Wow. This really does work, How did you ever find this?
@zipit Quick trick I found off of medium.com after normal clearing wasn't rendering :)
I am pulling my hair out trying to figure out why this works! Thanks for sharing!
Can anyone explain why that does work and even canvas.width+=0 does the job too, whats the freaking science behind of this?
Canvases are being reset when any dimension is set. To be more specific, they become transparent black.
o
orion elenzil

there are a ton of good answers here. one further note is that sometimes it's fun to only partially clear the canvas. that is, "fade out" the previous image instead of erasing it entirely. this can give nice trails effects.

it's easy. supposing your background color is white:

// 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);

I know it's possible and I have no problem with your answer, I just wonder, if you have 2 objects that are moving, and you only want to trail one of them, how would you go about doing that?
that's a good deal more complex. in that case, you should create an off-screen canvas, and each frame draw the objects you want trailed onto that canvas using the partial-erase method described here, and also each frame clear the main canvas 100%, draw the non-trailed objects, and then composite the off-screen canvas onto the main one using drawImage(). You'll also need to set the globalCompositeOperation to something appropriate for the images. eg "multiply" would work well for dark objects on a light background.
s
superruzafa

This is what I use, regardless boundaries and matrix transformations:

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();
}

Basically, it saves the current state of the context, and draws a transparent pixel with copy as globalCompositeOperation. Then, restores the previous context state.


o
object-Object

I always use

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

For a custom color, and

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

For making the canvas transparent when clearing


this is the easiest way! Well, for me at least.
No, it does not do the job. <canvas> is transparent by default. It should show the underlying content in case it overlaps with other HTML elements. With your code above, you assumed the parent of the canvas (most likely <body>) has a white background. On the other hand ctx.clearRect(0, 0, canvas.width, canvas.height) does the job correctly.
You are correct, I will edit the answer. Completely forgot about that method!
I find the mix of cxt and ctx in your example a bit frustrating.
@Wyck edited it
S
Sanal S

This worked for my pieChart in chart.js

<div class="pie_nut" id="pieChartContainer">
    <canvas id="pieChart" height="5" width="6"></canvas> 
</div>

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

J
John Page

I have found that in all browsers I test, the fastest way is to actually fillRect with white, or whataever color you would like. I have a very large monitor and in full screen mode the clearRect is agonizingly slow, but the fillRect is reasonable.

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

The drawback is that the canvas is no longer transparent.


Y
Yukulélé

the shortest way:

canvas.width += 0

sure its short but also a bit confusing, if im reading that line of code i wouldnt think of it clearing the canvas
the shortest or most efficient way doesnt have to be the best way
I see this in the W3C example above this line and I think this way will be more efficient than the clear rect. it will be similar to canvas.width = canvas.width
J
John Allsopp

in webkit you need to set the width to a different value, then you can set it back to the initial value


I
Imagine Breaker
function clear(context, color)
{
    var tmp = context.fillStyle;
    context.fillStyle = color;
    context.fillRect(0, 0, context.canvas.width, context.canvas.height);
    context.fillStyle = tmp;
}

This is slower than clearRect(0,0,canvas.width,canvas.height)
C
Chang

A simple, but not very readable way is to write this:

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

// after doing some rendering

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

d
danday74
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()
}

F
FelixSFD
Context.clearRect(starting width, starting height, ending width, ending height);

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


A
Anthony Gedeon

I always use this

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

NOTE

combining clearRect and requestAnimationFrame allows for more fluid animation if that is what you're going for


e
elser

fastest way:

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

wow... In which browser? In mine (Chrome 22 and Firefox 14 on OS X) your method is, by far, the slowest option. jsperf.com/canvas-clear-speed/9
>5 years in the future, this is still the slowest method by a very large amount, ~700x slower than clearRect.
J
JoelBonetR

If you use clearRect only, if you have it in a form to submit your drawing, you'll get a submit instead the clearing, or maybe it can be cleared first and then upload a void drawing, so you'll need to add a preventDefault at the beggining of the function:

   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);">

Hope it helps someone.


3
3 revs

This is a Free hand drawing Canvas with a Clear Canvas Button.
See this live example of a canvas which you can draw on and also when required clear it for redrawing clearRect() is used to delete the prersent canvas and fillRect() is used to again draw the initial canvas which was clean and had no drawings on it.

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);} #clear{border-radius:10px; font-size:8px !important; position:absolute; top:1px;} #canvas{border-radius:10px}


r
rmtheis

These are all great examples of how you clear a standard canvas, but if you are using paperjs, then this will work:

Define a global variable in JavaScript:

var clearCanvas = false;

From your PaperScript define:

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

Now wherever you set clearCanvas to true, it will clear all the items from the screen.