Поддерживает ли HTML5 / Canvas двойную буферизацию?

Я бы хотел нарисовать свою графику в буфере, а затем иметь возможность скопировать ее как есть на холст, чтобы я мог делать анимацию и избегать мерцания. Но я не нашла такой вариант. Кто-нибудь знает, как я могу это сделать?


person Shai UI    schedule 08.05.2010    source источник
comment
По моему опыту, рисование на холсте объединяется браузером, поэтому анимация получается плавной. Можете ли вы поделиться кодом, который мерцает, когда вы описываете?   -  person eyelidlessness    schedule 08.05.2010
comment
Я заметил, что IE в некоторых случаях может мерцать при использовании explorercanvas, но это, конечно, не HTML5, а элемент canvas, просто эмулируемый VML. Однако я никогда не видел, чтобы другие браузеры делали это.   -  person cryo    schedule 19.05.2010
comment
Связано с stackoverflow.com/questions/11777483   -  person julien    schedule 29.11.2012
comment
Действительно тупой код для новичков, который не мерцает. jsfiddle.net/linuxlizard/ksohjr4f/3 По всем правам, должно мерцать. Браузеры впечатляют.   -  person David Poole    schedule 21.08.2014
comment
Вам нужна только двойная буферизация, если у вас есть функция асинхронного рисования. Пока вы не уступаете браузеру, т.е. делаете рисование синхронным, все будет в порядке. Как только вы добавляете туда обещание или setTimeout или что-то в этом роде, вы возвращаетесь к браузеру, и он отрисовывает текущее состояние до его завершения, вызывая мерцание.   -  person albertjan    schedule 10.01.2018


Ответы (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);
person Rick Suggs    schedule 27.04.2012

Очень простой метод - разместить два элемента холста в одном месте экрана и установить видимость буфера, который вам нужно показать. Нарисуйте скрытое и переверните, когда закончите.

Некоторый код:

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');
person Fedor van Eldijk    schedule 19.05.2010
comment
Не по теме: мне лично нравится использовать <noscript> и создавать элементы холста в Javascript. Как правило, вы все равно захотите проверить поддержку холста в Javascript, так зачем вам помещать резервное сообщение холста в свой HTML? - person Shea; 15.07.2012

Все браузеры, которые я тестировал, обрабатывают эту буферизацию за вас, не перерисовывая холст до тех пор, пока код, отрисовывающий ваш фрейм, не будет завершен. См. Также список рассылки WHATWG: http://www.mail-archive.com/[email protected]/msg19969.html

person Edward Coffey    schedule 28.02.2011
comment
Ну замечаю мерцание или разрыв экрана. Не знаю, как это описать. Используется последняя версия Chrome в Linux. - person grom; 27.07.2012

Вы всегда можете сделать var canvas2 = document.createElement("canvas"); и вообще не добавлять его в DOM.

Просто скажу, что, поскольку вы, ребята, кажетесь настолько одержимыми display:none;, мне кажется, что это чище и точнее имитирует идею двойной буферизации, чем просто неловко невидимый холст.

person DeadlyBacon    schedule 16.01.2016

Более чем через два года:

Нет необходимости «вручную» реализовывать двойную буферизацию. Гири написал об этом в своей книге "HTML5 Canvas".

Чтобы эффективно уменьшить мерцание, используйте requestAnimationFrame()!

person ohager    schedule 09.10.2013
comment
Как вы объясните улучшение производительности, которое наблюдалось при использовании двойной буферизации? html5rocks.com/en/tutorials/canvas/performance - person Rick Suggs; 11.10.2013
comment
@ricksuggs Что ж, двойная буферизация или закадровый рендеринг, упомянутые в html5rocks, немного отличаются от того, о чем я думал. Я рассматривал DB как замену экранных страниц (во vram), что фактически было только операцией указателя, а не копированием фрагментов памяти. OP попросил использовать БД, чтобы избежать мерцания, что действительно решается requestAnimationFrame (). Возможно, эта статья о внеэкранном рендеринге может быть интересной. Там я отвечаю на вопрос OP о копировании и вставке буферизованных данных изображения на экран с помощью Sprite Buffer. - person ohager; 04.06.2014

Джош спросил (некоторое время назад), как браузер узнает, «когда заканчивается процесс рисования», чтобы избежать мерцания. Я бы прокомментировал прямо к его сообщению, но моя репутация недостаточно высока. Также это только мое мнение. У меня нет фактов, подтверждающих это, но я довольно уверен в этом, и это может быть полезно для других, читающих это в будущем.

Я предполагаю, что браузер не «знает», когда вы закончили рисовать. Но, как и большинство javascript, пока ваш код работает без передачи управления браузеру, браузер по существу заблокирован и не может / не может обновлять / отвечать на свой пользовательский интерфейс. Я предполагаю, что если вы очистите холст и нарисуете весь свой фрейм, не передавая управление браузеру, он не будет рисовать ваш холст, пока вы не закончите.

Если вы настроили ситуацию, когда ваш рендеринг охватывает несколько вызовов setTimeout / setInterval / requestAnimationFrame, когда вы очищаете холст за один вызов и рисуете элементы на своем холсте в следующих нескольких вызовах, повторяя цикл (например) каждые 5 вызовов, я Готов поспорить, что вы увидите мерцание, поскольку холст будет обновляться после каждого вызова.

Тем не менее, я не уверен, что доверяю этому. Мы уже достигли того момента, когда javascript компилируется в собственный машинный код перед выполнением (по крайней мере, это то, что делает движок Chrome V8, насколько я понимаю). Я не был бы удивлен, если бы не прошло много времени, прежде чем браузеры начали запускать свой javascript в отдельном потоке от пользовательского интерфейса и синхронизировать любой доступ к элементам пользовательского интерфейса, позволяя пользовательскому интерфейсу обновляться / отвечать во время выполнения javascript, который не имел доступа к пользовательскому интерфейсу. Когда / если это произойдет (и я понимаю, что необходимо преодолеть множество препятствий, таких как запуск обработчиков событий, пока вы все еще выполняете другой код), мы, вероятно, увидим мерцание на анимации холста, которая не использует какая-то двойная буферизация.

Лично мне нравится идея двух элементов холста, расположенных друг над другом и чередующихся, которые отображаются / рисуются на каждом кадре. Довольно ненавязчивый и, вероятно, довольно легко добавляемый в существующее приложение с помощью нескольких строк кода.

person Lee    schedule 25.09.2012
comment
Точно. См. JSFiddle здесь stackoverflow.com/questions/11777483/ для примера. - person Eric; 01.08.2013

Для неверующих вот мерцающий код. Обратите внимание, что я явно очищаю предыдущий круг, чтобы стереть его.

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>

person John    schedule 05.06.2012
comment
Мне не кажется, что он мерцает, по крайней мере, когда мяч не движется больше, чем на свой радиус каждый кадр. Chrome / Windows. - person Jules; 16.07.2012
comment
Я добавил вызов requestAnimFrame - см. здесь - ваш пример на jsfiddle.net/GzSWJ/28 - больше не мерцает - person rhu; 18.09.2012

В браузерах нет мерцания! Они уже используют буферизацию dbl для рендеринга. Js-движок выполнит весь ваш рендеринг перед его показом. Кроме того, контекстное сохранение и восстановление только данных матрицы преобразования стека и т.п., а не самого содержимого холста. Итак, вам не нужна и не нужна буферизация dbl!

person Luka    schedule 06.03.2011
comment
Можете ли вы предоставить доказательства, подтверждающие ваши утверждения? - person Rick Suggs; 11.10.2013
comment
@ricksuggs Я обнаружил, что неиспользование БД приводит к резким рывкам в анимации, я еще не пробовал БД - person FutuToad; 18.08.2014

Вместо того, чтобы использовать свою собственную, вы, вероятно, получите максимальную отдачу, используя существующую библиотеку для создания чистой и немерцающей анимации JavaScript:

Вот популярный: http://processingjs.org

person a7drew    schedule 12.05.2010
comment
Точно! Зачем писать 10 строк собственного кода, если можно просто использовать целую библиотеку 275 КБ, не имея ни малейшего представления об этом !? Да, я был саркастичен. - person Tom; 23.05.2010

вам нужно 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;
person Aviad Gispan    schedule 14.04.2012
comment
Хорошая реализация, если кому-то нужно вручную применить технику двойной буферизации. Просто добавьте следующую строку: var ctx = new Array (2); в пустую строку в вашем коде выше. - person dimmat; 02.08.2019

Opera 9.10 работает очень медленно и показывает процесс рисования. Если вы хотите, чтобы браузер не использовал двойную буферизацию, попробуйте Opera 9.10.

Некоторые люди предположили, что браузеры каким-то образом определяют, когда процесс рисования заканчивается, но можете ли вы объяснить, как это может работать? Я не заметил явного мерцания в Firefox, Chrome или IE9, даже когда отрисовка идет медленно, поэтому кажется, что они делают именно это, но как это достигается, для меня загадка. Как браузер может когда-либо узнать, что он обновляет дисплей непосредственно перед выполнением других инструкций по рисованию? Как вы думаете, они просто рассчитывают время, поэтому, если интервал более 5 мс или около того проходит без выполнения инструкции по рисованию холста, предполагается, что он может безопасно менять буферы?

person Josh    schedule 23.01.2012

В большинстве случаев этого делать не нужно, браузер реализует это за вас. Но не всегда полезно!

Вам все равно придется реализовать это, когда ваш рисунок очень сложен. Большая часть частоты обновления экрана составляет около 60 Гц, это означает, что экран обновляется каждые 16 мс. Скорость обновления браузера может приближаться к этому числу. Если для завершения вашей фигуры требуется 100 мс, вы увидите незавершенную фигуру. Таким образом, вы можете реализовать двойную буферизацию в этой ситуации.

Я провел тест: Clear a rect, wait for some time, then fill with some color. Если я установлю время на 10 мс, я не увижу мерцания. Но если я выставлю 20 мс, мерцание произойдет.

person Chien-Wei Huang    schedule 16.06.2013