2010-05-08 40 views
68

我想要做的是在缓冲区上绘制图形,然后将其原样复制到画布上,这样我就可以进行动画并避免闪烁。但我找不到这个选项。任何人都知道我可以如何去做这件事?HTML5/Canvas支持双缓冲吗?

+12

我的经验是,帆布画是由浏览器合并,使动画顺利。你能分享一些闪烁的代码吗? – eyelidlessness 2010-05-08 19:10:42

+2

我注意到IE在使用'explorercanvas'时可能会闪烁,但这当然不是HTML5,并且是仅由VML模拟的'canvas'元素。我从来没有见过其他浏览器这样做。 – cryo 2010-05-19 10:16:54

+3

相关http://stackoverflow.com/questions/11777483/ – julien 2012-11-29 14:48:12

回答

18

下有用的链接,除了展示的例子,并使用双缓冲的优势,展示了使用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); 
3

而不是滚动您自己,你可能会使用现有的库,以获得最佳里程为创造清洁,无闪烁的JavaScript动画:

这里有一个流行的一种:http://processingjs.org

+91

正是!为什么打扰并写出自己的代码10行,当你可以使用一个完整的275KB库而没有丝毫的线索!?是的,我正在讽刺。 – Tom 2010-05-23 15:33:34

+8

Yeesh,苛刻多少? – notJim 2011-01-28 04:54:30

80

一非常简单的方法是在同一屏幕位置放置两个画布元素,并为您需要显示的缓冲区设置可见性。当你完成后,在隐藏处画画并翻转。

某些代码:

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; 

在此代码阵列 '缓冲器[]' 同时拥有画布对象。所以,当你要开始绘制你仍然需要获得上下文:

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

不错的解决方案:D – 2011-07-05 13:36:21

+0

偏题:我个人喜欢用'

17

浏览器我测试过的所有,直到吸引你的框架代码完成时不重画在画布上处理这个缓冲你。请参阅WHATWG邮件列表:http://www.mail-archive.com/[email protected]/msg19969.html

+9

好吧,我注意到闪烁或屏幕撕裂。不知道如何描述它。在Linux上使用最新的Chrome。 – grom 2012-07-27 04:45:17

5

网页浏览器中没有闪烁!他们已经使用dbl缓冲来渲染它们。 Js引擎将在展示之前进行所有渲染。此外,上下文保存和恢复仅堆栈转换矩阵数据等,而不是画布内容本身。 所以,你不需要或不需要DBL缓冲!

+8

你能提供一些证据来支持你的说法吗? – ricksuggs 2013-10-11 13:54:59

+0

@ricksuggs我发现在动画中没有使用数据库结果动不动,我还没有尝试过数据库 – FutuToad 2014-08-18 14:19:00

2

Opera 9.10非常慢并显示绘图过程。如果你想看到浏览器不使用双缓冲,请尝试Opera 9.10。

有些人建议浏览器以某种方式确定绘图过程何时结束,但您能否解释这可以如何工作?即使画图很慢,我也没有注意到Firefox,Chrome或IE9中有任何明显的闪烁,所以看起来这就是他们正在做的事情,但是如何实现这一点对我来说是个谜。在更多的绘图指令被执行之前,浏览器如何知道它正在刷新显示?你认为他们只是计时吗?如果超过5毫秒的时间间隔没有执行画布绘图指令,它会认为它可以安全地交换缓冲区?

2

需要2个帆布:(注意CSS的z-index和位置:绝对)

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

对于不信的人,这里有一些闪烁的代码。请注意,我明确地清除了以前的圈子。

http://coderextreme.net/basketball2.htmlhttp://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> 
+3

对我来说,似乎并没有闪烁,至少在球没有移动超过其每半帧的半径时。镀铬/ Windows操作系统。 – Jules 2012-07-16 12:01:28

+8

我在http://jsfiddle.net/GzSWJ/28上添加了一个对requestAnimFrame的调用 - 参见[这里](http://paulirish.com/2011/requestanimationframe-for-smart-animating/) - 你的例子/ - 它不再闪烁 – rhu 2012-09-18 10:41:52

7

乔什问(而回)关于浏览器如何知道“什么时候绘制过程结束”,以避免闪烁。我会直接评论他的帖子,但我的代表不够高。这也只是我的看法。我没有支持它的事实,但我对此相当有信心,未来读者可能会对此有所帮助。

我猜测你完成绘图时浏览器并不知道。但是就像大多数JavaScript一样,只要代码在不放弃对浏览器的控制权的情况下运行,浏览器就会被锁定,并且不会更新/响应其UI。我在猜测,如果你清除画布并画出整个画面而不放弃对浏览器的控制,那么在完成之前它不会实际绘制画布。

如果您设置了一种情况,即您的渲染跨越多个setTimeout/setInterval/requestAnimationFrame调用,您可以在一次调用中清除画布,并在接下来的几次调用中在画布上绘制元素,每次重复循环5次电话,我愿意打赌你会看到闪烁,因为每次通话后画布都会更新。

这就是说,我不确定我会相信这一点。我们已经认识到,JavaScript在执行之前会被编译为本地机器代码(至少这是Chrome的V8引擎根据我的理解所做的)。如果浏览器在与用户界面不同的线程中开始运行它们的javascript并同步对UI元素的任何访问,以允许UI在不访问UI的JavaScript执行期间更新/响应,我不会感到惊讶。当/如果发生这种情况(并且我知道有许多障碍需要克服,例如事件处理程序在您仍在运行其他代码时开始运行),我们可能会看到未使用的画布动画闪烁某种双缓冲。

就我个人而言,我喜欢将两个画布元素放置在彼此顶部并交替显示/在每个画面上绘制的想法。相当不引人注意,可能很容易通过几行代码添加到现有应用程序中。

+0

正确。例如,查看JSFiddle http://stackoverflow.com/questions/11777483/do-we-need-to-implement-double-buffering-ourselves-with-canvas。 – Eric 2013-08-01 03:51:27

1

在大多数情况下,您不需要执行此操作,浏览器会为您执行此操作。 但并不总是有用!

当你的绘图非常复杂时,你仍然需要实现这一点。 大部分的屏幕更新率大约是60Hz,这意味着屏幕每16ms更新一次。浏览器的更新率可能接近这个数字。如果你的形状需要100ms完成,你会看到一个未完成的形状。所以你可以在这种情况下实现双缓冲。

我做了一个测试:Clear a rect, wait for some time, then fill with some color.如果我把时间设置为10ms,我就不会看到闪烁。但是如果我将其设置为20ms,闪烁就会发生。

7

两年多后:

没有必要'手动'实现双缓冲。 Geary先生在他的书"HTML5 Canvas"中写了这个。

为了有效减少闪烁使用requestAnimationFrame()

+0

您如何解释使用双缓冲技术所见证的性能改进? http://www.html5rocks.com/en/tutorials/canvas/performance/ – ricksuggs 2013-10-11 13:58:52

+0

@ricksuggs那么,html5rocks上提到的“双缓冲”或“离屏渲染”与我想的有点不同。我认为DB是交换屏幕页面(在vram中),这实际上只是一个指针操作,而不是复制内存块。 OP要求使用DB来避免闪烁,这是通过requestAnimationFrame()真正解决的。也许这个[文章](http://tinyurl.com/lpnkfnj)关于屏外渲染可能很有趣。在那里,我回答了关于使用Sprite Buffer – ohager 2014-06-04 17:21:55

9

你总是可以做 var canvas2 = document.createElement("canvas"); 而不是将它追加到DOM。

只是说,因为你们似乎很迷恋display:none; 它只是看起来更清洁,模仿双缓冲方式的想法比只是一个笨拙的无形画布更准确。

+1

le在4岁面包上烤面包的方法将缓冲图像数据复制并粘贴到屏幕上的问题:-DD – DeadlyBacon 2016-01-16 12:23:46

+1

答案仍然帮助我:D – AME 2017-06-09 07:59:44