Comment effacer le canevas pour le redessiner

1013

Après avoir expérimenté des opérations composites et dessiné des images sur la toile, j'essaie maintenant de supprimer les images et le compositing. Comment puis-je faire cela?

J'ai besoin d'effacer la toile pour redessiner d'autres images; cela peut durer un certain temps, donc je ne pense pas que dessiner un nouveau rectangle à chaque fois soit l'option la plus efficace.

Richard
la source

Réponses:

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

context.clearRect(0, 0, canvas.width, canvas.height);
Pentium10
la source
42
Notez que clearRectvous devez soit avoir un contexte non transformé, soit garder une trace de vos limites réelles.
Phrogz
7
Notez en outre que la définition de la largeur du canevas a) doit la définir sur une valeur différente, puis revenir à nouveau pour la compatibilité de Webkit , et b) cela réinitialisera votre transformation de contexte .
Phrogz
45
N'utilisez pas l'astuce de largeur. C'est un sale hack. Dans un langage de haut niveau tel que JavaScript, ce que vous voulez, c'est exprimer au mieux vos intentions, puis laisser le code de niveau inférieur les réaliser. La définition de la largeur sur elle-même n'indique pas votre véritable intention, qui est de vider le canevas.
andrewrk
22
Une suggestion totalement mineure mais je suggérerais context.clearRect(0, 0, context.canvas.width, context.canvas.height). C'est effectivement la même chose mais une dépendance en moins (1 variable au lieu de 2)
gman
6
Pour les lecteurs plus récents de cette réponse: sachez que cette réponse a été modifiée et que certains des commentaires ci-dessus ne s'appliquent plus .
daveslab
719

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

C'est le moyen le plus rapide et le plus descriptif pour effacer l'intégralité du canevas.

Ne pas utiliser: canvas.width = canvas.width;

La réinitialisation canvas.widthréinitialise tous les états du canevas (par exemple les transformations, lineWidth, strokeStyle, etc.), elle est très lente (par rapport à clearRect), elle ne fonctionne pas dans tous les navigateurs et ne décrit pas ce que vous essayez réellement de faire.

Gérer les coordonnées transformées

Si vous avez modifié la matrice de transformation (par exemple , en utilisant scale, rotateoutranslate ) alors context.clearRect(0,0,canvas.width,canvas.height)ne sera probablement pas effacer toute la partie visible de la toile.

La solution? Réinitialisez la matrice de transformation avant d'effacer le canevas:

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

Éditer: je viens de faire du profilage et (dans Chrome), il est environ 10% plus rapide pour effacer un canevas 300x150 (taille par défaut) sans réinitialiser la transformation. À mesure que la taille de votre toile augmente, cette différence diminue.

C'est déjà relativement insignifiant, mais dans la plupart des cas, vous tirerez beaucoup plus que vous ne supprimez et je pense que cette différence de performances n'est pas pertinente.

100000 iterations averaged 10 times:
1885ms to clear
2112ms to reset and clear
Prestaul
la source
4
@YumYumYum: clear () est-il une fonction standard? Cela ne semble pas l'être.
nobar
7
Notez que vous pouvez supprimer le besoin d'une canvasvariable locale en utilisant à la ctx.canvasplace.
Drew Noakes du
1
@DrewNoakes, bon point, mais j'opte presque toujours pour des variables distinctes à des fins de vitesse. C'est extrêmement mineur, mais j'essaie d'éviter de déréférencer le temps en aliasant les propriétés fréquemment utilisées (en particulier à l'intérieur d'une boucle d'animation).
Prestaul
La démo d'AlexanderN ne fonctionnait plus en raison d'un lien d'image cassé, corrigé: jsfiddle.net/Ktqfs/50
Domino
Une autre façon d'effacer la totalité du canevas en cas de modification de la matrice de transformation consiste à simplement suivre les transformations et à les appliquer vous-même canvas.widthet canvas.heightlors de l'effacement. Je n'ai pas fait d'analyse numérique sur la différence d'exécution par rapport à la réinitialisation de la transformation, mais je pense que cela améliorerait légèrement les performances.
NanoWizard
226

Si vous dessinez des lignes, assurez-vous de ne pas oublier:

context.beginPath();

Sinon, les lignes ne seront pas effacées.

trembler
la source
10
Je sais que cela existe depuis un certain temps et il est probablement idiot que je commente après tout ce temps, mais cela me dérange depuis un an ... Appelez beginPathlorsque vous commencez un nouveau chemin , ne supposez pas cela simplement parce que vous effacez la toile que vous souhaitez également effacer votre chemin existant! Ce serait une pratique horrible et une façon à l'envers de voir le dessin.
Prestaul
Et si vous voulez simplement effacer la toile de tout. Disons que vous créez un jeu et que vous devez redessiner l'écran tous les centièmes de seconde.
1
@JoseQuinones, beginPathne supprime rien de votre canevas, il réinitialise le chemin afin que les entrées de chemin précédentes soient supprimées avant de dessiner. Vous en aviez probablement besoin, mais en tant qu'opération de dessin, pas en tant qu'opération de compensation. effacer, puis beginPath et dessiner, pas clair et beginPath, puis dessiner. La différence a-t-elle un sens? Regardez ici pour un exemple: w3schools.com/tags/canvas_beginpath.asp
Prestaul
1
Cela a résolu le ralentissement de mon animation que j'ai connu au fil du temps. J'ai redessiné les lignes de la grille à chaque étape, mais sans effacer les lignes de l'étape précédente.
Georg Patscheider
118

D'autres ont déjà fait un excellent travail en répondant à la question mais si une clear()méthode simple sur l'objet contextuel vous serait utile (c'était à moi), c'est l'implémentation que j'utilise en fonction des réponses ici:

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
};
JonathanK
la source
6
Il s'agit d'une excellente mise en œuvre. Je soutiens entièrement l'amélioration des prototypes natifs, mais vous voudrez peut-être vous assurer que "clair" n'est pas défini avant de l'attribuer - j'espère toujours un jour une implémentation native. :) Savez-vous dans quelle mesure les navigateurs prennent en charge CanvasRenderingContext2D et le laissent "accessible en écriture"?
Prestaul
Merci pour les commentaires @Prestaul - également, aucun navigateur ne devrait vous empêcher d'étendre les objets javascript de cette manière.
JonathanK
@JonathanK, j'adorerais voir un profilage de la différence de performances entre l'effacement avec et sans réinitialisation de la transformation. Je suppose que la différence sera évidente si vous faites peu de dessin mais que si ce que vous dessinez n'est pas trivial alors l'étape claire est négligeable ... Je devrai peut-être le tester plus tard quand j'aurai plus de temps :)
Prestaul
Ok, j'ai fait le profilage et je vais ajouter les détails à mon post.
Prestaul
35
  • Chrome répond bien à: context.clearRect ( x , y , w , h );comme suggéré par @ Pentium10 mais IE9 semble ignorer complètement cette instruction.
  • IE9 semble répondre à: canvas.width = canvas.width;mais il n'efface pas les lignes, juste les formes, les images et autres objets à moins que vous n'utilisiez également la solution de @John Allsopp de changer d'abord la largeur.

Donc, si vous avez un canevas et un contexte créés comme ceci:

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

Vous pouvez utiliser une méthode comme celle-ci:

function clearCanvas(context, canvas) {
  context.clearRect(0, 0, canvas.width, canvas.height);
  var w = canvas.width;
  canvas.width = 1;
  canvas.width = w;
}
grenade
la source
13
Notez que la méthode peut être simplifiée en passant uniquement dans le contexte et en utilisant context.clearRect(0,0,context.canvas.width,context.canvas.height).
Phrogz
5
IE 9 devrait absolument répondre à un appel clearRect ... (Voir: msdn.microsoft.com/en-us/library/ff975407(v=vs.85).aspx ) Aussi lent que le changement de canvas.width, la seule façon vous pouvez ralentir en le modifiant deux fois et en appelant clearRect également.
Prestaul
1
Désolé, je devrais être plus clair ... Cette méthode fonctionnera (tant que vous n'avez pas appliqué de transformation), mais c'est une technique de force brute [lente] où elle n'est pas nécessaire. Utilisez simplement clearRect car il est le plus rapide et devrait fonctionner sur tous les navigateurs avec une implémentation de canevas.
Prestaul
1
Je crois qu'IE redessine les lignes car elles sont toujours dans le tampon du chemin. J'utilise ce format et cela fonctionne parfaitement. function clearCanvas(ctx) { ctx.beginPath(); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); }
DarrenMB
J'ai essayé chaque méthode avant cela ... et c'était la seule qui fonctionnait. J'utilise Chrome avec des lignes, des rectangles et du texte ... n'aurait pas pensé qu'il serait si difficile de faire quelque chose qui devrait être intégré! Merci!
Anthony Griggs
27

Nous sommes en 2018 et il n'y a toujours pas de méthode native pour effacer complètement le canevas pour le redessiner. clearRect() n'efface pas complètement la toile. Les dessins de type non remplis ne sont pas effacés (par exemple. rect())

1.Pour une toile complètement claire quelle que soit la façon dont vous dessinez:

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

Avantages: préserve strokeStyle, fillStyle, etc.; Pas de décalage;

Inconvénients: inutile si vous utilisez déjà beginPath avant de dessiner quoi que ce soit

2.Utilisation du hack largeur / hauteur:

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

OU

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

Pour: Fonctionne avec IE Contre: Réinitialise strokeStyle, fillStyle sur noir; Laggy;

Je me demandais pourquoi une solution native n'existe pas. En fait, clearRect()est considéré comme la solution à ligne unique car la plupart des utilisateurs le font beginPath()avant de dessiner un nouveau chemin. Bien que beginPath ne soit utilisé que pour dessiner des lignes et non un chemin fermé commerect().

C'est la raison pour laquelle la réponse acceptée n'a pas résolu mon problème et j'ai fini par perdre des heures à essayer différents hacks. Maudis-tu mozilla

sziraqui
la source
Je pense que c'est la bonne réponse, en particulier pour dessiner une toile. J'ai souffert du contexte restant.
Shao-Kui
20

Utilisez la méthode clearRect en passant les coordonnées x, y et la hauteur et la largeur du canevas. ClearRect effacera la toile entière comme:

canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
Vishwas
la source
Vous pouvez simplement mettre cela dans une méthode et l'appeler chaque fois que vous voulez rafraîchir l'écran, c'est correct.
@ XXX.xxx plz vérifiez l'id du canevas dans votre html et utilisez-le pour la première ligne (pour obtenir l'élément par id)
Vishwas
14

il y a une tonne de bonnes réponses ici. une autre remarque est que parfois c'est amusant de ne nettoyer que partiellement la toile. c'est-à-dire "effacer" l'image précédente au lieu de l'effacer entièrement. cela peut donner de beaux effets de sentiers.

c'est facile. en supposant que votre couleur de fond est blanche:

// 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
la source
Je sais que c'est possible et je n'ai aucun problème avec votre réponse, je me demande simplement, si vous avez 2 objets qui se déplacent, et que vous voulez seulement en suivre un, comment feriez-vous?
super
c'est beaucoup plus complexe. dans ce cas, vous devez créer un canevas hors écran, et chaque cadre dessine les objets que vous souhaitez traîner sur ce canevas en utilisant la méthode d'effacement partiel décrite ici, et également chaque frame efface le canevas principal à 100%, dessinez le non traîné , puis composez le canevas hors écran sur le canevas principal à l'aide de drawImage (). Vous devrez également définir globalCompositeOperation sur quelque chose de approprié pour les images. Par exemple, "multiplier" fonctionnerait bien pour les objets sombres sur un fond clair.
orion elenzil
12

Un moyen rapide est de faire

canvas.width = canvas.width

Idk comment ça marche mais ça marche!

Jacob Morris
la source
Sensationnel. Cela fonctionne vraiment, comment avez-vous trouvé cela?
zipzit le
@zipit Astuce rapide que j'ai trouvée sur medium.com après que le nettoyage normal n'ait pas été rendu :)
Jacob Morris
1
J'arrache mes cheveux en essayant de comprendre pourquoi cela fonctionne! Merci d'avoir partagé!
aabbccsmith
Quelqu'un peut-il expliquer pourquoi cela fonctionne et même canvas.width + = 0 fait le travail aussi, quelle est la science flippante derrière cela?
PYK
8

C'est ce que j'utilise, quelles que soient les frontières et les transformations matricielles:

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

Fondamentalement, il enregistre l'état actuel du contexte et dessine un pixel transparent avec copyas globalCompositeOperation. Restaure ensuite l'état de contexte précédent.

superruzafa
la source
ça marche pour moi...! Merci.
NomanJaved
6

Cela a fonctionné pour mon pieChart dans 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
la source
5

J'ai constaté que dans tous les navigateurs que je teste, le moyen le plus rapide est de remplir effectivement le blanc ou la couleur de votre choix. J'ai un très grand moniteur et en mode plein écran, le clearRect est terriblement lent, mais le fillRect est raisonnable.

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

L'inconvénient est que la toile n'est plus transparente.

John Page
la source
4

dans le webkit, vous devez définir la largeur sur une valeur différente, puis vous pouvez la redéfinir sur la valeur initiale

John Allsopp
la source
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;
}
Imagine Breaker
la source
C'est plus lent que clearRect (0,0, canvas.width, canvas.height)
sEver
4

Un moyen simple mais peu lisible est d'écrire ceci:

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

// after doing some rendering

canvas.width = canvas.width;  // clear the whole canvas
Chang
la source
2

J'utilise toujours

cxt.fillStyle = "rgb(255, 255, 255)";
cxt.fillRect(0, 0, canvas.width, canvas.height);
GameMaster1928
la source
c'est le moyen le plus simple! Enfin, pour moi au moins.
Jacques Olivier
1
context.clearRect(0,0,w,h)   

remplir le rectangle donné avec des valeurs RGBA:
0 0 0 0: avec Chrome
0 0 0 255: avec FF & Safari

Mais

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

laissez le rectangle rempli de
0 0 0 255
peu importe le navigateur!

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

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

Gavin T
la source
1

le chemin le plus court:

canvas.width += 0
Yukulélé
la source
0

moyen le plus rapide:

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
la source
12
wow ... Dans quel navigateur? Dans la mienne (Chrome 22 et Firefox 14 sous OS X), votre méthode est, de loin, l'option la plus lente. jsperf.com/canvas-clear-speed/9
Prestaul
1
> 5 ans dans le futur, c'est toujours la méthode la plus lente par une très grande quantité, ~ 700x plus lente que clearRect.
mgthomas99
0

Si vous utilisez clearRect uniquement, si vous l'avez dans un formulaire pour soumettre votre dessin, vous obtiendrez un envoi à la place de la suppression, ou peut-être qu'il peut d'abord être effacé puis télécharger un dessin nul, vous devrez donc ajouter un preventDefault au début de la fonction:

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

J'espère que cela aide quelqu'un.

JoelBonetR
la source
0

J'utilise toujours ça

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

REMARQUE

la combinaison de clearRect et requestAnimationFrame permet une animation plus fluide si c'est ce que vous recherchez

Anthony Gedeon
la source
-2

Ce sont tous d'excellents exemples de la façon dont vous effacez un canevas standard, mais si vous utilisez paperjs, cela fonctionnera:

Définissez une variable globale en JavaScript:

var clearCanvas = false;

À partir de votre PaperScript, définissez:

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

Maintenant, où que vous définissiez clearCanvas sur true, tous les éléments de l'écran seront supprimés.

Robert Ogle
la source