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?
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');
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.
https://web.dev/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);
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
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.
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()!
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>
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.
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!
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
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;
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.
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?
This is an example of double buffering with image data.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML5 Canvas Demo of PixelBuffer</title>
<style>
html,
body {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<canvas id="buffer1"></canvas>
<script>
const canvas = document.getElementById('buffer1');
const ctx = canvas.getContext('2d');
const imageData = ctx.createImageData(100, 100);
const pixelBuffer = new Uint32Array(imageData.data.buffer);
for (let i = 0; i < pixelBuffer.length; i++) {
pixelBuffer[i] = 0xFF0000FF;
}
ctx.putImageData(imageData, 0, 0);
</script>
</body>
</html>
Related
I'm looking for a way to render graphics onto an HTML5 canvas using JavaScript, but I want to only render said graphics if they're inside a pre-defined mask.
I'm creating a GUI framework that can be used to easily and quickly create GUIs on an HTML5 canvas. I think that something that would be really nice to have is a way to render graphics inside an element, and make the element auto-crop the graphics so that they always stay inside of it. For example, I can make a rectangular element and animate a circular pulse inside of it, and as the circle extends past the outside of the element, those parts of he circle should just not render to keep it looking smooth and sharp. This is similar to what CSS does with overflow: hidden;
Now, I know that one option is to use a mask-like feature. For example, P5.js has mask(). However, this is very very slow. Masking a single element a single time using P5.js significantly reduces framerate, and I want to be doing this potentially hundreds of times per frame without frame drops. I know that CSS does this incredibly efficiently (from my own experience working with it), but I can't seem to think of any way to make it efficient on a canvas element.
I could do it pretty simply if it was just a rectangle, but I want to do this for any shape. For example, a circle, a star, a rectangle with rounded edges, or really any polygon at all.
How can this be done? I thought of potentially rendering to an off screen canvas (which is shrunken to the size of the element in question), then render the element onto that screen using one color (let's say the background color will be white, and the shape will be black), then rendering the image we want masked onto another off screen canvas that's the same width as our other OSC, then looping through one of their image data arrays and mapping one to the other based on whether said pixel is white or black on the mask canvas.
But........ I can't help but think that that's going to be incredibly slow for the computer to process. I assume that CSS somehow leverages the GPU to do this type of computation incredibly efficiently and that's why they get such an increase in performance. Is it possible for me to do the same or am I just dreaming?
Okay, so I have found two different means of doing this (huge thank you to #Kaiido). One method is to use ctx.clip() while one works with CanvasPattern.
This snippet shows both means in action:
<canvas id = "c" width = "400" height = "400"></canvas>
<canvas id = "c2" width = "400" height = "400"></canvas>
<script>
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "yellow";
ctx.fillRect(0,0,400,400);
ctx.beginPath();
ctx.arc(200,200,100,0,6);
ctx.clip();
ctx.beginPath();// This clears our previous arc from the path so that it doesn't render in when we `fill()`
ctx.fillStyle = "rgb(255,0,0)";
for(var i = 0;i < 20;i++){
for(var j = 0;j < 40;j++){
ctx.rect(i * 20 + j % 2 * 10,j * 10,10,10);
}
}
ctx.fill();
</script>
<script>
var canvas2 = document.getElementById("c2");
var ctx2 = canvas2.getContext("2d");
ctx2.fillStyle = "orange";
ctx2.fillRect(0,0,400,400);
var osc = new OffscreenCanvas(400,400);
var oscctx = osc.getContext("2d");
oscctx.fillStyle = "rgb(255,0,0)";
for(var i = 0;i < 20;i++){
for(var j = 0;j < 40;j++){
oscctx.rect(i * 20 + j % 2 * 10,j * 10,10,10);
}
}
oscctx.fill();
var pattern = ctx2.createPattern(osc,"no-repeat");
ctx2.fillStyle = pattern;
ctx2.arc(200,200,100,0,6);
ctx2.fill();
</script>
Which one is more efficient and better to be run hundreds of times per frame?
Another edit:
I spent about an hour messing around with it on a sandbox website, and I made this small project:
https://www.khanacademy.org/computer-programming/-/6446241383661568
There I run each one every millisecond and see how quickly each one updates to see which appears more efficient. clip() is on top while CanvasPattern is on the bottom. They both appear to be incredibly fast to me, and I feel that no matter which I chose I will have almost exactly the same results. However, clip() does still appear to be a bit faster as far as I can tell.
See for yourself and let me know what you think!
I am learning JavaScript, coming from eight years of Python experience, and as an exercise I made a Mandelbrot renderer that generates the fractal and uses putImageData to update the canvas from left to right one single-pixel column at a time.
I found that with this approach the visible image in the browser only updates when the full-screen calculation is complete (rather than seeing it appear gradually from left to right as I wanted). I understand that it is the expected behaviour, so I decided to add the "scan-line" animation by using requestAnimationFrame. (something like seen here: Christian Stigen Larsen renderer)
My expectation is that lighter calculations should render faster (as the next frame is available sooner) and higher-iteration calculations should render slower. What I found with my implementation is that the scan-line is consistently slow.
I have isolated this issue from anything to do with my Mandelbrot calculations, as this behaviour also happens for the minimal case below. I am running this on Chrome 83 and see the canvas populate very slowly at a constant rate (approx 30 pixels per second) from left to right.
Is my implementation of rAF incorrect or are my expectations wrong? The renderer I linked to uses setTimeout() to animate, but I read that it is a widely discouraged practice these days.
How should I implement a left-to-right scan update of my canvas at the highest available frame-rate (I am not worried about limiting it at the moment)?
EDIT: For clarity, the code below draws a single thin rectangle at every frame request by rAF, and takes exactly the same amount of time to paint the full canvas as a 100-iteration Mandelbrot render.
This suggest to me that the slowness of it is not due to amount of calculations taking place between frames.
const canvas = document.querySelector('.myCanvas');
const width = window.innerWidth;
const height = window.innerHeight;
const ctx = canvas.getContext('2d');
function anim(timestamp, i) {
if (i < width) {
ctx.fillRect(i, 0, 1, height);
window.requestAnimationFrame(function(timestamp) {
anim(timestamp, i + 1);
})
}
}
ctx.fillStyle = 'rgb(0,0,0)';
window.requestAnimationFrame(function(timestamp) {
anim(timestamp, 0);
});
<!DOCTYPE html>
<html lang="en">
<head>
<canvas class="myCanvas">
<p>Add suitable fallback here.</p>
</canvas>
<script src="slow%20rAF.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
<meta charset="UTF-8">
<title>Mandelbrot</title>
</head>
<body>
</body>
</html>
There is no 'faster' requestAnimationFrame.
Generally the answer to this is to separate calcuations from rendering. Ideally the calculations happen off of the main thread and rendering is handled on the main thread. Even without threading you should be able to find a way to do the calculations outside of the RAF, and just have the RAF render the pixels.
I want to measure the performance of browsers from my JavaScript when they render the HTML5 Canvas.
To begin, I drew a simple 2D canvas and logged the time right before and after it was done:
HTML
<canvas id="myCanvas" width="200" height="100"></canvas>
JS
var start = performance.now();
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.moveTo(0, 0);
ctx.lineTo(200, 100);
ctx.stroke();
var end =performance.now();
alert(end-start); //alert for testing only
This returned a very small value as predicted.
To mesure the performance I should render something more complex, So i did a code that renders a 3D image and animates it.
But now It's tricky to calculate time before and after since the animation is on a constant loop. I tried logging the time for the first animation before it continued to loop, but still the time difference is very small.
I want to see if I am in the right direction or if there are better practices. My main aim is to check differences in performance between as many browsers as possible, using javascript, and I thought this was better logged while doing a complex task such as renderign a 3D canvas.
Is there any way to force a canvas to update? I'd like to display a loading message but the game locks up loading before it displays. I feel like I need to tell the canvas to "draw now" before load function is called, but maybe there is a another way...
EDIT:
ok, just to be clear i've written a very small dirty script (best i can do in my tea break :P) to demonstrate what i'm trying to over come. Here:
<html>
<head>
<title>test page</title>
</head>
<body onload="init();">
<canvas id="cnv" width="300" height="300">
canvas not supported.<br/>
</canvas>
</body>
</html>
<script type="text/javascript">
var ctx;
function init()
{
var canvas = document.getElementById('cnv');
ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(255, 0, 0)";
ctx.fillRect(0,0,300,300);
FakeLoad();
ctx.fillStyle = "rgb(0, 255, 0)";
ctx.fillRect(0,0,300,300);
}
function FakeLoad()
{
var d = new Date();
var start = d.getTime();
while (new Date().getTime() - start < 5000)
{
//emulate 5 secs of loading time
}
}
</script>
now the idea is the script should draw a red square to show its "loading" and a green square when finished. but all you will see if you copy that into a html file is a pause of 5 seconds then the green appears, never red. In my head I wanted to have some command like ctx.Refresh(); after i draw the green square to tell it "update now! don't wait!" but judging from replies this is not the way to handle the problem.
what should I do? :)
Another thing you could do to allow the canvas to display before you do the longer task is begin FakeLoad after a short timeout:
var canvas = document.getElementById('cnv');
ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(255, 0, 0)";
ctx.fillRect(0,0,300,300);
setTimeout(function() {
FakeLoad();
ctx.fillStyle = "rgb(0, 255, 0)";
ctx.fillRect(0,0,300,300);
}, 20); // 20 ms - should be enough to draw something simple
If I understand you correctly, there's no way to update a canvas in the same way that you may be able to re-render/paint in WPF or re-flow the HTML DOM.
This is because a canvas uses intermediate mode graphics. Essentially, the only thing that the canvas retains knowledge of is the pixels which make up the canvas. When you call draw rect or draw line, the pixels that make up the rectangle or line are added to the canvas and as far as the canvas is concerned, it forgets all about the draw rect or draw circle call and therefore we cannot refresh the canvas and get the same result.
If your looking for something that 'retains' knowledge of the graphics rendered into it, you could try SVG, this uses retained mode graphics. Essentially it has it's own DOM (heirachy of objects (circle, rect etc)), which are re-rendered each time a region is made dirty.
In short, this page is very good at helping you understand the difference between immediate mode graphics and retained mode graphics: http://msdn.microsoft.com/en-us/library/ie/gg193983(v=vs.85).aspx
All that said, if I'm misunderstanding your question, please accept my apologies!
Update
From the extra info in the question,
The reason why you never see the green square is because the main javascript thread is always busy with your five second loop. It never has any time to update the UI. To allow time for the canvas to update with your loading screen, you'll need to do all your loading asynchronously.
What sort of loading will you be doing? If it's images etc, you can grab them using $.ajax and jQuery. This will asynchronously get the images from their location and once the last image has been retrieved, you can then clear and repaint the canvas with your content.
I guess something like this:
<script type="text/javascript">
var ctx;
function init()
{
var canvas = document.getElementById('cnv');
ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(255, 0, 0)";
ctx.fillRect(0,0,300,300);
FakeLoad();
}
function FakeLoad()
{
$.ajax({
url: "www.foo.com/bar.jpg",
success: function(data)
{
// Do something with the data (persist it)
RenderApp();
}
});
}
function RenderApp()
{
// Rendering the rest of the application / game
ctx.fillStyle = "rgb(0, 255, 0)";
ctx.fillRect(0,0,300,300);
}
</script>
Obviously this is kinda psudo-code, but hopefully it'll give you some idea!
let me know if you need any clarification!
I am about to implement Photoshop-like Layers in HTML5 Canvas. Currently I have two ideas. The first and maybe the simpler idea is to have a Canvas element for each layer like:
<canvas id="layerName" width="320" height="240" style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
<canvas id="layerName" width="320" height="240" style="position: absolute; left: 0; top: 0; z-index: 2;"></canvas>
<canvas id="layerName" width="320" height="240" style="position: absolute; left: 0; top: 0; z-index: 3;"></canvas>
This way when you draw to a layer -- it actually goes to that "layer". Layers with transparent positions can be seen through to below layers (Canvases). Layer stacking is controlled with z-index property.
The second idea is to use a single Canvas element and implement some logic to handle layers like in this case:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script>
window.addEventListener('load', function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var order = 0;
function drawLayer1() {
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
}
function drawLayer2() {
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, 55, 50);
}
function draw() {
ctx.clearRect(0, 0, 256, 256);
if (order === 0) {
drawLayer1();
drawLayer2();
}
else {
drawLayer2();
drawLayer1();
}
}
setInterval(draw, 250);
setInterval(function() {
order = 1 - order;
}, 200);
}, false);
</script>
</head>
<body>
<canvas id="canvas" width="256px" height="256px"></canvas>
</body>
</html>
In the above code the two layers will change stacking order every 200msec.
So, the question is that which way would be the best way? What are the pros and cons of both approaches?
If you want to use a single canvas element and have multiple layers inside it, you might want to look at my library:
https://github.com/ant512/CanvasLayers
It uses a damaged rect system to reduce the amount of repainting done every time the canvas changes, so not only do you get layers (which can be nested), but you also get optimised redraws.
Using multiple canvases should be faster, because the canvas gets drawn off-screen and then just blitted to the screen by the browser. You put the burden of switching layers on the browser, which just has to move some rectangles of graphics data around.
If you do the layering yourself, you have more control, but the burden is on the JS and the JS engine to do all the work. I would avoid this if I had a choice, but if you're going for layer effects that work on the underlying layers, this might be your only choice.
Setting the container div relative ought to have prevented that layer-overwrite issue. Try setting the position on the "occluded text" - e.g. if it's currently absolue it will obvious go in the same region as the top left of the relative stuff.
And it's probably obvious but, by the order of divs in the html you can eliminate the use of the z axis. If you want your stuff to be generic (and for other developers too), use the z axis but store a baseline to which you add your layer indices (so that baseline can be tweaked when using other code using z-axis in a problematic way).
Using jCanvas,see its layer API: http://projects.calebevans.me/jcanvas/docs/layerAPI/