Jak wyczyścić płótno do przerysowania

1012

Po eksperymentowaniu z operacjami złożonymi i rysowaniu obrazów na płótnie próbuję teraz usunąć obrazy i kompozycję. Jak mam to zrobic?

Muszę wyczyścić płótno, aby przerysować inne obrazy; to może trwać przez jakiś czas, więc nie sądzę, aby rysowanie nowego prostokąta za każdym razem było najbardziej wydajną opcją.

Richard
źródło

Odpowiedzi:

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

context.clearRect(0, 0, canvas.width, canvas.height);
Pentium10
źródło
42
Pamiętaj, clearRectże musisz mieć nieprzetworzony kontekst lub śledzić swoje rzeczywiste granice.
Phrogz
7
Zauważ też, że ustawienie szerokości obszaru roboczego a) musi ustawić inną wartość, a następnie ponownie w celu zapewnienia zgodności z zestawem Webkit , i b) spowoduje to zresetowanie transformacji kontekstu .
Phrogz
45
Nie używaj sztuczki szerokości. To brudny hack. W języku wysokiego poziomu, takim jak JavaScript, najlepiej jest określić swoje intencje, a następnie pozwolić, aby kod niższego poziomu je spełnił. Ustawianie szerokości do siebie nie nie podać swoje prawdziwe intencje, co jest jasne płótno.
andrewrk
22
Zupełnie drobna sugestia, ale proponuję context.clearRect(0, 0, context.canvas.width, context.canvas.height). Jest to właściwie to samo, ale jedna mniejsza zależność (1 zmienna zamiast 2)
gman
6
Dla nowszych czytelników tej odpowiedzi: pamiętaj, że ta odpowiedź została zmodyfikowana i że niektóre z powyższych komentarzy nie mają już zastosowania .
daveslab
719

Posługiwać się: context.clearRect(0, 0, canvas.width, canvas.height);

Jest to najszybszy i najbardziej opisowy sposób na wyczyszczenie całego obszaru roboczego.

Nie używaj: canvas.width = canvas.width;

Resetowanie canvas.widthresetuje cały stan obszaru roboczego (np. Transformacje, lineWidth, strokeStyle itp.), Jest bardzo wolny (w porównaniu do clearRect), nie działa we wszystkich przeglądarkach i nie opisuje tego, co tak naprawdę próbujesz zrobić.

Radzenie sobie z transformowanymi współrzędnymi

Jeśli zmodyfikowałeś macierz transformacji (np. Używając scale, rotatelub translate) context.clearRect(0,0,canvas.width,canvas.height), prawdopodobnie nie wyczyści całej widocznej części obszaru roboczego.

Rozwiązanie? Zresetuj macierz transformacji przed wyczyszczeniem kanwy:

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

Edycja: Właśnie przeprowadziłem profilowanie i (w Chrome) jest około 10% szybsze wyczyszczenie obszaru roboczego 300 x 150 (domyślny rozmiar) bez resetowania transformacji. Wraz ze wzrostem rozmiaru płótna różnica ta spada.

Jest to już stosunkowo mało znaczące, ale w większości przypadków będziesz rysować znacznie więcej niż to, co robisz, i uważam, że ta różnica w wydajności nie ma znaczenia.

100000 iterations averaged 10 times:
1885ms to clear
2112ms to reset and clear
Prestaul
źródło
4
@YumYumYum: Czy clear () jest standardową funkcją? Nie wydaje się.
nobar
7
Zauważ, że możesz usunąć potrzebę canvaszmiennej lokalnej , używając ctx.canvaszamiast tego.
Drew Noakes,
1
@DrewNoakes, dobra uwaga, ale prawie zawsze wybieram osobne zmienne ze względu na prędkość. Jest to bardzo niewielkie, ale staram się unikać czasu dereferencji, aliasingując często używane właściwości (szczególnie wewnątrz pętli animacji).
Prestaul
Demo AlexanderN już nie działało z powodu zepsutego łącza do obrazu, naprawiono: jsfiddle.net/Ktqfs/50
Domino
Innym sposobem, aby wyczyścić cały obszar roboczy w przypadku modyfikacji macierzy transformacji jest po prostu śledzić przemian i zastosować je do siebie canvas.widthi canvas.heightpo polanie. Nie wykonałem żadnej analizy numerycznej różnicy czasu wykonywania w porównaniu do zresetowania transformacji, ale podejrzewam, że przyniosłoby to nieco lepszą wydajność.
NanoWizard
226

Jeśli rysujesz linie, pamiętaj, aby nie zapomnieć:

context.beginPath();

W przeciwnym razie linie nie zostaną usunięte.

drżenie
źródło
10
Wiem, że już tu jest, i chyba głupio jest komentować po całym tym czasie, ale przeszkadza mi to od roku ... Zadzwoń, beginPathkiedy zaczynasz nową ścieżkę , nie zakładaj tego tylko dlatego, że czyścisz płótno, które chcesz wyczyścić również istniejącą ścieżkę! Byłaby to okropna praktyka i jest odwróconym spojrzeniem na rysunek.
Prestaul
Co jeśli chcesz po prostu wyczyścić płótno ze wszystkiego. Powiedzmy, że tworzysz grę i że musisz przerysować ekran co setne sekundy.
1
@JoseQuinones, beginPathnie usuwa niczego z obszaru roboczego, resetuje ścieżkę, dzięki czemu poprzednie wpisy ścieżki są usuwane przed rysowaniem. Prawdopodobnie potrzebujesz go, ale jako operacji rysowania, a nie operacji czyszczenia. wyczyść, następnie rozpocznij Ścieżkę i narysuj, nie wyczyść i rozpocznij Ścieżkę, a następnie narysuj. Czy różnica ma sens? Spójrz tutaj na przykład: w3schools.com/tags/canvas_beginpath.asp
Prestaul
1
To rozwiązało spowolnienie mojej animacji z czasem. Przerysowałem linie siatki na każdym kroku, ale bez czyszczenia linii z poprzedniego kroku.
Georg Patscheider,
118

Inni już wykonali świetną robotę, odpowiadając na pytanie, ale jeśli prosta clear()metoda na obiekcie kontekstowym byłaby dla ciebie przydatna (to było dla mnie), jest to implementacja, której używam na podstawie odpowiedzi tutaj:

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

Stosowanie:

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
źródło
6
To świetne wdrożenie. W pełni popieram ulepszanie natywnych prototypów, ale możesz chcieć upewnić się, że „jasne” nie jest zdefiniowane przed przypisaniem - wciąż mam nadzieję na natywną implementację pewnego dnia. :) Czy wiesz, jak szeroko przeglądarki obsługują CanvasRenderingContext2D i pozostawiają to do zapisu?
Prestaul
Dzięki za opinie @ Prestaul - żadna przeglądarka nie powinna powstrzymywać Cię przed rozszerzaniem obiektów javascript w ten sposób.
JonathanK
@JonathanK, chciałbym zobaczyć pewne profilowanie różnicy w wydajności między czyszczeniem zi bez resetowania transformacji. Domyślam się, że różnica będzie widoczna, jeśli robisz małe rysunki, ale jeśli to, co rysujesz, nie jest trywialne, to wyraźny krok jest znikomy ... Być może będę musiał to sprawdzić później, gdy będę miał więcej czasu :)
Prestaul
Ok, zrobiłem profilowanie i dodam szczegóły do ​​mojego postu.
Prestaul
35
  • Chrome dobrze reaguje na: context.clearRect ( x , y , w , h );jak sugeruje @ Pentium10, ale IE9 wydaje się całkowicie ignorować tę instrukcję.
  • Wydaje się, że IE9 reaguje na: canvas.width = canvas.width;ale nie usuwa linii, tylko kształty, obrazy i inne obiekty, chyba że użyjesz rozwiązania @John Allsopp, polegającego na zmianie szerokości.

Więc jeśli masz kanwę i kontekst utworzone w ten sposób:

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

Możesz użyć metody takiej jak ta:

function clearCanvas(context, canvas) {
  context.clearRect(0, 0, canvas.width, canvas.height);
  var w = canvas.width;
  canvas.width = 1;
  canvas.width = w;
}
granat
źródło
13
Zauważ, że metodę można uprościć, przekazując tylko kontekst i używając context.clearRect(0,0,context.canvas.width,context.canvas.height).
Phrogz
5
IE 9 powinien bezwzględnie reagować na wywołanie clearRect ... (Patrz: msdn.microsoft.com/en-us/library/ff975407(v=vs.85).aspx ) Tak wolno, jak zmiana canvas.width jest jedynym sposobem możesz zwolnić, zmieniając go dwukrotnie i wywołując także clearRect.
Prestaul
1
Przepraszam, powinienem być bardziej przejrzysty ... Ta metoda będzie działać (dopóki nie zastosujesz transformacji), ale jest techniką [wolną] brutalną siłą, w której nie jest konieczna. Wystarczy użyć clearRect, ponieważ jest on najszybszy i powinien działać w każdej przeglądarce z implementacją canvas.
Prestaul
1
Wierzę, że IE przerysowuje linie, ponieważ wciąż znajdują się w buforze ścieżki. Używam tego formatu i działa idealnie. function clearCanvas(ctx) { ctx.beginPath(); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); }
DarrenMB
Wypróbowałem każdą metodę wcześniej ... i to była jedyna, która zadziałała. Używam Chrome z liniami, prostokątami i tekstem ... nie pomyślałbym, że tak trudno byłoby zrobić coś, co powinno być wbudowane! Dzięki!
Anthony Griggs
26

Jest rok 2018 i nadal nie ma natywnej metody całkowitego wyczyszczenia obszaru roboczego do przerysowania. clearRect() nie usuwa całkowicie płótna. Rysunki typu niepełniącego nie są usuwane (np. rect())

1. Aby całkowicie wyczyścić płótno, niezależnie od sposobu rysowania:

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

Zalety: Zachowuje styl udarowy, styl wypełnienia itp .; Bez opóźnień;

Minusy: niepotrzebne, jeśli już korzystasz z beginPath przed rysowaniem czegokolwiek

2. Za pomocą hacka szerokości / wysokości:

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

LUB

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

Plusy: Współpracuje z IE Cons: Resetuje strokeStyle, fillStyle na czarny; Laggy;

Zastanawiałem się, dlaczego natywne rozwiązanie nie istnieje. W rzeczywistości clearRect()jest uważany za rozwiązanie jednowierszowe, ponieważ większość użytkowników robi to beginPath()przed narysowaniem nowej ścieżki. Chociaż beginPath może być używany tylko podczas rysowania linii, a nie zamkniętej ścieżki jakrect().

To jest powód, dla którego zaakceptowana odpowiedź nie rozwiązała mojego problemu i skończyłem marnowaniem godzin na próby różnych hacków. Przeklnij mozillę

sziraqui
źródło
Myślę, że to właściwa odpowiedź, szczególnie w przypadku rysowania na płótnie. Cierpiałem z powodu pozostającego kontekstu. Stroke bez względu na to, jak nazywam clearRect, a beginPath pomógł!
Shao-Kui
20

Użyj metody clearRect, przekazując współrzędne x, y oraz wysokość i szerokość płótna. ClearRect wyczyści całe płótno jako:

canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
Vishwas
źródło
Możesz po prostu zastosować tę metodę i wywołać ją za każdym razem, gdy chcesz odświeżyć ekran, popraw.
@ XXX.xxx plz sprawdź identyfikator płótna w pliku HTML i użyj tego samego w pierwszym wierszu (aby uzyskać element id)
Vishwas
14

jest tu mnóstwo dobrych odpowiedzi. jedna dodatkowa uwaga jest taka, że ​​czasami fajnie jest tylko częściowo wyczyścić płótno. to znaczy „wyciemnia” poprzedni obraz zamiast go całkowicie wymazać. może to dać ładne efekty szlaków.

To jest łatwe. zakładając, że kolor tła jest biały:

// 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
źródło
Wiem, że jest to możliwe i nie mam problemu z twoją odpowiedzią, po prostu zastanawiam się, czy masz 2 poruszające się obiekty i chcesz tylko prześledzić jeden z nich, jak byś to zrobił?
super
to dużo bardziej skomplikowane. w takim przypadku należy utworzyć płótno poza ekranem, a każda ramka narysuje obiekty, które mają zostać narysowane na tym płótnie, używając opisanej tutaj metody częściowego wymazywania, a także każda ramka wyczyści główne płótno w 100%, narysuj nierysowane obiekty, a następnie skomponuj płótno poza ekranem na głównym za pomocą drawImage (). Musisz także ustawić globalCompositeOperation na coś odpowiedniego dla obrazów. np. „zwielokrotnienie” działałoby dobrze w przypadku ciemnych obiektów na jasnym tle.
orion elenzil
12

Szybki sposób to zrobić

canvas.width = canvas.width

Idk jak to działa, ale działa!

Jacob Morris
źródło
Łał. To naprawdę działa, jak to znalazłeś?
zipzit
@zipit Szybka sztuczka, którą znalazłem na medium.com po normalnym wyczyszczeniu, nie renderowałem :)
Jacob Morris
1
Wyciągam włosy, próbując zrozumieć, dlaczego to działa! Dzięki za udostępnienie!
aabbccsmith
Czy ktoś może wyjaśnić, dlaczego to działa, a nawet canvas.width + = 0 to też działa, co za tym dziwna nauka?
PYK
8

Oto, czego używam, bez względu na granice i transformacje macierzy:

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

Zasadniczo zapisuje bieżący stan kontekstu i rysuje przezroczysty piksel za pomocą copyas globalCompositeOperation. Następnie przywraca poprzedni stan kontekstu.

superruzafa
źródło
mi to pasuje...! Dzięki.
NomanJaved
6

To zadziałało dla mojego pieChart w 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.
źródło
5

Zauważyłem, że we wszystkich testowanych przeglądarkach najszybszym sposobem jest wypełnienie Rect białym lub innym kolorem, który chcesz. Mam bardzo duży monitor iw trybie pełnoekranowym clearRect działa agonalnie wolno, ale fillRect jest rozsądny.

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

Wadą jest to, że płótno nie jest już przezroczyste.

John Page
źródło
4

w webkicie musisz ustawić szerokość na inną wartość, a następnie możesz ustawić ją z powrotem na wartość początkową

John Allsopp
źródło
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;
}
Wyobraź sobie Breakera
źródło
Jest to wolniejsze niż clearRect (0,0, canvas.width, canvas.height)
sver
4

Prostym, ale niezbyt czytelnym sposobem jest napisanie tego:

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

// after doing some rendering

canvas.width = canvas.width;  // clear the whole canvas
Chang
źródło
2

Zawsze używam

cxt.fillStyle = "rgb(255, 255, 255)";
cxt.fillRect(0, 0, canvas.width, canvas.height);
GameMaster1928
źródło
to jest najprostszy sposób! Cóż, przynajmniej dla mnie.
Jacques Olivier
1
context.clearRect(0,0,w,h)   

wypełnij podany prostokąt wartościami RGBA:
0 0 0 0: z Chrome
0 0 0 255: z FF i Safari

Ale

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

niech prostokąt wypełni się
0 0 0 255
bez względu na przeglądarkę!

Philippe Oceangermanique
źródło
1
Context.clearRect(starting width, starting height, ending width, ending height);

Przykład: context.clearRect(0, 0, canvas.width, canvas.height);

Gavin T.
źródło
1

najkrótsza droga:

canvas.width += 0
Yukulélé
źródło
0

najszybsza droga:

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
źródło
12
wow ... W jakiej przeglądarce? W moim (Chrome 22 i Firefox 14 na OS X) twoja metoda jest zdecydowanie najwolniejszą opcją. jsperf.com/canvas-clear-speed/9
Prestaul
1
> 5 lat w przyszłości, jest to wciąż najwolniejsza metoda o bardzo dużą ilość, ~ 700x wolniejsza niż clearRect.
mgthomas99
0

Jeśli użyjesz tylko clearRect, jeśli masz go w formularzu, aby przesłać rysunek, zamiast tego otrzymasz przesłanie, a może najpierw możesz je wyczyścić, a następnie przesłać nieważny rysunek, więc musisz dodać preventDefault na początku funkcji:

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

Mam nadzieję, że to komuś pomoże.

JoelBonetR
źródło
0

Zawsze tego używam

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

UWAGA

Łączenie clearRect i requestAnimationFrame pozwala na płynniejszą animację, jeśli właśnie o to chodzi

Anthony Gedeon
źródło
-2

Są to świetne przykłady tego, jak wyczyścić standardowe płótno, ale jeśli używasz paperjs, to zadziała:

Zdefiniuj zmienną globalną w JavaScript:

var clearCanvas = false;

Ze swojego PaperScript zdefiniuj:

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

Teraz, gdziekolwiek ustawisz clearCanvas na true, usunie wszystkie elementy z ekranu.

Robert Ogle
źródło