Chciałbym narysować moją grafikę w buforze, a następnie móc skopiować ją tak, jak jest na płótnie, aby móc tworzyć animacje i unikać migotania. Ale nie mogłem znaleźć tej opcji. Czy ktoś wie, jak mam się do tego zabrać?
javascript
html
canvas
double-buffering
Shai UI
źródło
źródło
explorercanvas
, ale to oczywiście nie jest HTML5 i jest tocanvas
element tylko emulowany przez VML. Jednak nigdy nie widziałem, aby inne przeglądarki to robiły.Odpowiedzi:
Poniższy pomocny link, oprócz pokazania przykładów i zalet korzystania z podwójnego buforowania, zawiera kilka innych wskazówek dotyczących wydajności przy korzystaniu z elementu canvas html5. Zawiera łącza do testów jsPerf, które gromadzą wyniki testów z różnych przeglądarek w bazie danych Browserscope. Zapewnia to weryfikację wskazówek dotyczących wydajności.
http://www.html5rocks.com/en/tutorials/canvas/performance/
Dla Twojej wygody zamieściłem minimalny przykład skutecznego podwójnego buforowania, jak opisano w artykule.
// canvas element in DOM var canvas1 = document.getElementById('canvas1'); var context1 = canvas1.getContext('2d'); // buffer canvas var canvas2 = document.createElement('canvas'); canvas2.width = 150; canvas2.height = 150; var context2 = canvas2.getContext('2d'); // create something on the canvas context2.beginPath(); context2.moveTo(10,10); context2.lineTo(10,30); context2.stroke(); //render the buffered canvas onto the original canvas element context1.drawImage(canvas2, 0, 0);
źródło
Bardzo prostą metodą jest umieszczenie dwóch elementów kanwy w tym samym miejscu na ekranie i ustawienie widoczności dla bufora, który chcesz pokazać. Rysuj na ukrytym i odwróć, gdy skończysz.
Jakiś kod:
CSS:
canvas { border: 2px solid #000; position:absolute; top:0;left:0; visibility: hidden; }
Odwracanie w JS:
Buffers[1-DrawingBuffer].style.visibility='hidden'; Buffers[DrawingBuffer].style.visibility='visible'; DrawingBuffer=1-DrawingBuffer;
W tym kodzie tablica „Buffers []” zawiera oba obiekty kanwy. Więc jeśli chcesz zacząć rysować, nadal musisz uzyskać kontekst:
var context = Buffers[DrawingBuffer].getContext('2d');
źródło
<noscript>
i tworzyć moje elementy płótna w JavaScript. Zazwyczaj i tak chciałbyś sprawdzić obsługę kanwy w JavaScript, więc dlaczego miałbyś chcieć umieścić zastępczą wiadomość kanwy w kodzie HTML?Wszystkie przeglądarki, które przetestowałem, obsługują to buforowanie, nie malując płótna, dopóki kod, który rysuje twoją ramkę, nie zostanie ukończony. Zobacz także listę mailingową WHATWG: http://www.mail-archive.com/[email protected]/msg19969.html
źródło
Zawsze możesz to zrobić
var canvas2 = document.createElement("canvas");
i w ogóle nie dodawać go do DOM.Samo powiedzenie, ponieważ
display:none;
wydajecie się mieć obsesję na jego punkcie , wydaje mi się czystsze i naśladuje ideę podwójnego buforowania o wiele dokładniej niż po prostu posiadanie niezręcznie niewidocznego płótna.źródło
Ponad dwa lata później:
Nie ma potrzeby „ręcznego” wdrażania podwójnego buforowania. Pan Geary napisał o tym w swojej książce „HTML5 Canvas” .
Aby skutecznie zredukować migotanie
requestAnimationFrame()
!źródło
Josh zapytał (jakiś czas temu), skąd przeglądarka wie, „kiedy kończy się proces rysowania”, aby uniknąć migotania. Skomentowałbym bezpośrednio do jego postu, ale mój przedstawiciel nie jest wystarczająco wysoki. To tylko moja opinia. Nie mam faktów na poparcie tego, ale czuję się dość pewnie, co do tego i może to być pomocne dla innych, którzy przeczytają to w przyszłości.
Domyślam się, że przeglądarka nie „wie”, kiedy skończysz rysować. Ale tak jak większość javascript, o ile twój kod działa bez zrzekania się kontroli na przeglądarkę, przeglądarka jest zasadniczo zablokowana i nie może / nie może zaktualizować / odpowiedzieć na swój interfejs użytkownika. Zgaduję, że jeśli wyczyścisz płótno i narysujesz całą ramkę bez zrzekania się kontroli na przeglądarkę, tak naprawdę nie będzie rysować płótna, dopóki nie skończysz.
Jeśli skonfigurujesz sytuację, w której renderowanie obejmuje wiele wywołań setTimeout / setInterval / requestAnimationFrame, w których wyczyścisz kanwę w jednym wywołaniu i narysujesz elementy na płótnie w następnych kilku wywołaniach, powtarzając cykl (na przykład) co 5 wywołań, ja Byłbym skłonny założyć się, że zobaczysz migotanie, ponieważ płótno będzie aktualizowane po każdym wezwaniu.
To powiedziawszy, nie jestem pewien, czy temu ufam. Jesteśmy już w punkcie, w którym javascript jest kompilowany do natywnego kodu maszynowego przed wykonaniem (przynajmniej tak robi silnik V8 Chrome z tego, co rozumiem). Nie zdziwiłbym się, gdyby nie minęło dużo czasu, zanim przeglądarki zaczęły uruchamiać swój javascript w oddzielnym wątku z interfejsu użytkownika i synchronizować dostęp do elementów interfejsu użytkownika, umożliwiając interfejsowi aktualizację / odpowiedź podczas wykonywania javascript, który nie miał dostępu do interfejsu użytkownika. Kiedy / jeśli tak się stanie (i rozumiem, że istnieje wiele przeszkód, które trzeba by pokonać, na przykład programy obsługi zdarzeń uruchamiające się, gdy nadal używasz innego kodu), prawdopodobnie zobaczymy migotanie animacji płótna, które nie używają jakiś rodzaj podwójnego buforowania.
Osobiście podoba mi się pomysł dwóch elementów płótna umieszczonych jeden na drugim i naprzemiennie pokazanego / narysowanego na każdej klatce. Dość dyskretny i prawdopodobnie dość łatwy do dodania do istniejącej aplikacji za pomocą kilku wierszy kodu.
źródło
Dla niewierzących, oto jakiś migający kod. Zauważ, że wyraźnie usuwam poprzednie koło.
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); function draw_ball(ball) { ctx.clearRect(0, 0, 400, 400); ctx.fillStyle = "#FF0000"; ctx.beginPath(); ctx.arc(ball.x, ball.y, 30, 0, Math.PI * 2, true); ctx.closePath(); ctx.fill(); } var deltat = 1; var ball = {}; ball.y = 0; ball.x = 200; ball.vy = 0; ball.vx = 10; ball.ay = 9.8; ball.ax = 0.1; function compute_position() { if (ball.y > 370 && ball.vy > 0) { ball.vy = -ball.vy * 84 / 86; } if (ball.x < 30) { ball.vx = -ball.vx; ball.ax = -ball.ax; } else if (ball.x > 370) { ball.vx = -ball.vx; ball.ax = -ball.ax; } ball.ax = ball.ax / 2; ball.vx = ball.vx * 185 / 186; ball.y = ball.y + ball.vy * deltat + ball.ay * deltat * deltat / 2 ball.x = ball.x + ball.vx * deltat + ball.ax * deltat * deltat / 2 ball.vy = ball.vy + ball.ay * deltat ball.vx = ball.vx + ball.ax * deltat draw_ball(ball); } setInterval(compute_position, 40);
<!DOCTYPE html> <html> <head><title>Basketball</title></head> <body> <canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"> Your browser does not support the canvas element. </canvas> </body></html>
źródło
W przeglądarkach internetowych nie ma migotania! Używają już buforowania dbl do renderowania. Silnik js wykona cały twój rendering przed jego wyświetleniem. Ponadto kontekstowe zapisywanie i przywracanie tylko danych macierzy transformacji stosu i tym podobnych, a nie samej zawartości kanwy. Więc nie potrzebujesz ani nie chcesz buforowania dbl!
źródło
Zamiast tworzyć własną, prawdopodobnie uzyskasz najlepszy przebieg, korzystając z istniejącej biblioteki do tworzenia czystej i wolnej od migotania animacji JavaScript:
Oto popularny: http://processingjs.org
źródło
potrzebujesz 2 płótna: (zwróć uwagę na indeks z css i położenie: bezwzględne)
<canvas id="layer1" width="760" height="600" style=" position:absolute; top:0;left:0; visibility: visible; z-index: 0; solid #c3c3c3;"> Your browser does not support the canvas element. </canvas> <canvas id="layer2" width="760" height="600" style="position:absolute; top:0;left:0; visibility: visible; z-index: 1; solid #c3c3c3;"> Your browser does not support the canvas element. </canvas>
możesz zauważyć, że pierwsze płótno jest widoczne, a drugie jest ukryte. Pomysł polega na narysowaniu na ukrytym, po czym ukryjemy to, co jest widoczne, a ukryte płótno będzie widoczne. kiedy jest ukryty, wyczyść ukryte płótno
<script type="text/javascript"> var buff=new Array(2); buff[0]=document.getElementById("layer1"); buff[1]=document.getElementById("layer2"); ctx[0]=buff[0].getContext("2d"); ctx[1]=buff[1].getContext("2d"); var current=0; // draw the canvas (ctx[ current ]); buff[1- current ].style.visibility='hidden'; buff[ current ].style.visibility='visible'; ctx[1-current].clearRect(0,0,760,600); current =1-current;
źródło
Opera 9.10 jest bardzo powolna i pokazuje proces rysowania. Jeśli chcesz, aby przeglądarka nie używała podwójnego buforowania, wypróbuj Opera 9.10.
Niektórzy sugerowali, że przeglądarki w jakiś sposób określają, kiedy kończy się proces rysowania, ale czy możesz wyjaśnić, jak to działa? Nie zauważyłem żadnego wyraźnego migotania w Firefoksie, Chrome lub IE9, nawet gdy rysowanie jest powolne, więc wydaje się, że to właśnie robią, ale jak to się robi, jest dla mnie tajemnicą. Skąd przeglądarka może kiedykolwiek wiedzieć, że odświeża ekran tuż przed wykonaniem kolejnych instrukcji rysowania? Czy myślisz, że po prostu mierzą czas, więc jeśli interwał dłuższy niż 5 ms minie bez wykonywania instrukcji rysowania płótna, zakłada, że może bezpiecznie zamienić bufory?
źródło
W większości sytuacji nie musisz tego robić, przeglądarka implementuje to za Ciebie. Ale nie zawsze przydatne!
Nadal musisz to zaimplementować, gdy rysunek jest bardzo skomplikowany. Większość częstotliwości odświeżania ekranu wynosi około 60 Hz, co oznacza aktualizację ekranu co 16 ms. Szybkość aktualizacji przeglądarki może zbliżać się do tej liczby. Jeśli Twój kształt wymaga 100 ms do ukończenia, zobaczysz nieukończony kształt. Możesz więc w takiej sytuacji zaimplementować podwójne buforowanie.
Wykonałem test:
Clear a rect, wait for some time, then fill with some color.
jeśli ustawię czas na 10 ms, nie zobaczę migotania. Ale jeśli ustawię to na 20 ms, pojawi się migotanie.źródło