HTML5 / Canvas支持双caching吗?
我想要做的是将我的graphics绘制在一个缓冲区,然后能够将其原样复制到canvas上,以便我可以进行animation并避免闪烁。 但是我找不到这个选项。 任何人都知道我可以怎么做呢?
一个非常简单的方法是在同一屏幕位置放置两个canvas元素,并为需要显示的缓冲区设置可见性。 当你完成后,在隐藏处画画并翻转。
一些代码:
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');
我testing过的浏览器都是通过在绘制帧的代码完成之前不重绘canvas来处理这个缓冲。 另见WHATWG邮件列表: http : //www.mail-archive.com/whatwg@lists.whatwg.org/msg19969.html
除了显示使用双缓冲的例子和优点之外,下面的有用链接还显示了使用html5 canvas元素的其他几个性能提示。 它包含jsPerftesting的链接,它将浏览器的testing结果汇总到一个Browserscope数据库中。 这可确保性能提示得到validation。
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);
你总是可以做var canvas2 = document.createElement("canvas");
而不是将其附加到DOM。
只是说,因为你们似乎对display:none;
痴迷display:none;
它看起来比较干净,而且比只有一个笨拙的无形canvas更能准确地模仿双缓冲方式。
Josh询问了浏览器如何知道“绘图过程何时结束”,以避免闪烁。 我会直接评论他的职位,但我的代表不够高。 这也只是我的意见。 我没有支持的事实,但我对此相当有信心,未来可能会对读者有所帮助。
我猜测,当你完成绘图的时候,浏览器并不知道。 但是就像大多数JavaScript一样,只要代码在不放弃对浏览器的控制的情况下运行,浏览器就会被locking,不会更新/响应其UI。 我猜测如果你清除canvas并画出整个画面而不放弃对浏览器的控制,那么在完成之前它不会真正画出canvas。
如果您设置了一种情况,即您的渲染跨越了多个setTimeout / setInterval / requestAnimationFrame调用,您可以在一次调用中清除canvas,并在接下来的几次调用中在canvas上绘制元素,例如每5次调用一次我敢打赌你会看到闪烁,因为canvas会在每次通话后更新。
也就是说,我不确定我会相信这一点。 我们已经认识到,在执行之前,javascript被编译为本地机器代码(至less这是Chrome的V8引擎根据我的理解所做的)。 如果浏览器在与用户界面不同的线程中开始运行他们的javascript并同步对UI元素的任何访问,允许用户界面在不执行访问UI的JavaScript执行期间更新/响应,我不会感到惊讶。 当/如果发生这种情况(我知道有许多障碍需要克服,比如事件处理程序在你还在运行其他代码的时候启动),我们可能会看到canvas上的animation不闪烁某种双缓冲。
就我个人而言,我喜欢将两个帆布元素放置在彼此顶部并交替显示/绘制在每个框架上的想法。 相当不引人注意,可能很容易用几行代码添加到现有的应用程序中。
两年多后:
不需要“手动”实现双缓冲。 Geary先生在他的书“HTML5 Canvas”中写了这个。
为了有效减less闪烁使用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>
网页浏览器没有闪烁! 他们已经使用dbl缓冲他们的渲染。 Js引擎会在显示之前进行所有渲染。 此外,上下文保存和恢复只是堆栈转换matrix数据等,而不是canvas内容本身。 所以,你不需要或者不需要dbl缓冲!
您可能会通过使用现有的库来创build干净,无闪烁的JavaScriptanimation,从而获得最佳的里程数:
这是一个stream行的: http : //processingjs.org
歌剧9.10是非常缓慢的,并显示绘制过程。 如果你想看到一个浏览器不使用双缓冲,请尝试Opera 9.10。
有些人build议浏览器以某种方式确定绘图过程何时结束,但是你能解释一下怎样才能工作? 我还没有注意到Firefox,Chrome或者IE9中的任何明显的闪烁,即使画图很慢,所以看起来这就是他们正在做的事情,但是如何实现对我来说是个谜。 在更多的绘图指令被执行之前,浏览器如何知道刷新显示? 你认为他们只是时间,所以如果超过5毫秒左右的时间间隔没有执行canvas绘图指令,它假定它可以安全地交换缓冲区?
你需要2个canvas:(注意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>
您可以注意到第一个canvas是可见的,第二个canvas隐藏了在隐藏之后绘制的想法,之后我们将隐藏可见并使隐藏的canvas可见。 当它隐藏“清晰的隐藏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;
在大多数情况下,你不需要这样做,浏览器为你实现这一点。 但并不总是有用的!
当您的绘图非常复杂时,您仍然必须执行此操作。 大部分的屏幕更新率大约是60Hz,这意味着屏幕每16ms更新一次。 浏览器的更新率可能接近这个数字。 如果你的形状需要100ms完成,你会看到一个不完整的形状。 所以你可以在这种情况下实现双缓冲。
我做了一个testing: Clear a rect, wait for some time, then fill with some color.
如果我把时间设定为10ms,我不会看到闪烁。 但如果我把它设置为20ms,闪烁发生。