Я бы хотел нарисовать свою графику в буфере, а затем иметь возможность скопировать ее как есть на холст, чтобы я мог делать анимацию и избегать мерцания. Но я не нашла такой вариант. Кто-нибудь знает, как я могу это сделать?
Поддерживает ли HTML5 / Canvas двойную буферизацию?
Ответы (12)
Следующая полезная ссылка, помимо демонстрации примеров и преимуществ использования двойной буферизации, показывает несколько других советов по повышению производительности при использовании элемента холста 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?
- person Shea; 15.07.2012
Все браузеры, которые я тестировал, обрабатывают эту буферизацию за вас, не перерисовывая холст до тех пор, пока код, отрисовывающий ваш фрейм, не будет завершен. См. Также список рассылки WHATWG: http://www.mail-archive.com/[email protected]/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 мс, мерцание произойдет.
explorercanvas
, но это, конечно, не HTML5, а элементcanvas
, просто эмулируемый VML. Однако я никогда не видел, чтобы другие браузеры делали это. - person cryo   schedule 19.05.2010