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


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

12 69

12 ответов:

Следующая полезная ссылка, в дополнение к показу примеров и преимуществ использования двойной буферизации, показывает несколько других советов по производительности для использования элемента HTML5 canvas. Он включает ссылки на тесты см. Этот тест 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 []' содержит оба объекта canvas. Поэтому, когда вы хотите начать рисовать, вам все равно нужно получить контекст:

var context = Buffers[DrawingBuffer].getContext('2d');

браузеры я проверил все обрабатывать эту буферизацию для вас, не перекрашивая холст, пока код, который рисует ваш кадр не будет завершен. Смотрите также список рассылки WHATWG:http://www.mail-archive.com/whatwg@lists.whatwg.org/msg19969.html

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

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

более двух лет спустя:

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

для эффективного уменьшения мерцания используйте requestAnimationFrame()!

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

http://coderextreme.net/basketball2.html ( http://jsfiddle.net/GzSWJ/ )

<!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>
<script>
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);
</script></body></html>

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

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

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

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

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

там нет мерцания в веб-браузерах! Они уже используют буферизацию dbl для их рендеринга. Движок JavaScript сделает все свои визуализации, прежде чем показывать его. Кроме того, контекст сохраняет и восстанавливает только данные трансформационной матрицы стека и т. д., а не само содержимое холста. Таким образом, вам не нужна или не нужна буферизация dbl!

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

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

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

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

вам нужно 2 холст: (обратите внимание на css z-индекс и положение: абсолютный)

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

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

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

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