for*_*yez 81 javascript html5 canvas double-buffering
我想做的是在缓冲区上绘制我的图形然后能够将其原样复制到画布上,这样我就可以做动画并避免闪烁.但我找不到这个选项.谁知道我怎么能这样做?
小智 83
一种非常简单的方法是在同一屏幕位置放置两个canvas元素,并为需要显示的缓冲区设置可见性.完成后绘制隐藏和翻转.
一些代码:
CSS:
canvas { border: 2px solid #000; position:absolute; top:0;left:0;
visibility: hidden; }
Run Code Online (Sandbox Code Playgroud)
翻到JS:
Buffers[1-DrawingBuffer].style.visibility='hidden';
Buffers[DrawingBuffer].style.visibility='visible';
DrawingBuffer=1-DrawingBuffer;
Run Code Online (Sandbox Code Playgroud)
在此代码中,数组'Buffers []'包含两个canvas对象.因此,当您想要开始绘图时,您仍然需要获取上下文:
var context = Buffers[DrawingBuffer].getContext('2d');
Run Code Online (Sandbox Code Playgroud)
ric*_*ggs 35
除了显示使用双缓冲的示例和优点之外,以下有用链接还显示了使用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);
Run Code Online (Sandbox Code Playgroud)
小智 19
我测试过的浏览器都是通过不重新绘制画布来处理这个缓冲,直到绘制框架的代码完成为止.另请参阅WHATWG邮件列表:http://www.mail-archive.com/whatwg@lists.whatwg.org/msg19969.html
Dea*_*con 13
你可以随时做,
var canvas2 = document.createElement("canvas");
而不是将它附加到DOM.
只是说,因为你们似乎display:none;
对它如此着迷,对我来说似乎更清洁,并且比仅仅拥有一个笨拙的看不见的画布更能准确地模仿双缓冲的想法.
小智 6
对于不信的人来说,这里有一些闪烁的代码.请注意,我明确清除以删除上一个圆圈.
http://coderextreme.net/basketball2.html(http://jsfiddle.net/GzSWJ/)
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);
Run Code Online (Sandbox Code Playgroud)
乔希(一会儿)询问浏览器如何知道"绘图过程何时结束",以避免闪烁.我会直接评论他的帖子,但我的代表不够高.这也只是我的意见.我没有事实来支持它,但我对它有相当的信心,这可能对将来读这篇文章有帮助.
我猜测你完成绘图时浏览器并不"知道".但就像大多数javascript一样,只要你的代码在没有放弃对浏览器的控制的情况下运行,浏览器就会被锁定,并且不会/不能更新/响应它的UI.我猜你如果清除画布并绘制整个画面而不放弃对浏览器的控制,那么在你完成之前它实际上不会绘制你的画布.
如果你设置了一个渲染跨越多个setTimeout/setInterval/requestAnimationFrame调用的情况,你在一个调用中清除画布并在接下来的几个调用中在画布上绘制元素,重复循环(例如)每5个调用,I我愿意打赌你会看到闪烁,因为画布会在每次通话后更新.
那就是说,我不确定我是否相信.我们已经准备好在执行之前将javascript编译成本地机器代码(至少这是Chrome的V8引擎根据我的理解做的).如果浏览器开始在UI的单独线程中运行javascript并同步对UI元素的任何访问,允许UI在未执行UI的javascript执行期间更新/响应,我不会感到惊讶.当/如果发生这种情况(并且我知道有许多障碍需要克服,例如当你还在运行其他代码时事件处理程序开始),我们可能会看到没有使用的画布动画上的闪烁某种双缓冲.
就个人而言,我喜欢将两个帆布元素放在彼此顶部并且在每个框架上显示/绘制的交替的想法.相当简单,可能很容易添加到几行代码的现有应用程序.
小智 5
网络浏览器没有闪烁!他们已经使用dbl缓冲进行渲染.Js引擎会在显示之前完成所有渲染.此外,上下文保存和恢复只有堆栈转换矩阵数据等,而不是画布内容本身.所以,你不需要或不想要dbl缓冲!