ChatGPT解决这个技术问题 Extra ChatGPT

Does HTML5/Canvas Support Double Buffering?

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?

My experience has been that canvas drawing is coalesced by the browser so that animations are smooth. Can you share some code that flickers as you describe?
I've noticed IE can flicker in some cases when using explorercanvas, but that's of course not HTML5 and is a canvas element merely emulated by VML. I've never seen any other browsers do it though.
Really dumb beginner code that doesn't flicker. jsfiddle.net/linuxlizard/ksohjr4f/3 By all rights, should flicker. Browsers are impressive.
You only need double buffering if you have an async draw function. As long as you don't yield to the browser i.e. make drawing synchronous you'll be fine. As soon as you add a promise or setTimeout or something in there, you yield back to the browser and it will draw the current state before its finished effectively causing flicker.

q
quantumpotato

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:

canvas { border: 2px solid #000; position:absolute; top:0;left:0; 
visibility: hidden; }

Flipping in 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:

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

Off topic: I personally like to use <noscript>, and create my canvas elements in Javascript. You would typically want to check for canvas support in Javascript anyways, so why would you want to put a canvas fallback message in your HTML?
R
Roko C. Buljan

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.

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

E
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/whatwg@lists.whatwg.org/msg19969.html


Well I notice a flicker or screen tear. Not sure how to describe it. Using latest Chrome on Linux.
D
DeadlyBacon

You could always do var canvas2 = document.createElement("canvas"); and not append it to the DOM at all.

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.


o
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".

To effectively reduce flicker use requestAnimationFrame()!


How do you explain the performance improvements that have been witnessed by using double buffering? html5rocks.com/en/tutorials/canvas/performance
@ricksuggs Well, the "double buffering" or "offscreen-rendering" mentioned on html5rocks is a bit different than I thought about. I considered DB as swapping screen pages (in vram), which effectively was only a pointer operation instead of copying memory chunks. The OP asked to use DB to avoid flickering, which is really addressed by requestAnimationFrame(). Maybe this article about Offscreen-Rendering may be interesting. There I answer the OP's question about copying and pasting buffered image data to screen using a Sprite Buffer
c
colxi

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); Basketball Your browser does not support the canvas element.


Doesn't seem to flicker for me, at least not when the ball isn't moving by more than its radius each frame. Chrome/Windows.
I've added a call to requestAnimFrame - see here - to your example at jsfiddle.net/GzSWJ/28 - it doesn't flicker any more
L
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.

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.

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.

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.

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.


Exactly. See the JSFiddle here stackoverflow.com/questions/11777483/… for an example.
L
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!


Can you provide some evidence to back up your claims?
@ricksuggs I find not using DB results in bad jerkiness in animations, I havn't tried DB yet
a
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:

Here's a popular one: http://processingjs.org


Exactly! Why bother and write 10 lines of your own code, when you can just use a whole 275KB library without having the slightest clue about it!? Yes, I was being sarcastic.
A
Aviad Gispan

you need 2 canvas: (notice the css z-index and 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;

Nice implementation, in case that someone needs to apply manually the double buffering technique. Just add the following line: var ctx = new Array(2); to the empty line in your code above.
J
Josh

Opera 9.10 is very slow and shows the drawing process. If you want to see a browser not use double buffering, try Opera 9.10 out.

Some people suggested that browsers are somehow determining when the drawing process ends but can you explain how that can work? I haven't noticed any obvious flicker in Firefox, Chrome, or IE9 even when the drawing is slow so it seems like that is what they are doing but how that is accomplished is a mystery to me. How would the browser ever know that it is refreshing the display just before more drawing instructions are to be executed? Do you think they just time it so if an interval of more than 5ms or so goes by without executing a canvas drawing instruction, it assumes it can safely swap buffers?


C
Chien-Wei Huang

In most situations, you don't need to do this, the browser implements this for you. But not always useful!

You still have to implement this when your drawing is very complicated. Most of the screen update rate is about 60Hz, it means the screen updates per 16ms. The browser's update rate may near this number. If your shape need 100ms to be completed, you'll see a uncompleted shape. So you can implement double buffering in this situation.

I have made a test: Clear a rect, wait for some time, then fill with some color. If I set the time to 10ms, I won't see flickering. But if I set it to 20ms, the flickering happens.