Я бы хотел нарисовать свою графику в буфере, а затем иметь возможность скопировать ее как есть на холст, чтобы я мог делать анимацию и избегать мерцания. Но я не нашел такой вариант. Кто-нибудь знает, как я могу это сделать?
javascript
html
canvas
double-buffering
Shai UI
источник
источник
explorercanvas
, но это, конечно, не HTML5, аcanvas
просто эмулируемый VML элемент. Однако я никогда не видел, чтобы другие браузеры делали это.Ответы:
Следующая полезная ссылка, помимо демонстрации примеров и преимуществ использования двойной буферизации, показывает несколько других советов по повышению производительности при использовании элемента холста html5. Он включает ссылки на тесты jsPerf, которые объединяют результаты тестов по браузерам в базу данных Browserscope. Это гарантирует, что советы по производительности проверены.
http://www.html5rocks.com/en/tutorials/canvas/performance/
Для вашего удобства я включил минимальный пример эффективной двойной буферизации, как описано в статье.
// 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);
источник
Очень простой метод - иметь два элемента холста в одном месте экрана и установить видимость буфера, который вам нужно показать. Нарисуйте скрытое и переверните, когда закончите.
Некоторый код:
CSS:
canvas { border: 2px solid #000; position:absolute; top:0;left:0; visibility: hidden; }
Листаем в JS:
Buffers[1-DrawingBuffer].style.visibility='hidden'; Buffers[DrawingBuffer].style.visibility='visible'; DrawingBuffer=1-DrawingBuffer;
В этом коде массив Buffers [] содержит оба холста-объекта. Поэтому, когда вы хотите начать рисовать, вам все равно нужно получить контекст:
var context = Buffers[DrawingBuffer].getContext('2d');
источник
<noscript>
и создавать элементы холста в Javascript. Как правило, вы все равно захотите проверить поддержку холста в Javascript, так зачем вам вставлять резервное сообщение холста в свой HTML?Все браузеры, которые я тестировал, обрабатывают эту буферизацию за вас, не перерисовывая холст до тех пор, пока не будет завершен код, отрисовывающий ваш фрейм. См. Также список рассылки WHATWG: http://www.mail-archive.com/whatwg@lists.whatwg.org/msg19969.html
источник
Вы всегда можете сделать это
var canvas2 = document.createElement("canvas");
и вообще не добавлять его в DOM.Просто сказать, что раз уж вы, ребята, так одержимы,
display:none;
мне кажется, что это чище и точнее имитирует идею двойной буферизации, чем просто наличие неловко невидимого холста.источник
Более двух лет спустя:
Нет необходимости «вручную» реализовывать двойную буферизацию. Об этом г-н Гири написал в своей книге «HTML5 Canvas» .
Для эффективного уменьшения мерцания используйте
requestAnimationFrame()
!источник
Джош спросил (некоторое время назад), как браузер узнает, «когда процесс рисования заканчивается», чтобы избежать мерцания. Я бы прокомментировал его пост, но моя репутация недостаточно высока. Также это только мое мнение. У меня нет фактов, подтверждающих это, но я вполне уверен в этом, и это может быть полезно другим, читающим это в будущем.
Я предполагаю, что браузер не «знает», когда вы закончили рисовать. Но, как и большинство javascript, пока ваш код работает без передачи управления браузеру, браузер по существу заблокирован и не может / не может обновлять / отвечать на свой пользовательский интерфейс. Я предполагаю, что если вы очистите холст и нарисуете весь свой фрейм, не передавая управление браузеру, он не будет рисовать ваш холст, пока вы не закончите.
Если вы настроили ситуацию, когда ваш рендеринг охватывает несколько вызовов setTimeout / setInterval / requestAnimationFrame, когда вы очищаете холст за один вызов и рисуете элементы на своем холсте в следующих нескольких вызовах, повторяя цикл (например) каждые 5 вызовов, я Готов поспорить, что вы увидите мерцание, так как холст будет обновляться после каждого вызова.
Тем не менее, я не уверен, что доверяю этому. Мы уже достигли того момента, когда javascript компилируется в собственный машинный код перед выполнением (по крайней мере, это то, что делает движок Chrome V8, насколько я понимаю). Я не удивлюсь, если не прошло много времени, прежде чем браузеры начали запускать свой javascript в отдельном потоке от пользовательского интерфейса и синхронизировать любой доступ к элементам пользовательского интерфейса, позволяя пользовательскому интерфейсу обновлять / отвечать во время выполнения javascript, который не имел доступа к пользовательскому интерфейсу. Когда / если это произойдет (и я понимаю, что необходимо преодолеть множество препятствий, таких как запуск обработчиков событий, пока вы все еще выполняете другой код), мы, вероятно, увидим мерцание на анимации холста, которая не использует какая-то двойная буферизация.
Лично мне нравится идея двух элементов холста, расположенных друг над другом и чередующихся, которые отображаются / рисуются на каждом кадре. Довольно ненавязчивый и, вероятно, довольно легко добавляемый в существующее приложение с помощью нескольких строк кода.
источник
Для неверующих вот мерцающий код. Обратите внимание, что я явно очищаю предыдущий круг, чтобы стереть его.
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>
источник
В браузерах нет мерцания! Они уже используют буферизацию dbl для рендеринга. Js-движок выполнит весь ваш рендеринг перед его показом. Кроме того, контекстное сохранение и восстановление только данных матрицы преобразования стека и т.п., но не самого содержимого холста. Итак, вам не нужна и не нужна буферизация dbl!
источник
Вместо того, чтобы использовать свою собственную, вы, вероятно, извлечете максимальную пользу, используя существующую библиотеку для создания чистой и немерцающей анимации JavaScript:
Вот популярный: http://processingjs.org
источник
вам нужно 2 холста: (обратите внимание на z-index css и положение: absolute)
<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>
вы можете заметить, что первый холст виден, а второй - скрыт. Идея рисовать на скрытом, после чего мы скроем видимое и сделаем скрытый холст видимым. когда он скрыт 'очистить скрытый холст
<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;
источник
Opera 9.10 работает очень медленно и показывает процесс рисования. Если вы хотите, чтобы браузер не использовал двойную буферизацию, попробуйте Opera 9.10.
Некоторые люди предположили, что браузеры каким-то образом определяют, когда процесс рисования заканчивается, но можете ли вы объяснить, как это может работать? Я не заметил явного мерцания в Firefox, Chrome или IE9, даже когда отрисовка идет медленно, поэтому кажется, что это именно то, что они делают, но как это делается, для меня загадка. Как браузер может когда-либо узнать, что он обновляет отображение непосредственно перед выполнением других инструкций по рисованию? Как вы думаете, они просто рассчитывают время, чтобы, если интервал более 5 мс или около того проходит без выполнения инструкции по рисованию холста, предполагается, что он может безопасно менять буферы?
источник
В большинстве случаев этого делать не нужно, браузер реализует это за вас. Но не всегда полезно!
Вам все равно придется реализовать это, когда ваш рисунок очень сложен. Большая часть частоты обновления экрана составляет около 60 Гц, это означает, что экран обновляется каждые 16 мс. Частота обновления браузера может приближаться к этому числу. Если для завершения вашей фигуры требуется 100 мс, вы увидите незавершенную фигуру. Таким образом, вы можете реализовать двойную буферизацию в этой ситуации.
Я провел тест:
Clear a rect, wait for some time, then fill with some color.
если я установлю время на 10 мс, я не увижу мерцания. Но если установить 20 мс, мерцание будет.источник