我想做的是在缓冲区上绘制图形,然后将其原样复制到画布上,这样我就可以制作动画并避免闪烁。但是我找不到这个选项。有人知道我该怎么做吗?
explorercanvas
,但这当然不是HTML5,它canvas
只是由VML模拟的元素。我从未见过其他浏览器可以做到这一点。
我想做的是在缓冲区上绘制图形,然后将其原样复制到画布上,这样我就可以制作动画并避免闪烁。但是我找不到这个选项。有人知道我该怎么做吗?
explorercanvas
,但这当然不是HTML5,它canvas
只是由VML模拟的元素。我从未见过其他浏览器可以做到这一点。
Answers:
以下有用的链接除了显示使用双缓冲的示例和优点之外,还显示了使用html5 canvas元素的其他一些性能提示。它包含指向jsPerf测试的链接,这些链接将跨浏览器的测试结果汇总到Browserscope数据库中。这样可以确保性能提示得到验证。
http://www.html5rocks.com/zh-CN/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);
一个非常简单的方法是在同一屏幕位置上放置两个画布元素,并为需要显示的缓冲区设置可见性。完成后,在隐藏的地方绘制并翻转。
一些代码:
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');
<noscript>
,并用Javascript创建画布元素。通常,无论如何,您通常都希望在Javascript中检查画布支持,那么为什么要在HTML中放入画布后备消息呢?
我测试过的所有浏览器都通过在绘制框架的代码完成之前不重新绘制画布来为您处理此缓冲。另请参阅WHATWG邮件列表:http : //www.mail-archive.com/whatwg@lists.whatwg.org/msg19969.html
约什(前一段时间)询问浏览器如何知道“绘制过程何时结束”,以避免闪烁。我会直接对他的帖子发表评论,但我的代表不够高。这也是我的看法。我没有事实要备份,但是我对此很有信心,这对将来阅读此书的人可能会有帮助。
我猜您完成绘制后浏览器不会“知道”。但是,就像大多数JavaScript一样,只要您的代码在不放弃对浏览器的控制的情况下运行,浏览器实际上就被锁定了,并且不会/无法更新/响应其UI。我猜想,如果您清除画布并绘制整个框架而没有放弃对浏览器的控制,则直到完成后它才真正绘制您的画布。
如果您设置了一个呈现跨多个setTimeout / setInterval / requestAnimationFrame调用的情况,您在一个调用中清除了画布,并在接下来的几个调用中绘制了画布上的元素,例如每5个调用重复一次循环,我们愿意打赌您会看到闪烁,因为在每次调用后都会更新画布。
也就是说,我不确定我是否会相信。我们已经到了在执行之前将javascript编译成本机代码的程度(至少从我的理解来看,这就是Chrome的V8引擎所做的事情)。如果不久之后浏览器就开始在与UI分开的线程中运行javascript并同步对UI元素的任何访问,从而允许UI在不访问UI的javascript执行期间进行更新/响应,那么我不会感到惊讶。当/如果发生这种情况(并且我知道有许多障碍需要克服,例如在您仍在运行其他代码时启动事件处理程序),我们可能会在画布动画上看到没有使用的闪烁某种双重缓冲。
就个人而言,我喜欢两个画布元素彼此重叠放置并交替显示在每个框架上的想法。相当简单,只需几行代码就可以很容易地将其添加到现有应用程序中。
对于不信的人,这是一些闪烁的代码。请注意,我明确清除了前一个圆。
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>
通过使用现有的库来创建干净且无闪烁的JavaScript动画,您可能会获得最好的成绩,而不是自己动手:
这是一个受欢迎的网站:http : //processingjs.org
您需要2个canvas :(请注意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;