Чи підтримує HTML5 / Canvas подвійну буферизацію?


83

Що я хотів би зробити, це намалювати свою графіку на буфері, а потім мати можливість скопіювати її як є на полотно, щоб я міг робити анімацію та уникати мерехтіння. Але я не міг знайти цей варіант. Хтось знає, як я можу про це піти?


12
Мій досвід полягає в тому, що малювання на полотні з’єднується браузером, щоб анімація була плавною. Чи можете ви поділитися кодом, який мерехтить, коли ви описуєте?
безвічність

2
Я помітив, що IE може мерехтіти в деяких випадках при використанні explorercanvas, але це, звичайно, не HTML5 і є canvasелементом, просто емульованим VML. Я ніколи не бачив, щоб будь-який інший браузер робив це.
кріо

3
Відноситься до stackoverflow.com/questions/11777483
Жульєн

2
Дійсно німий початковий код, який не мерехтить. jsfiddle.net/linuxlizard/ksohjr4f/3 За всіма правами, має мерехтіти. Браузери вражають.
Девід Пул,

Вам потрібна подвійна буферизація, лише якщо у вас є функція асинхронного малювання. Поки ви не поступаєтесь браузеру, тобто робите синхронізацію малювання, у вас все буде добре. Як тільки ви додасте обіцянку або setTimeout або щось інше, ви повертаєтесь до браузера, і він буде малювати поточний стан до того, як його закінчить, ефективно викликаючи мерехтіння.
albertjan

Відповіді:


38

Наступне корисне посилання, крім показаних прикладів та переваг використання подвійної буферизації, показує ще кілька порад щодо ефективності використання елемента полотна 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);

83

Дуже простий метод - мати два елементи полотна в одному і тому ж розташуванні на екрані та встановити видимість для буфера, який потрібно показати. Малюйте на прихованому та перевертайте, коли закінчите.

Деякий код:

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

Не в темі: я особисто люблю користуватися <noscript>і створювати свої елементи полотна в Javascript. Зазвичай ви хочете перевірити підтримку полотна в Javascript у будь-якому випадку, то чому б ви хотіли розмістити резервне повідомлення для полотна у своєму HTML?
Ши

19

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


10
Ну, я помічаю мерехтіння або розрив екрану. Не знаю, як це описати. Використання останнього Chrome на Linux.
grom

14

Ви завжди можете зробити var canvas2 = document.createElement("canvas"); і взагалі не додавати його до DOM.

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


8

Більше ніж через два роки:

Немає необхідності застосовувати подвійну буферизацію вручну. Про це пан Гірі написав у своїй книзі "Полотно HTML5" .

Для ефективного зменшення використання мерехтіння requestAnimationFrame()!


1
Як ви пояснюєте покращення продуктивності, яке спостерігалося за допомогою подвійної буферизації? html5rocks.com/en/tutorials/canvas/performance
Rick Suggs

@ricksuggs Ну, "подвійна буферизація" або "позаекранна передача", згадана на html5rocks, дещо інша, ніж я думав про це. Я розглядав БД як обмін екранними сторінками (у vram), що фактично було лише операцією вказівника замість копіювання фрагментів пам'яті. Оператор попросив використовувати БД, щоб уникнути мерехтіння, на що дійсно звертається requestAnimationFrame (). Можливо, ця стаття про візуалізацію поза екраном може бути цікавою. Там я відповідаю на питання ОП щодо копіювання та вставки даних буферизованих зображень на екран за допомогою буфера спрайтів
ohager

6

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

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

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

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

Особисто мені подобається ідея двох елементів полотна, розташованих один над одним і чергуються, які показано / намальовано на кожному кадрі. Досить ненав’язливий і, мабуть, досить легко додається до існуючого додатка з кількома рядками коду.


Точно так. Дивіться JSFiddle тут stackoverflow.com/questions/11777483 / ... для прикладу.
Ерік

6

Для невіруючих ось кілька мерехтливих кодів. Зверніть увагу, що я явно очищаю, щоб стерти попереднє коло.

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>


4
Здається, це не мерехтить для мене, принаймні, коли куля не рухається більше, ніж її радіус для кожного кадру. Chrome / Windows.
Жуль,

10
Я додав дзвінок до requestAnimFrame - див. Тут - до вашого прикладу на jsfiddle.net/GzSWJ/28 - він більше не мерехтить
rhu

5

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


8
Чи можете ви надати деякі докази, що підтверджують ваші претензії?
Rick Suggs,

@ricksuggs Я вважаю, що не використання БД призводить до поганого ривка в анімації, я ще не пробував БД
FutuToad

3

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

Ось популярний: http://processingjs.org


94
Точно так! Навіщо турбуватися і писати 10 рядків власного коду, коли ви можете просто використовувати цілу бібліотеку розміром 275 КБ, не маючи ні найменшого уявлення про це !? Так, я був саркастичним.
Том,

10
Так, різко?
davidtbernal

3

вам потрібно 2 полотна: (зверніть увагу на z-індекс css та позицію: абсолютну)

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

Приємна реалізація, якщо комусь потрібно застосувати техніку подвійної буферизації вручну. Просто додайте такий рядок: var ctx = new Array (2); до порожнього рядка вашого коду вище.
dimmat

2

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

Деякі люди припускали, що браузери якимось чином визначають, коли закінчується процес малювання, але чи можете ви пояснити, як це може працювати? Я не помітив явного мерехтіння у Firefox, Chrome чи IE9, навіть коли малюнок повільний, тому здається, що це те, що вони роблять, але як це вдається, для мене загадка. Звідки браузер коли-небудь дізнається, що він оновлює дисплей безпосередньо перед тим, як виконуватись інші інструкції з малювання? Чи вважаєте ви, що вони просто встановлюють час, щоб, якщо проходить інтервал більше 5 мс або близько того, не виконуючи інструкції з малювання полотна, він припускав, що він може безпечно міняти місцями буфери?


2

У більшості ситуацій вам не потрібно цього робити, браузер реалізує це для вас. Але не завжди корисно!

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

Я зробив тест: Clear a rect, wait for some time, then fill with some color.якщо встановити час 10 мс, я не побачу мерехтіння. Але якщо я встановив його на 20 мс, трапляється мерехтіння.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.