HTML5 / Canvas는 이중 버퍼링을 지원합니까?


83

내가하고 싶은 것은 버퍼에 그래픽을 그린 다음 캔버스에 그대로 복사하여 애니메이션을 수행하고 깜박임을 피할 수 있도록하는 것입니다. 하지만이 옵션을 찾을 수 없습니다. 내가 어떻게 할 수 있는지 아는 사람 있나요?


12
내 경험으로는 캔버스 그리기가 브라우저에 의해 통합되어 애니메이션이 부드럽습니다. 설명 할 때 깜박이는 코드를 공유 할 수 있습니까?
눈꺼풀 없음

2
IE를 사용할 때 어떤 경우에는 깜박일 수 explorercanvas있지만 물론 HTML5가 canvas아니며 단순히 VML에 의해 에뮬레이트 된 요소입니다. 그래도 다른 브라우저에서는 본 적이 없습니다.
cryo


2
깜박이지 않는 정말 멍청한 초보자 코드. jsfiddle.net/linuxlizard/ksohjr4f/3 모든 권리에 의해 깜박 여야합니다. 브라우저는 인상적입니다.
David Poole

비동기 그리기 기능이있는 경우에만 이중 버퍼링이 필요합니다. 브라우저에 양보하지 않는 한, 즉 그림을 동기식으로 만들면 괜찮습니다. promise 또는 setTimeout 또는 그 안에 무언가를 추가하자마자 브라우저로 돌아와서 효과적으로 깜박임을 유발하기 전에 현재 상태를 그립니다.
albertjan

답변:


38

다음의 유용한 링크는 이중 버퍼링 사용의 예와 장점을 보여주는 것 외에도 html5 canvas 요소 사용에 대한 몇 가지 다른 성능 팁을 보여줍니다. 여기에는 브라우저 전반의 테스트 결과를 Browserscope 데이터베이스로 집계하는 jsPerf 테스트에 대한 링크가 포함되어 있습니다. 이렇게하면 성능 팁이 확인됩니다.

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 []'배열은 두 캔버스 객체를 모두 보유합니다. 따라서 그리기를 시작하려면 여전히 컨텍스트를 가져와야합니다.

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

Off topic : 저는 개인적으로를 사용 <noscript>하고 Javascript로 캔버스 요소를 만들고 싶습니다. 일반적으로 Javascript에서 캔버스 지원을 확인하고 싶은데 HTML에 캔버스 폴백 메시지를 넣는 이유는 무엇입니까?
Shea


14

당신은 항상 할 수 var canvas2 = document.createElement("canvas"); 있고 DOM에 전혀 추가 하지 않을 수 있습니다.

너희들이 display:none; 그것에 집착하는 것처럼 보이기 때문에 그냥 나에게 더 깨끗해 보이고 어색하게 보이지 않는 캔버스를 갖는 것보다 더 정확하게 이중 버퍼링 방식의 아이디어를 모방합니다.


8

2 년 이상 후 :

이중 버퍼링을 '수동'으로 구현할 필요가 없습니다. Geary는 그의 책 "HTML5 Canvas" 에서 이에 대해 썼습니다 .

플리커 사용을 효과적으로 줄이려면 requestAnimationFrame()!


1
이중 버퍼링을 사용하여 목격 된 성능 향상을 어떻게 설명합니까? html5rocks.com/en/tutorials/canvas/performance
Rick Suggs

@ricksuggs 글쎄요, html5rocks에 언급 된 "이중 버퍼링"또는 "오프 스크린 렌더링"은 제가 생각했던 것과 약간 다릅니다. 나는 DB를 스와핑 스크린 페이지 (vram에서)로 생각했는데, 이는 사실상 메모리 청크를 복사하는 대신 포인터 작업 일뿐이었습니다. OP는 깜박임을 피하기 위해 DB를 사용하도록 요청했는데, 이는 실제로 requestAnimationFrame ()에 의해 해결됩니다. 오프 스크린 렌더링에 대한 이 기사 가 흥미로울 수 있습니다. 거기에서 Sprite Buffer를 사용하여 버퍼링 된 이미지 데이터를 화면에 복사하고 붙여 넣는 것에 대한 OP의 질문에 대답합니다.
ohager

6

Josh는 깜박임을 방지하기 위해 브라우저가 "그리기 프로세스가 끝날 때"를 어떻게 인식하는지 물었습니다. 나는 그의 게시물에 직접 댓글을 달았지만 내 담당자는 충분히 높지 않습니다. 또한 이것은 내 의견입니다. 나는 그것을 뒷받침 할 사실이 없지만 그것에 대해 상당히 확신을 가지고 있으며 이것은 미래에 이것을 읽는 다른 사람들에게 도움이 될 수 있습니다.

나는 당신이 그리기를 마쳤을 때 브라우저가 "알지"못하는 것 같다. 그러나 대부분의 자바 스크립트와 마찬가지로 코드가 브라우저에 대한 제어권을 포기하지 않고 실행되는 한 브라우저는 기본적으로 잠겨 있으며 UI를 업데이트하거나 응답 할 수 없습니다. 캔버스를 지우고 브라우저에 대한 제어권을 포기하지 않고 전체 프레임을 그리면 완료 될 때까지 실제로 캔버스를 그리지 않을 것이라고 생각합니다.

렌더링이 여러 setTimeout / setInterval / requestAnimationFrame 호출에 걸쳐있는 상황을 설정하는 경우, 한 번의 호출로 캔버스를 지우고 다음 여러 번의 호출에서 캔버스에 요소를 그려서 5 번 호출 할 때마다주기를 반복합니다. 캔버스가 각 호출 후에 업데이트되기 때문에 깜박임을 볼 수있을 것입니다.

그렇긴하지만 믿을 수 있을지 모르겠습니다. 우리는 이미 자바 스크립트가 실행되기 전에 네이티브 기계 코드로 컴파일되는 시점에 있습니다 (적어도 크롬의 V8 엔진이 내가 이해하는 것입니다). 브라우저가 UI와 별도의 스레드에서 자바 스크립트를 실행하고 UI에 액세스하지 않은 자바 스크립트 실행 중에 UI가 업데이트 / 응답 할 수 있도록 UI 요소에 대한 액세스를 동기화하기까지 그리 오래 걸리지 않았다면 놀라지 않을 것입니다. 그럴 때 (그리고 다른 코드를 실행하는 동안 시작되는 이벤트 핸들러와 같이 극복해야 할 많은 장애물이 있음을 이해합니다), 사용하지 않는 캔버스 애니메이션에서 깜박임이 나타날 수 있습니다. 어떤 종류의 이중 버퍼링.

개인적으로 저는 두 개의 캔버스 요소가 서로 위에 배치되고 각 프레임에 표시 / 그리는 번갈아 표시된다는 아이디어를 좋아합니다. 상당히 방해가되지 않으며 몇 줄의 코드로 기존 애플리케이션에 쉽게 추가 할 수 있습니다.


바로 그거죠. 예제는 여기에서 JSFiddle stackoverflow.com/questions/11777483/… 을 참조하십시오 .
Eric

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
적어도 공이 각 프레임의 반경보다 더 많이 움직이지 않을 때 나는 깜박 거리지 않는 것 같습니다. 크롬 / 윈도우.
Jules

10
나는 requestAnimFrame에 대한 호출을 추가 한 - 참조 여기에 - 당신의 예에에 jsfiddle.net/GzSWJ/28 - 그것은 더 이상 깜빡이지 않다가

5

웹 브라우저에서 깜박임이 없습니다! 그들은 이미 렌더링을 위해 dbl 버퍼링을 사용합니다. Js 엔진은 그것을 보여주기 전에 모든 렌더링을 만듭니다. 또한 컨텍스트는 캔버스 콘텐츠 자체가 아닌 스택 변환 매트릭스 데이터 만 저장하고 복원합니다. 따라서 dbl 버퍼링이 필요하거나 필요하지 않습니다!


8
주장을 뒷받침 할 몇 가지 증거를 제공 할 수 있습니까?
Rick Suggs 2013 년

@ricksuggs 나는 DB를 사용하지 않으면 애니메이션에서 나쁜
멍청함이 발생한다는 것을 알았습니다.

3

직접 롤링하는 대신 깨끗하고 깜박임없는 JavaScript 애니메이션을 만들기 위해 기존 라이브러리를 사용하여 최고의 마일리지를 얻을 수 있습니다.

다음은 인기있는 것입니다 : http://processingjs.org


94
바로 그거죠! 275KB 라이브러리 전체를 전혀 알지 못하더라도 10 줄의 코드를 작성해야하는 이유는 무엇입니까? 네, 비꼬는 거 였어요.
Tom

10
그래, 가혹한?
davidtbernal 2011 년

3

캔버스 2 개가 필요합니다 : (css z-index 및 position : 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;

누군가가 수동으로 이중 버퍼링 기술을 적용해야하는 경우에 좋은 구현입니다. 다음 줄을 추가하면됩니다. var ctx = new Array (2); 위 코드의 빈 줄에.
dimmat

2

Opera 9.10은 매우 느리고 그리기 과정을 보여줍니다. 이중 버퍼링을 사용하지 않는 브라우저를 보려면 Opera 9.10을 사용해보십시오.

어떤 사람들은 브라우저가 그리기 프로세스가 끝나는시기를 결정한다고 제안했지만 어떻게 작동하는지 설명해 주시겠습니까? 그림이 느려도 Firefox, Chrome 또는 IE9에서 뚜렷한 깜박임이 눈에 띄지 않아서 그들이하는 일인 것처럼 보이지만 어떻게 수행되는지는 나에게 미스터리입니다. 더 많은 그리기 명령이 실행되기 직전에 브라우저가 디스플레이를 새로 고치고 있다는 것을 어떻게 알 수 있습니까? 캔버스 그리기 명령을 실행하지 않고 5ms 이상의 간격이 지나면 버퍼를 안전하게 스왑 할 수 있다고 가정합니다.


2

대부분의 경우이 작업을 수행 할 필요가 없으며 브라우저가이를 구현합니다. 그러나 항상 유용하지는 않습니다!

드로잉이 매우 복잡한 경우에도이를 구현해야합니다. 대부분의 화면 업데이트 속도는 약 60Hz로 16ms마다 화면이 업데이트됩니다. 브라우저의 업데이트 속도는이 수치에 가까울 수 있습니다. 모양을 완성하는 데 100ms가 필요한 경우 완성되지 않은 모양이 표시됩니다. 따라서이 상황에서 이중 버퍼링을 구현할 수 있습니다.

테스트를했습니다. Clear a rect, wait for some time, then fill with some color.시간을 10ms로 설정하면 깜박임이 표시되지 않습니다. 하지만 20ms로 설정하면 깜박임이 발생합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.