Javascript HTML5/Canvas 是否支持双缓冲?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2795269/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Does HTML5/Canvas Support Double Buffering?
提问by foreyez
What I'd like to do is draw my graphics on a buffer and then be able to copy it as is to the canvas so I can do animation and avoid flickering. But I couldn't find this option. Anyone know how I can go about this?
我想要做的是在缓冲区上绘制我的图形,然后能够将其按原样复制到画布上,这样我就可以制作动画并避免闪烁。但是我找不到这个选项。有谁知道我该怎么做?
采纳答案by Rick Suggs
The following helpful link, in addition to showing examples and advantages of using double buffering, shows several other performance tips for using the html5 canvas element. It includes links to jsPerf tests, which aggregate test results across browsers into a Browserscope database. This ensures that the performance tips are verified.
除了显示使用双缓冲的示例和优势之外,以下有用链接还显示了使用 html5 canvas 元素的其他几个性能提示。它包括指向 jsPerf 测试的链接,这些测试将跨浏览器的测试结果聚合到 Browserscope 数据库中。这可确保验证性能提示。
http://www.html5rocks.com/en/tutorials/canvas/performance/
http://www.html5rocks.com/en/tutorials/canvas/performance/
For your convenience, I have included a minimal example of effective double buffering as described in the article.
为方便起见,我提供了一个有效双缓冲的最小示例,如本文所述。
// 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);
回答by Fedor van Eldijk
A very simple method is to have two canvas-elements at the same screen location and set visibility for the buffer that you need to show. Draw on the hidden and flip when you are done.
一个非常简单的方法是在同一屏幕位置放置两个画布元素,并为需要显示的缓冲区设置可见性。完成后在隐藏处绘制并翻转。
Some code:
一些代码:
CSS:
CSS:
canvas { border: 2px solid #000; position:absolute; top:0;left:0;
visibility: hidden; }
Flipping in JS:
在 JS 中翻转:
Buffers[1-DrawingBuffer].style.visibility='hidden';
Buffers[DrawingBuffer].style.visibility='visible';
DrawingBuffer=1-DrawingBuffer;
In this code the array 'Buffers[]' holds both canvas-objects. So when you want to start drawing you still need to get the context:
在此代码中,数组 'Buffers[]' 包含两个画布对象。所以当你想开始绘图时,你仍然需要获取上下文:
var context = Buffers[DrawingBuffer].getContext('2d');
回答by Edward Coffey
Browsers I've tested all handle this buffering for you by not repainting the canvas until the code that draws your frame has completed. See also the WHATWG mailing list: http://www.mail-archive.com/[email protected]/msg19969.html
我测试过的所有浏览器都通过在绘制框架的代码完成之前不重新绘制画布来为您处理这种缓冲。另请参阅 WHATWG 邮件列表:http: //www.mail-archive.com/[email protected]/msg19969.html
回答by DeadlyBacon
You could always do
var canvas2 = document.createElement("canvas");and not append it to the DOM at all.
你总是可以这样做
var canvas2 = document.createElement("canvas");而不是将它附加到 DOM 中。
Just saying since you guys seem so obsessed with display:none;it just seems cleaner to me and mimicks the idea of double buffering way more accurately than just having an awkwardly invisible canvas.
只是说,因为你们似乎display:none;对它如此着迷,对我来说似乎更干净,并且比仅仅拥有一个笨拙的隐形画布更准确地模仿双缓冲方式的想法。
回答by ohager
More than two years later:
两年多后:
There is no need for 'manually' implement double buffering. Mr. Geary wrote about this in his book "HTML5 Canvas".
不需要“手动”实现双缓冲。Geary 先生在他的书“HTML5 Canvas”中写到了这一点。
To effectively reduce flicker use requestAnimationFrame()!
有效减少闪烁使用requestAnimationFrame()!
回答by John
For the unbelievers, here's some flickering code. Note that I'm explicitly clearing to erase the previous circle.
对于不信者,这里有一些闪烁的代码。请注意,我明确清除以擦除前一个圆圈。
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>
回答by Lee
Josh asked (a while back) about how the browser knows "when the drawing process ends" so as to avoid flicker. I would have commented directly to his post but my rep isn't high enough. Also this is just my opinion. I don't have facts to back it up, but I feel fairly confident about it and it may be helpful to others reading this in the future.
Josh 询问(不久前)浏览器如何知道“绘制过程何时结束”以避免闪烁。我会直接评论他的帖子,但我的代表不够高。这也只是我的意见。我没有事实来支持它,但我对此相当有信心,这可能对将来阅读本文的其他人有所帮助。
I'm guessing the browser doesn't "know" when you're done drawing. But just like most javascript, as long as your code runs without relinquishing control to the browser, the browser is essentially locked up and won't/can't update/respond to its UI. I'm guessing that if you clear the canvas and draw your entire frame without relinquishing control to the browser, it won't actually draw your canvas until you're done.
我猜你画完后浏览器不会“知道”。但就像大多数 javascript 一样,只要您的代码在不放弃对浏览器的控制的情况下运行,浏览器本质上就会被锁定并且不会/无法更新/响应其 UI。我猜如果您清除画布并绘制整个框架而不放弃对浏览器的控制,那么在您完成之前它实际上不会绘制您的画布。
If you set up a situation where your rendering spans multiple setTimeout/setInterval/requestAnimationFrame calls, where you clear the canvas in one call and draw elements on your canvas in the next several calls, repeating the cycle (for example) every 5 calls, I'd be willing to bet you'd see flicker since the canvas would be updated after each call.
如果您设置了一种情况,其中您的渲染跨越多个 setTimeout/setInterval/requestAnimationFrame 调用,您在一次调用中清除画布并在接下来的几次调用中在画布上绘制元素,重复循环(例如)每 5 次调用,我愿意打赌你会看到闪烁,因为每次调用后画布都会更新。
That said, I'm not sure I'd trust that. We're already at the point that javascript is compiled down to native machine code before execution (at least that's what Chrome's V8 engine does from what I understand). I wouldn't be surprised if it wasn't too long before browsers started running their javascript in a separate thread from the UI and synchronizing any access to UI elements allowing the UI to update/respond during javascript execution that wasn't accessing UI. When/if that happens (and I understand there are many hurdles that would have to be overcome, such as event handlers kicking off while you're still running other code), we'll probably see flicker on canvas animation that aren't using some kind of double-buffering.
也就是说,我不确定我是否会相信。我们已经到了在执行之前将 javascript 编译成本机机器代码的阶段(至少根据我的理解,这是 Chrome 的 V8 引擎所做的)。如果不久之后浏览器开始在与 UI 不同的线程中运行它们的 javascript 并同步对 UI 元素的任何访问,允许 UI 在未访问 UI 的 javascript 执行期间更新/响应,我不会感到惊讶。当/如果发生这种情况(我知道有许多障碍需要克服,例如在您仍在运行其他代码时启动事件处理程序),我们可能会在未使用的画布动画上看到闪烁某种双缓冲。
Personally, I love the idea of two canvas elements positioned over top of each other and alternating which is shown/drawn on each frame. Fairly unintrusive and probably pretty easily added to an existing application with a few lines of code.
就个人而言,我喜欢将两个画布元素置于彼此之上并交替显示/绘制在每一帧上的想法。相当不干扰,可能很容易通过几行代码添加到现有应用程序中。
回答by Luka
There is no flickering in web browsers! They already use dbl buffering for their rendering. Js engine will make all your rendering before showing it. Also, context save and restore only stack transformational matrix data and such, not the canvas content itself. So, you do not need or want dbl buffering!
Web 浏览器中没有闪烁!他们已经使用 dbl 缓冲进行渲染。Js 引擎将在显示之前进行所有渲染。此外,上下文保存和恢复仅堆栈转换矩阵数据等,而不是画布内容本身。因此,您不需要或不想要 dbl 缓冲!
回答by Aviad Gispan
you need 2 canvas: (notice the css z-index and position:absolute)
你需要 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>
you can notice that the first canvas is visible and the second it's hidden the idea it's to draw on the hidden after that we will hide the visible and make the hidden canvas visible. when it's hidden 'clear hidden 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;
回答by a7drew
Rather than rolling your own, you're probably going to get the best mileage by using an existing library for creating clean and flicker-free JavaScript animation:
与其自己动手,不如使用现有的库来创建干净且无闪烁的 JavaScript 动画,从而获得最佳效果:
Here's a popular one: http://processingjs.org
这是一个流行的:http: //processingjs.org

