Cómo limpiar el lienzo para volver a dibujar

1013

Después de experimentar con operaciones compuestas y dibujar imágenes en el lienzo, ahora estoy tratando de eliminar imágenes y componer. ¿Cómo hago esto?

Necesito limpiar el lienzo para volver a dibujar otras imágenes; esto puede continuar por un tiempo, así que no creo que dibujar un nuevo rectángulo cada vez sea la opción más eficiente.

Ricardo
fuente

Respuestas:

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

context.clearRect(0, 0, canvas.width, canvas.height);
Pentium10
fuente
42
Tenga en cuenta que clearRectdebe tener un contexto no transformado o realizar un seguimiento de sus límites reales.
Phrogz
77
Tenga en cuenta además que establecer el ancho del lienzo a) necesita establecerlo en un valor diferente y luego volver a hacerlo por compatibilidad con Webkit , y b) esto restablecerá su transformación de contexto .
Phrogz
45
No uses el truco de ancho. Es un truco sucio. En un lenguaje de alto nivel como JavaScript, lo que desea es expresar mejor sus intenciones y luego dejar que el código de nivel inferior las cumpla. Establecer el ancho en sí mismo no indica su verdadera intención, que es limpiar el lienzo.
andrewrk
22
Una sugerencia totalmente menor pero sugeriría context.clearRect(0, 0, context.canvas.width, context.canvas.height). Efectivamente es lo mismo pero una dependencia menos (1 variable en lugar de 2)
gman
66
Para lectores más recientes de esta respuesta: tenga en cuenta que esta respuesta fue modificada y que algunos de los comentarios anteriores ya no se aplican .
daveslab
719

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

Esta es la forma más rápida y descriptiva de borrar todo el lienzo.

No utilice: canvas.width = canvas.width;

El restablecimiento canvas.widthrestablece todos los estados del lienzo (por ejemplo, transformaciones, ancho de línea, estilo de trazo, etc.), es muy lento (en comparación con clearRect), no funciona en todos los navegadores y no describe lo que realmente está intentando hacer.

Manejo de coordenadas transformadas

Si ha modificado la matriz de transformación (por ejemplo scale, usando , rotateo translate), entonces context.clearRect(0,0,canvas.width,canvas.height)probablemente no borrará toda la parte visible del lienzo.

¿La solución? Restablezca la matriz de transformación antes de limpiar el lienzo:

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

Editar: Acabo de hacer algunos perfiles y (en Chrome) es aproximadamente un 10% más rápido borrar un lienzo de 300x150 (tamaño predeterminado) sin restablecer la transformación. A medida que aumenta el tamaño de su lienzo, esta diferencia disminuye.

Eso ya es relativamente insignificante, pero en la mayoría de los casos obtendrá mucho más de lo que está limpiando y creo que esta diferencia de rendimiento es irrelevante.

100000 iterations averaged 10 times:
1885ms to clear
2112ms to reset and clear
Prestaul
fuente
44
@YumYumYum: ¿Clear () es una función estándar? No parece ser
nobar
77
Tenga en cuenta que puede eliminar la necesidad de una canvasvariable local utilizando en su ctx.canvaslugar.
Drew Noakes
1
@DrewNoakes, buen punto, pero casi siempre opto por variables separadas para fines de velocidad. Es extremadamente menor, pero trato de evitar la desreferenciación del tiempo aliasing las propiedades de uso frecuente (especialmente dentro de un bucle de animación).
Prestaul
La demostración de AlexanderN ya no funcionó debido al enlace de imagen roto, corregido: jsfiddle.net/Ktqfs/50
Domino
Otra forma de borrar todo el lienzo en caso de modificación de la matriz de transformación es simplemente realizar un seguimiento de las transformaciones y aplicarlas usted mismo canvas.widthy canvas.heightal borrarlas. No he hecho ningún análisis numérico sobre la diferencia de tiempo de ejecución en comparación con el restablecimiento de la transformación, pero sospecho que obtendría un rendimiento un poco mejor.
NanoWizard
226

Si está dibujando líneas, asegúrese de no olvidar:

context.beginPath();

De lo contrario, las líneas no se despejarán.

temblar
fuente
10
Sé que esto ha estado aquí por un tiempo y probablemente sea una tontería para mí comentar después de todo este tiempo, pero esto me ha estado molestando durante un año ... Llama beginPathcuando comiences un nuevo camino , no asumas eso solo porque están limpiando el lienzo que desea borrar su ruta existente también! Esta sería una práctica horrible y es una forma inversa de mirar el dibujo.
Prestaul
¿Qué pasa si solo quieres limpiar el lienzo de todo? Digamos que está creando un juego y que necesita volver a dibujar la pantalla cada tantas centésimas de segundo.
1
@JoseQuinones, beginPathno borra nada de su lienzo, restablece la ruta para que las entradas de ruta anteriores se eliminen antes de dibujar. Probablemente lo necesitaba, pero como operación de dibujo, no como una operación de limpieza. borrar, luego comenzar Ruta y dibujar, no borrar y comenzar Ruta, luego dibujar. ¿Tiene sentido la diferencia? Mire aquí para ver un ejemplo: w3schools.com/tags/canvas_beginpath.asp
Prestaul
1
Esto resolvió la desaceleración de mi animación que experimenté con el tiempo. Redibujé líneas de cuadrícula en cada paso, pero sin borrar las líneas del paso anterior.
Georg Patscheider
118

Otros ya han hecho un excelente trabajo respondiendo la pregunta, pero si un clear()método simple en el objeto de contexto fuera útil para usted (lo fue para mí), esta es la implementación que uso en base a las respuestas aquí:

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

Uso:

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
fuente
66
Esta es una gran implementación. Apoyo totalmente la mejora de los prototipos nativos, pero es posible que desee asegurarse de que "clear" no esté definido antes de asignarlo; todavía espero una implementación nativa algún día. :) ¿Sabes en qué medida los navegadores son compatibles con CanvasRenderingContext2D y lo dejan "grabable"?
Prestaul
Gracias por los comentarios de @Prestaul; además, ningún navegador debe evitar que extienda objetos JavaScript de esta manera.
JonathanK
@ Jonathan Jonathan, me encantaría ver un perfil de la diferencia de rendimiento entre borrar con y sin restablecer la transformación. Supongo que la diferencia será evidente si estás dibujando poco, pero si lo que estás dibujando no es trivial, entonces el paso claro es insignificante ... Puede que tenga que probarlo más tarde cuando tenga más tiempo :)
Prestaul
Ok, hice el perfil y voy a agregar los detalles a mi publicación.
Prestaul
35
  • Chrome responde bien a: context.clearRect ( x , y , w , h );como lo sugiere @ Pentium10, pero IE9 parece ignorar por completo esta instrucción.
  • Parece que IE9 responde: canvas.width = canvas.width;pero no borra líneas, solo formas, imágenes y otros objetos a menos que también use la solución de @John Allsopp de cambiar primero el ancho.

Entonces, si tiene un lienzo y un contexto creados así:

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

Puedes usar un método como este:

function clearCanvas(context, canvas) {
  context.clearRect(0, 0, canvas.width, canvas.height);
  var w = canvas.width;
  canvas.width = 1;
  canvas.width = w;
}
granada
fuente
13
Tenga en cuenta que el método puede simplificarse pasando solo el contexto y utilizando context.clearRect(0,0,context.canvas.width,context.canvas.height).
Phrogz
55
IE 9 debería responder absolutamente a una llamada clearRect ... (Ver: msdn.microsoft.com/en-us/library/ff975407(v=vs.85).aspx ) Tan lento como cambiar canvas.width es, la única forma podría disminuir su velocidad cambiando dos veces y llamando a clearRect también.
Prestaul
1
Lo siento, debería ser más claro ... Este método funcionará (siempre que no haya aplicado una transformación), pero es una técnica de fuerza bruta [lenta] donde no es necesaria. Simplemente use clearRect, ya que es más rápido y debería funcionar en todos los navegadores con una implementación de lienzo.
Prestaul
1
Creo que IE está redibujando las líneas debido a que todavía están en el búfer de ruta. Yo uso este formato y funciona perfecto. function clearCanvas(ctx) { ctx.beginPath(); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); }
DarrenMB
Probé todos y cada uno de los métodos antes de esto ... y este fue el único que funcionó. Estoy usando Chrome con líneas, rectángulos y texto ... ¡no habría pensado que sería tan difícil hacer algo que debería integrarse! ¡Gracias!
Anthony Griggs
27

Esto es 2018 y aún no existe un método nativo para borrar completamente el lienzo para volver a dibujar. clearRect() No borra el lienzo por completo. Los dibujos de tipo sin relleno no se borran (p. Ej. rect())

1.Para limpiar completamente el lienzo, independientemente de cómo dibuje:

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

Pros: conserva el estilo de trazo, el estilo de relleno, etc .; Sin retraso;

Contras: innecesario si ya está utilizando beginPath antes de dibujar cualquier cosa

2.Utilizando el ancho / alto hack:

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

O

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

Pros: Funciona con IE Contras: Restablece strokeStyle, fillStyle a negro; Laggy;

Me preguntaba por qué no existe una solución nativa. En realidad, clearRect()se considera como la solución de línea única porque la mayoría de los usuarios lo hacen beginPath()antes de dibujar cualquier ruta nueva. Aunque beginPath solo se debe usar al dibujar líneas y no en rutas cerradas comorect().

Esta es la razón por la cual la respuesta aceptada no resolvió mi problema y terminé perdiendo horas probando diferentes hacks. Te maldigo mozilla

sziraqui
fuente
Creo que esta es la respuesta correcta, especialmente para dibujar lienzos. Sufrí de context.stroke restante sin importar cómo llame a clearRect, ¡mientras beginPath ayudó!
Shao-Kui
20

Use el método clearRect pasando coordenadas x, y, altura y ancho del lienzo. ClearRect borrará todo el lienzo como:

canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
Vishwas
fuente
Simplemente puede poner esto en un método y llamarlo cada vez que desee actualizar la pantalla, correcto.
@ XXX.xxx por favor verifique la identificación del lienzo en su html y use la misma para la primera línea (para obtener el elemento por identificación)
Vishwas
14

Hay un montón de buenas respuestas aquí. Una nota adicional es que a veces es divertido borrar solo parcialmente el lienzo. es decir, "desvanece" la imagen anterior en lugar de borrarla por completo. Esto puede dar buenos efectos de senderos.

es fácil. suponiendo que el color de fondo sea blanco:

// 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
fuente
Sé que es posible y no tengo ningún problema con tu respuesta, solo me pregunto, si tienes 2 objetos que se mueven y solo quieres rastrear uno de ellos, ¿cómo harías eso?
súper
Eso es mucho más complejo. en ese caso, debe crear un lienzo fuera de la pantalla, y cada cuadro dibuja los objetos que desea seguir en ese lienzo utilizando el método de borrado parcial que se describe aquí, y también cada cuadro borra el lienzo principal al 100%, dibuja el no arrastrado objetos, y luego componga el lienzo fuera de la pantalla en el principal usando drawImage (). También deberá establecer globalCompositeOperation en algo apropiado para las imágenes. por ejemplo, "multiplicar" funcionaría bien para objetos oscuros sobre un fondo claro.
orion elenzil
12

Una forma rápida es hacer

canvas.width = canvas.width

¡No sé cómo funciona, pero lo hace!

Jacob Morris
fuente
Guau. Esto realmente funciona. ¿Cómo encontraste esto?
zipzit
@zipit Truco rápido que encontré en medium.com después de que el borrado normal no se mostraba :)
Jacob Morris
1
Me estoy sacando el pelo tratando de entender por qué esto funciona. ¡Gracias por compartir!
aabbccsmith
¿Alguien puede explicar por qué eso funciona e incluso canvas.width + = 0 también hace el trabajo, cuál es la maldita ciencia detrás de esto?
PYK
8

Esto es lo que uso, independientemente de los límites y las transformaciones matriciales:

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

Básicamente, guarda el estado actual del contexto y dibuja un píxel transparente con copyas globalCompositeOperation. Luego, restaura el estado del contexto anterior.

superruzafa
fuente
esto funciona para mi...! Gracias.
NomanJaved
6

Esto funcionó para mi pieChart en 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
fuente
5

He descubierto que en todos los navegadores que pruebo, la forma más rápida es llenar Rect con blanco o con el color que desee. Tengo un monitor muy grande y en modo de pantalla completa clearRect es extremadamente lento, pero fillRect es razonable.

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

El inconveniente es que el lienzo ya no es transparente.

John Page
fuente
4

en webkit necesita establecer el ancho en un valor diferente, luego puede volver a establecerlo en el valor inicial

John Allsopp
fuente
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;
}
Asesino de Ilusiones
fuente
Esto es más lento que clearRect (0,0, canvas.width, canvas.height)
sEver
4

Una manera simple, pero no muy legible es escribir esto:

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

// after doing some rendering

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

Yo siempre uso

cxt.fillStyle = "rgb(255, 255, 255)";
cxt.fillRect(0, 0, canvas.width, canvas.height);
GameMaster1928
fuente
¡Esta es la manera más fácil! Bueno, al menos para mí.
Jacques Olivier
1
context.clearRect(0,0,w,h)   

rellena el rectángulo dado con valores RGBA:
0 0 0 0: con Chrome
0 0 0 255: con FF y Safari

Pero

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

deje que el rectángulo se llene con
0 0 0 255
sin importar el navegador!

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

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

Gavin T
fuente
1

El camino más corto:

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

la manera más rápida:

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
fuente
12
wow ... ¿En qué navegador? En el mío (Chrome 22 y Firefox 14 en OS X) su método es, con mucho, la opción más lenta. jsperf.com/canvas-clear-speed/9
Prestaul
1
> 5 años en el futuro, este sigue siendo el método más lento en una cantidad muy grande, ~ 700 veces más lento que clearRect.
mgthomas99
0

Si usa clearRect solo, si lo tiene en un formulario para enviar su dibujo, recibirá un envío en lugar del borrado, o tal vez pueda borrarse primero y luego cargar un dibujo vacío, por lo que deberá agregar un preventDefault al comienzo de la función:

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

Espero que ayude a alguien.

JoelBonetR
fuente
0

Siempre uso esto

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

NOTA

la combinación de clearRect y requestAnimationFrame permite una animación más fluida si eso es lo que está buscando

Anthony Gedeon
fuente
-2

Todos estos son excelentes ejemplos de cómo limpiar un lienzo estándar, pero si está utilizando paperjs, entonces esto funcionará:

Defina una variable global en JavaScript:

var clearCanvas = false;

De su PaperScript defina:

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

Ahora, donde establezca clearCanvas en true, borrará todos los elementos de la pantalla.

Robert Ogle
fuente