So löschen Sie die Leinwand zum Neuzeichnen

1012

Nachdem ich mit zusammengesetzten Operationen experimentiert und Bilder auf die Leinwand gezeichnet habe, versuche ich nun, Bilder zu entfernen und zusammenzusetzen. Wie mache ich das?

Ich muss die Leinwand löschen, um andere Bilder neu zu zeichnen. Dies kann eine Weile dauern, daher denke ich nicht, dass das Zeichnen eines neuen Rechtecks ​​jedes Mal die effizienteste Option ist.

Richard
quelle

Antworten:

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

context.clearRect(0, 0, canvas.width, canvas.height);
Pentium10
quelle
42
Beachten Sie, dass clearRectSie entweder einen nicht transformierten Kontext haben oder Ihre tatsächlichen Grenzen verfolgen müssen.
Phrogz
7
Beachten Sie außerdem, dass das Festlegen der Zeichenflächenbreite a) aus Gründen der Webkit-Kompatibilität auf einen anderen Wert und dann wieder zurück festgelegt werden muss. B) Dadurch wird Ihre Kontextumwandlung zurückgesetzt .
Phrogz
45
Verwenden Sie nicht den Breitentrick. Es ist ein schmutziger Hack. In einer höheren Sprache wie JavaScript möchten Sie Ihre Absichten am besten darlegen und sie dann vom Code der unteren Ebene erfüllen lassen. Wenn Sie die Breite auf sich selbst einstellen, wird nicht Ihre wahre Absicht angegeben, nämlich die Leinwand zu löschen.
Andrewrk
22
Ein ganz kleiner Vorschlag, aber ich würde vorschlagen context.clearRect(0, 0, context.canvas.width, context.canvas.height). Es ist
praktisch
6
Für neuere Leser dieser Antwort: Beachten Sie, dass diese Antwort geändert wurde und einige der oben genannten Kommentare nicht mehr zutreffen .
Daveslab
719

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

Dies ist der schnellste und aussagekräftigste Weg, um die gesamte Leinwand zu löschen.

Verwende nicht: canvas.width = canvas.width;

Durch das canvas.widthZurücksetzen wird der gesamte Canvas-Status zurückgesetzt (z. B. Transformationen, Zeilenbreite, Strichstil usw.). Dies ist sehr langsam (im Vergleich zu clearRect), funktioniert nicht in allen Browsern und beschreibt nicht, was Sie tatsächlich versuchen.

Umgang mit transformierten Koordinaten

Wenn Sie die Transformationsmatrix geändert haben (zB mit scale, rotateoder translate) , danncontext.clearRect(0,0,canvas.width,canvas.height) wird wahrscheinlich nicht den gesamten sichtbaren Teil der Leinwand löschen.

Die Lösung? Setzen Sie die Transformationsmatrix zurück, bevor Sie die Zeichenfläche löschen:

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

Bearbeiten: Ich habe gerade ein Profil erstellt und (in Chrome) ist es ungefähr 10% schneller, eine Leinwand im Format 300 x 150 (Standardgröße) zu löschen, ohne die Transformation zurückzusetzen. Mit zunehmender Größe Ihrer Leinwand nimmt dieser Unterschied ab.

Das ist bereits relativ unbedeutend, aber in den meisten Fällen zeichnen Sie erheblich mehr als Sie löschen, und ich halte diesen Leistungsunterschied für irrelevant.

100000 iterations averaged 10 times:
1885ms to clear
2112ms to reset and clear
Prestaul
quelle
4
@YumYumYum: Ist clear () eine Standardfunktion? Es scheint nicht zu sein.
Nobar
7
Beachten Sie, dass Sie die Notwendigkeit einer lokalen canvasVariablen entfernen können, indem Sie ctx.canvasstattdessen verwenden.
Drew Noakes
1
@DrewNoakes, guter Punkt, aber ich entscheide mich fast immer für separate Variablen aus Geschwindigkeitsgründen. Es ist extrem geringfügig, aber ich versuche, die Dereferenzierungszeit durch Aliasing häufig verwendeter Eigenschaften (insbesondere innerhalb einer Animationsschleife) zu vermeiden.
Prestaul
AlexanderNs Demo funktionierte nicht mehr wegen defekter Bildverknüpfung
Domino
Eine andere Möglichkeit, die gesamte Zeichenfläche im Falle einer Änderung der Transformationsmatrix zu löschen, besteht darin, die Transformationen einfach zu verfolgen und sie selbst auf canvas.widthund canvas.heightbeim Löschen anzuwenden . Ich habe keine numerische Analyse des Laufzeitunterschieds im Vergleich zum Zurücksetzen der Transformation durchgeführt, aber ich vermute, dass dies zu einer etwas besseren Leistung führen würde.
NanoWizard
226

Wenn Sie Linien zeichnen, vergessen Sie nicht:

context.beginPath();

Andernfalls werden die Zeilen nicht gelöscht.

Zittern
quelle
10
Ich weiß, dass dies schon eine Weile hier ist und es wahrscheinlich albern für mich ist, nach all der Zeit einen Kommentar abzugeben, aber das hat mich seit einem Jahr gestört ... Rufen beginPathSie an, wenn Sie einen neuen Weg beginnen , nehmen Sie das nicht an, nur weil Sie Löschen Sie die Leinwand, auf der Sie auch Ihren vorhandenen Pfad löschen möchten! Dies wäre eine schreckliche Praxis und eine rückwärts gerichtete Sichtweise auf das Zeichnen.
Prestaul
Was ist, wenn Sie nur die Leinwand von allem befreien möchten? Nehmen wir an, Sie erstellen ein Spiel und müssen den Bildschirm alle hundertstel Sekunden neu zeichnen.
1
@JoseQuinones löscht beginPathnichts von Ihrer Zeichenfläche, setzt den Pfad zurück, sodass vorherige Pfadeinträge entfernt werden, bevor Sie zeichnen. Sie haben es wahrscheinlich gebraucht, aber als Zeichenvorgang, nicht als Löschvorgang. klar, dann beginPath und zeichne, nicht clear und beginPath, dann zeichne. Ist der Unterschied sinnvoll? Schauen Sie hier für ein Beispiel: w3schools.com/tags/canvas_beginpath.asp
Prestaul
1
Dies löste die Verlangsamung meiner Animation, die ich im Laufe der Zeit erlebte. Ich habe bei jedem Schritt Gitterlinien neu gezeichnet, ohne jedoch die Linien aus dem vorherigen Schritt zu löschen.
Georg Patscheider
118

Andere haben die Frage bereits hervorragend beantwortet, aber wenn eine einfache clear()Methode für das Kontextobjekt für Sie nützlich wäre (für mich), ist dies die Implementierung, die ich basierend auf den Antworten hier verwende:

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

Verwendungszweck:

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
};
JonathanK
quelle
6
Dies ist eine großartige Implementierung. Ich unterstütze die Verbesserung nativer Prototypen voll und ganz, aber Sie möchten vielleicht sicherstellen, dass "clear" nicht definiert ist, bevor Sie es zuweisen - ich hoffe immer noch auf eine native Implementierung eines Tages. :) Wissen Sie, wie allgemein Browser CanvasRenderingContext2D unterstützen und es "beschreibbar" lassen?
Prestaul
Vielen Dank für das Feedback @Prestaul - auch kein Browser sollte Sie daran hindern, Javascript-Objekte auf diese Weise zu erweitern.
JonathanK
@JonathanK, ich würde gerne ein Profil des Leistungsunterschieds zwischen Löschen mit und ohne Zurücksetzen der Transformation sehen. Ich vermute, dass der Unterschied offensichtlich sein wird, wenn Sie wenig zeichnen, aber wenn das, was Sie zeichnen, nicht trivial ist, ist der klare Schritt vernachlässigbar ... Ich muss das möglicherweise später testen, wenn ich mehr Zeit habe :)
Prestaul
Ok, ich habe die Profilerstellung durchgeführt und werde die Details zu meinem Beitrag hinzufügen.
Prestaul
35
  • Chrome reagiert gut auf: context.clearRect ( x , y , w , h );wie von @ Pentium10 vorgeschlagen, aber IE9 scheint diese Anweisung vollständig zu ignorieren.
  • IE9 scheint zu reagieren: canvas.width = canvas.width;Es werden jedoch keine Linien, sondern nur Formen, Bilder und andere Objekte gelöscht, es sei denn, Sie verwenden auch die Lösung von @John Allsopp, bei der zuerst die Breite geändert wird.

Wenn Sie also eine Leinwand und einen Kontext wie folgt erstellt haben:

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

Sie können eine Methode wie die folgende verwenden:

function clearCanvas(context, canvas) {
  context.clearRect(0, 0, canvas.width, canvas.height);
  var w = canvas.width;
  canvas.width = 1;
  canvas.width = w;
}
Granate
quelle
13
Beachten Sie, dass die Methode vereinfacht werden kann, indem nur der Kontext übergeben und verwendet wird context.clearRect(0,0,context.canvas.width,context.canvas.height).
Phrogz
5
IE 9 sollte unbedingt auf einen clearRect-Aufruf reagieren ... (Siehe: msdn.microsoft.com/en-us/library/ff975407(v=vs.85).aspx ) So langsam wie das Ändern der canvas.width ist, ist der einzige Weg Sie könnten langsamer werden, indem Sie es zweimal ändern und auch clearRect aufrufen.
Prestaul
1
Entschuldigung, ich sollte klarer sein ... Diese Methode funktioniert (solange Sie keine Transformation angewendet haben), ist jedoch eine [langsame] Brute-Force-Technik, bei der dies nicht erforderlich ist. Verwenden Sie einfach clearRect, da es am schnellsten ist und in jedem Browser mit einer Canvas-Implementierung funktionieren sollte.
Prestaul
1
Ich glaube, der IE zeichnet die Zeilen neu, da sie sich noch im Pfadpuffer befinden. Ich benutze dieses Format und es funktioniert perfekt. function clearCanvas(ctx) { ctx.beginPath(); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); }
DarrenMB
Ich habe vorher jede einzelne Methode ausprobiert ... und dies war die einzige, die funktioniert hat. Ich verwende Chrome mit Linien, Rechtecken und Text ... hätte nicht gedacht, dass es so schwer sein würde, etwas zu tun, das eingebaut werden sollte! Vielen Dank!
Anthony Griggs
26

Dies ist 2018 und es gibt noch keine native Methode, um die Leinwand für das Neuzeichnen vollständig zu löschen. clearRect() nicht die Leinwand ganz klar. Nicht ausgefüllte Zeichnungen werden nicht gelöscht (z. B. rect())

1.Um die Leinwand vollständig zu löschen, unabhängig davon, wie Sie zeichnen:

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

Vorteile: Erhält StrokeStyle, FillStyle usw.; Keine Verzögerung;

Nachteile: Unnötig, wenn Sie beginPath bereits verwenden, bevor Sie etwas zeichnen

2.Verwenden Sie den Breite / Höhe-Hack:

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

ODER

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

Vorteile: Funktioniert mit IE Nachteile: Setzt stroSStyle, fillStyle auf schwarz zurück; Laggy;

Ich habe mich gefragt, warum es keine native Lösung gibt. Wird tatsächlich clearRect()als einzeilige Lösung betrachtet, da die meisten Benutzer dies tun, beginPath()bevor sie einen neuen Pfad zeichnen. Obwohl beginPath nur zum Zeichnen von Linien und nicht für geschlossene Pfade verwendet werden darfrect().

Dies ist der Grund, warum die akzeptierte Antwort mein Problem nicht löste und ich Stunden damit verschwendete, verschiedene Hacks auszuprobieren. Verfluche dich Mozilla

sziraqui
quelle
Ich denke, das ist die richtige Antwort, besonders zum Zeichnen von Leinwand. Ich litt unter dem verbleibenden Kontext. Schlaganfall, egal wie ich clearRect aufrufe, während beginPath half!
Shao-Kui
20

Verwenden Sie die clearRect-Methode, indem Sie x-, y-Koordinaten sowie Höhe und Breite der Leinwand übergeben. ClearRect löscht die gesamte Leinwand wie folgt:

canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
Vishwas
quelle
Sie können dies einfach in eine Methode einfügen und jedes Mal aufrufen, wenn Sie den Bildschirm aktualisieren möchten.
@ XXX.xxx plz überprüfen Sie die ID der Leinwand in Ihrem HTML und verwenden Sie diese für die erste Zeile (um Element durch ID zu erhalten)
Vishwas
14

Hier gibt es eine Menge guter Antworten. Eine weitere Anmerkung ist, dass es manchmal Spaß macht, die Leinwand nur teilweise zu räumen. Das heißt, das vorherige Bild wird "ausgeblendet", anstatt es vollständig zu löschen. Dies kann schöne Trails-Effekte ergeben.

es ist einfach. Angenommen, Ihre Hintergrundfarbe ist weiß:

// 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);
Orion Elenzil
quelle
Ich weiß, dass es möglich ist und ich habe kein Problem mit Ihrer Antwort. Ich frage mich nur, ob Sie zwei Objekte haben, die sich bewegen, und Sie möchten nur eines davon verfolgen. Wie würden Sie das tun?
Super
das ist viel komplexer. In diesem Fall sollten Sie eine Leinwand außerhalb des Bildschirms erstellen. Jeder Frame zeichnet die gewünschten Objekte mit der hier beschriebenen Methode zum teilweisen Löschen auf diese Leinwand. Außerdem löscht jeder Frame die Hauptleinwand zu 100% und zeichnet die nicht gezogenen Objekte und setzen Sie dann die Zeichenfläche außerhalb des Bildschirms mit drawImage () auf die Hauptfläche. Sie müssen auch die globalCompositeOperation auf einen für die Bilder geeigneten Wert einstellen. zB "multiplizieren" würde gut für dunkle Objekte auf hellem Hintergrund funktionieren.
Orion Elenzil
12

Ein schneller Weg ist zu tun

canvas.width = canvas.width

Idk wie es funktioniert, aber es funktioniert!

Jacob Morris
quelle
Beeindruckend. Das funktioniert wirklich. Wie haben Sie das jemals gefunden?
Zipzit
@zipit Schneller Trick, den ich von medium.com gefunden habe, nachdem das normale Löschen nicht gerendert wurde :)
Jacob Morris
1
Ich ziehe mir die Haare aus und versuche herauszufinden, warum das funktioniert! Danke für das Teilen!
Aabbccsmith
Kann jemand erklären, warum das funktioniert und sogar canvas.width + = 0 macht den Job auch, was ist die verdammte Wissenschaft dahinter?
PYK
8

Dies ist, was ich benutze, unabhängig von Grenzen und Matrixtransformationen:

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

Grundsätzlich wird der aktuelle Status des Kontexts gespeichert und ein transparentes Pixel mit copyas gezeichnet globalCompositeOperation. Stellt dann den vorherigen Kontextstatus wieder her.

Superruzafa
quelle
Für mich geht das...! Vielen Dank.
NomanJaved
6

Dies funktionierte für mein 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
Sanal S.
quelle
5

Ich habe festgestellt, dass in allen Browsern, die ich teste, der schnellste Weg darin besteht, Rect tatsächlich mit Weiß oder einer beliebigen Farbe zu füllen. Ich habe einen sehr großen Monitor und im Vollbildmodus ist clearRect quälend langsam, aber fillRect ist vernünftig.

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

Der Nachteil ist, dass die Leinwand nicht mehr transparent ist.

John Page
quelle
4

Im Webkit müssen Sie die Breite auf einen anderen Wert einstellen, dann können Sie sie auf den Anfangswert zurücksetzen

John Allsopp
quelle
4
function clear(context, color)
{
    var tmp = context.fillStyle;
    context.fillStyle = color;
    context.fillRect(0, 0, context.canvas.width, context.canvas.height);
    context.fillStyle = tmp;
}
Stellen Sie sich Breaker vor
quelle
Dies ist langsamer als clearRect (0,0, canvas.width, canvas.height)
immer am
4

Eine einfache, aber nicht sehr lesbare Möglichkeit besteht darin, Folgendes zu schreiben:

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

// after doing some rendering

canvas.width = canvas.width;  // clear the whole canvas
Chang
quelle
2

Ich benutze immer

cxt.fillStyle = "rgb(255, 255, 255)";
cxt.fillRect(0, 0, canvas.width, canvas.height);
GameMaster1928
quelle
das ist der einfachste Weg! Zumindest für mich.
Jacques Olivier
1
context.clearRect(0,0,w,h)   

Füllen Sie das angegebene Rechteck mit RGBA-Werten:
0 0 0 0: mit Chrome
0 0 0 255: mit FF & Safari

Aber

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

Lass das Rechteck mit
0 0 0 255 gefüllt sein,
egal welcher Browser!

Philippe Oceangermanique
quelle
1
Context.clearRect(starting width, starting height, ending width, ending height);

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

Gavin T.
quelle
1

der kürzeste Weg:

canvas.width += 0
Yukulélé
quelle
0

schnellste Weg:

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
elser
quelle
12
wow ... In welchem ​​Browser? In meinem (Chrome 22 und Firefox 14 unter OS X) ist Ihre Methode bei weitem die langsamste Option. jsperf.com/canvas-clear-speed/9
Prestaul
1
> 5 Jahre in der Zukunft ist dies immer noch die langsamste Methode mit einem sehr großen Betrag, ~ 700x langsamer als clearRect.
mgthomas99
0

Wenn Sie clearRect nur verwenden und es in einem Formular zum Senden Ihrer Zeichnung haben, erhalten Sie stattdessen das Senden der Löschung, oder es kann zuerst gelöscht und dann eine leere Zeichnung hochgeladen werden, sodass Sie eine hinzufügen müssen PreventDefault zu Beginn der Funktion:

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

Hoffe es hilft jemandem.

JoelBonetR
quelle
0

Ich benutze das immer

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

HINWEIS

Die Kombination von clearRect und requestAnimationFrame ermöglicht eine flüssigere Animation, wenn Sie dies wünschen

Anthony Gedeon
quelle
-2

Dies sind alles gute Beispiele dafür, wie Sie eine Standard-Leinwand löschen. Wenn Sie jedoch paperjs verwenden, funktioniert dies:

Definieren Sie eine globale Variable in JavaScript:

var clearCanvas = false;

Definieren Sie in Ihrem PaperScript:

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

Wo immer Sie clearCanvas auf true setzen, werden alle Elemente vom Bildschirm gelöscht.

Robert Ogle
quelle