Noticed something I didn't expect when playing with animation in Canvas. I have a fairly easy animation of five images in different sizes moving from bottom to top of screen in a loop.
I have all my image data in an array and draw them onto the canvas via a loop, then use window.requestAnimationFrame to do a new draw on next frame.
Here is the interesting part, at first I just cleared the canvas for each frame rendered using context.clearRect(0, 0, canvas.width, canvas.height);. Then I though that this must be a waste of computing, to clear the entire canvas for each render even thou only parts of the screen actually changed.
So I rewrote the part clearing the canvas to only clear the trace of the old image draw, using something like this:
for (var key in _images) {
context.clearRect(_images[key].x-1, _images[key].y+_images[key].height, _images[key].width+2, 5);
}
But too my surprise this seems to be slower... First I had a frame rate at 49-60, and after 47-57. Any idea to why? And is there any other way to optimize this?
0) i would rather write :
for (var i=0, len=_images.length; i<len; i++) {
var thisImage = _images[i];
context.clearRect(thisImage.x, thisImage.y, thisImage.width, thisImage.height);
}
1) when requestAnimationFrame triggers, draw before updating, so you are right in sync with the screen.
2) round the coordinates to the nearest 64 bits, i.e. use &(~3) on each x coordinates can speed things up.
3) You might group some clearRect together (= all the bullets / a tiled horizontal floor, ...)
4) i think the main question here is the overhead of those methods.
I did a jsperf to know more :
http://jsperf.com/overhead-of-fillrect-and-clearrect/2
!! be sure to have the canvas in sight during the tests !!
Results :
Firefox, clearRect has very small overhead, and time seems
almost proportionnal with pixel count.
fillRect is like 60% slower and seems quite proportionnal also
with area covered.
64 bit Unalignment seems of no effet on clearRect, it makes performances
fall badly on fillRect.
some times from jsPerf (precision is so-so) :
208ns for fullscreen clearRect
55ns for halfscreen clearRect -> 4 times makes 220ns vs 208
13ns for quarter screen clearRect -> 16 times makes 208 (!!!)
588ns fullscreen fillRect
1290ns halfscreen fillRect --> 4 times makes 1160
41ns quarterscreen fillrect --> 16 times makes 656.
Safari, everything the results are quite the same, except there is
more overhead : it is only 3 times faster to draw 4 times less points.
I know the the precision is so-so but it seems that on firefox
and Safari, the conclusion is :
the less part of your screen you clear, the faster you erase.
And prefer clearRect to fillRect -
Chrome : (date 05 / 14) fillRect is much faster than clearRect.
IE11 : fillRect and clearRect are on par.
((If anyone can see on other Browser, i'll edit -when i have some time-))
( provided your images are 4 pixels aligned, the code with
64 align is :
for (var i=0, len=_images.length; i<len; i++) {
var thisImage = _images[i];
var x = thisImage.x ;
context.clearRect(x & (~3), thisImage.y, thisImage.width + ((x&3)&&(16-(x&3))), thisImage.height);
}
)
Since people answered in comments and I do feel they covered my questions. Here's a summery:
Each clearRect() takes up roughly the same amount of time, so it is better to have few than many.
To optimize one should calculate the area that needs to be cleared, if not 100%, and clear that.
Related
I have gone through the 'point-along-path' d3 visualization through the code: https://bl.ocks.org/mbostock/1705868. I have noticed that while the point is moving along its path it consumes 7 to 11% of CPU Usage.
Current scenario, I have around 100 paths and on each path, I will have to move points(circles) from sources to destinations. So it consumes more than 90% of the CPU memory as more number of points are moving at the same time.
I have tried as:
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";
};
};
}
// On each path(100 paths), we are moving circles from source to destination.
var movingCircle = mapNode.append('circle')
.attr('r', 2)
.attr('fill', 'white')
movingCircle.transition()
.duration(10000)
.ease("linear")
.attrTween("transform", translateAlong(path.node()))
.each("end", function() {
this.remove();
});
So what should be the better way to reduce the CPU usage?
Thanks.
There are a few approaches to this, which vary greatly in potential efficacy.
Ultimately, you are conducting expensive operations every animation frame to calculate each point's new location and to re render it. So, every effort should be made to reduce the cost of those operations.
If frame rate is dropping below 60, it probably means we're nearing CPU capacity. I've used frame rate below to help indicate CPU capacity as it is more easily measured than CPU usage (and probably less invasive).
I had all sorts of charts and theory for this approach, but once typed it seemed like it should be intuitive and I didn't want to dwell on it.
Essentially the goal is to maximize how many transitions I can show at 60 frames per second - this way I can scale back the number of transitions and gain CPU capacity.
Ok, let's get some transitions running with more than 100 nodes along more than 100 paths at 60 frames per second.
D3v4
First, d3v4 likely offers some benefits here. v4 synchronized transitions, which appears to have had the effect of slightly improved times. d3.transition is very effective and low cost in any event, so this isn't the most useful - but upgrading isn't a bad idea.
There are also minor browser specific gains to be had by using different shaped nodes, positioning by transform or by cx,cy etc. I didn't implement any of those because the gains are relatively trivial.
Canvas
Second, SVG just can't move fast enough. Manipulating the DOM takes time, additional elements slows down operations and takes up more memory. I realize canvas can be less convenient from a coding perspective but canvas is faster than SVG for this sort of task. Use detached circle elements to represent each node (the same as with the paths), and transition these.
Save more time by drawing two canvases: one to draw once and to hold the paths (if needed) and another to be redrawn each frame showing the points. Save further time by setting the datum of each circle to the length of the path it is on: no need to call path.getTotalLength() each time.
Maybe something like this
Canvas Simplified Lines
Third, we still have a detached node that has SVG paths so we can use path.getPointAtLength() - and this is actually pretty effective. A major point slowing this down though is the use of curved lines. If you can do it, draw straight lines (multiple segments are fine) - the difference is substantial.
As a further bonus, use context.fillRect() instead of context.arc()
Pure JS and Canvas
Lastly, D3 and the detached nodes for each path (so we can use path.getTotalLength()) can start to get in the way. If need be leave them behind using typed arrays, context.imageData, and your own formula for positioning nodes on paths. Here's a quick bare bones example (100 000 nodes, 500 000 nodes, 1 000 000 nodes (Chrome is best for this, possible browser limitations. Since the paths now essentially color the entire canvas a solid color I don't show them but the nodes follow them still). These can transition 700 000 nodes at 10 frames per second on my slow system. Compare those 7 million transition positioning calculations and renderings/second against about 7 thousand transition positioning calculations and renderings/second I got with d3v3 and SVG (three orders of magnitude difference):
canvas A is with curved lines (cardinal) and circle markers (link above), canvas B is with straight (multi-segment) lines and square markers.
As you might imagine, a machine and script that can render 1000 transitioning nodes at 60 frames per second will have a fair bit of extra capacity if only rendering 100 nodes.
If the transition position and rendering calculations are the primary activity and CPU usage is at 100%, then half the nodes should free up roughly half the CPU capacity. In the slowest canvas example above, my machine logged 200 nodes transitioning along cardinal curves at 60 frames per second (it then started to drop off, indicating that CPU capacity was limiting frame rate and consequently usage should be near 100%), with 100 nodes we have a pleasant ~50% CPU usage:
Horizontal centerline is 50% CPU usage, transition repeated 6 times
But the key savings are to be found from dropping complex cardinal curves - if possible use straight lines. The other key savings are from customizing your scripts to be purpose built.
Compare the above with straight lines (multi segment still) and square nodes:
Again, horizontal centerline is 50% CPU usage, transition repeated 6 times
The above is 1000 transitioning nodes on 1000 3 segment paths - more than an order of magnitude better than with curved lines and circular markers.
Other Options
These can be combined with methods above.
Don't animate every point each tick
If you can't position all nodes each transition tick before the next animation frame you'll be using close to all of your CPU capacity. One option is don't position each node each tick - you don't have to. This is a complex solution - but position one third of circles each tick - each circle still can be positioned 20 frames per second (pretty smooth), but the amount of calculations per frame are 1/3 of what they would be otherwise. For canvas you still have to render each node - but you could skip calculating the position for two thirds of the nodes. For SVG this is a bit easier as you could modify d3-transition to include an every() method that sets how many ticks pass before transition values are re-calculated (so that one third are transitioned each tick).
Caching
Depending on circumstance, caching is also not a bad idea - but the front-ending of all calculations (or loading of data) may lead to unnecessary delays in the commencement of animation - or slowness on first run. This approach did lead to positive outcomes for me, but is discussed in another answer so I won't go into it here.
Post edit:
Here is the default. (peak CPU around %99 for 100 points at 2.7Ghz i7)
Here is my version. (peak CPU around 20% for 100 points at 2.7Ghz i7)
On average I am 5 times faster.
I presume the bottleneck here is the call to getPointAtLength method at every 17ms. I would also avoid lengthy string concatenations if I have to, but in your case its not much long so I think the best way:
cache points beforehand and only calculate once with a given resolution (I divided here in 1000 parts)
reduce calls to DOM methods within the requestAnimationFrame (that function you see receiving the normalized t parameter)
In the default case there is 2 calls, 1 when you call getPointAtLength, and then another one when you are setting the translate(under the hood).
You can replace the translateAlong with this below:
function translateAlong(path){
var points = path.__points || collectPoints(path);
return function (d,i){
var transformObj = this.transform.baseVal[0];
return function(t){
var index = t * 1000 | 0,
point = points[index];
transformObj.setTranslate(point[0],point[1]);
}
}
}
function collectPoints(path) {
var l = path.getTotalLength(),
step = l*1e-3,
points = [];
for(var i = 0,p;i<=1000;++i){
p = path.getPointAtLength(i*step);
points.push([p.x,p.y]);
}
return path.__points = points;
}
And a small modification to that tweening line:
.tween("transform", translateAlong(path.node()))
setting attr is not necessary, calling it is enough. Here is the result:
http://jsfiddle.net/ibowankenobi/8kx04y29/
Tell me if it did improve, because I'm not 100% sure.
Another way of achieving this might be to use an svg:animateMotion which you can use to move an element along a given path. Here is an example from the docs. In essence you want:
<svg>
<path id="path1" d="...">
<circle cx="" cy="" r="10" fill="red">
<animateMotion dur="10s" repeatCount="0">
<mpath xlink:href="#path1" />
</animateMotion>
</circle>
</path>
</svg>
I've not profiled it, but I think you're struggle to get much better performance than using something built into SVG itself.
Browser Support
Note that after a comment from #ibrahimtanyalcin I started checking browser compatibility. Turns out that this isn't supported in IE or Microsoft Edge.
On my computer:
#mbostock uses 8% CPU.
#ibrahimtanyalcin uses 11% CPU.
#Ian uses 10% CPU.
For #mbostock and #ibrahimtanyalcin this means the transition and the transform update use the CPU.
If I put 100 of these animations in 1 SVG I get
#mbostock uses 50% CPU. (1 core full)
#Ian uses 40% CPU.
All animations look smooth.
One possibility is to add a sleep in the transform update function using https://stackoverflow.com/a/39914235/9938317
Edit
In the nice Andrew Reid answer i have found a few optimizations.
I have written a version of the Canvas+JS 100 000 test that only does the calculation part and counts how many iterations can be do in 4000ms. Half the time order=false as t controls it.
I have written my own random generator to be sure that I get the same random numbers each time I run the modified code.
The blocks version of the code: 200 iterations
According to the docs for parseInt
parseInt should not be used as a substitute for Math.floor()
Converting a number to a string and then parsing the string up to the . sounds not very efficient.
Replacing parseInt() with Math.floor(): 218 iterations
I also found a line that has no function and looks not important
let p = new Int16Array(2);
It is inside the inner while loop.
Replacing this line with
let p;
gives 300 iterations.
Using these modifications the code can handle more points with a frame rate of 60 Hz.
I tried a few other things but they turn out to be slower.
I was surprised that if I pre-calculate the lengths of the segments and simplify the calculation of segment to a mere array lookup it is slower than doing the 4 array lookups and a couple of Math.pow and additions and a Math.sqrt.
I'm seeing a really odd canvas clearRect performance issue.
In our game, the current move is rendered with a small white square 7px * 7px.
Once that move is over the square is removed with a clearRect(x, y, 7, 7) function.
This performs fine, but leaves a weird white outline behind. Which I believe is sub pixel rounding:
I can solve this, by issuing a different clearRect call, specifically clearRect(x, y - 1, 8, 8) this removes the white residue which kind of confirms the sub-pixel theory, but for some reason the performance is significantly worse.
This is the initial 7*7 clear: https://www.useloom.com/share/9b6bbd00647a4803ad5d0a8a4ce77d3a
And this is the 8x8 clear:
https://www.useloom.com/share/c36f7b2e81e74d1d9bfd9e16124a7503
Should I really be seeing this jank with such a small change?
To put things into context, each frame I have a series of messages to render (the coloured squares / crosses) these could number 1-2000. I also need to render the current message in a different colour (which is on a different canvas).
So I have a function renderMessages responsible for rendering each message each frame.
renderMessages() {
this.messages.forEach((data) => {
this.renderCurrentTask(data)
this.renderCell(data)
})
this.messages = []
window.requestAnimationFrame(() => this.renderMessages())
}
The function where I'm drawing and then clearing the canvas is renderCurrentTask. As a result when I try to use something simple like a clearRect(0,0,w,h) I'm seeing poor perf as I have to call it maybe 2000 per frame.
If it's any help in diagnosing this is my renderCurrentTask function where the small clearRect change has the negative effect:
renderCurrentTask(data) {
if(this.lastCoordinates) {
this.interactiveContext.clearRect(this.lastCoordinates.x,
this.lastCoordinates.y,
this.lastCoordinates.wh,
this.lastCoordinates.wh)
}
const coordinate = this.addressToScreenCoordinate(data.address)
this.interactiveContext.fillStyle = '#ffffff'
this.interactiveContext.fillRect(
coordinate.x,
coordinate.y,
this.cellSize,
this.cellSize)
this.lastCoordinates = {
x: coordinate.x,
y: coordinate. y - 1,
wh: this.cellSize + 2
}
}
If your canvas game has performance issue even after trying different methods to optimize it, you could try using webgl renderer. I suggest using a library for rendering 2d using webgl, for example Pixi.js. This solution could require a lot of rewriting your current code tho.
I've got an issue with an experiment I'm working on.
My plan is to have a beautiful and shining stars Background on a whole page.
Using that wondeful tutorial (http://timothypoon.com/blog/2011/01/19/html5-canvas-particle-animation/) I managed to get the perfect background.
I use a static canvas to display static stars and an animated canvas for the shining ones.
The fact is it's very memory hungry! On chrome and opera it runs quite smoothly, but on firefox IE or tablet, it was a total mess 1s to render each frame etc... It is worse on pages where HEIGHT is huge.
So i went into some optimisations:
-Using a buffer canvas, the problem was createRadialGradient which was called 1500 times each frame
-Using a big buffer canvas, and 1 canvas for each stars with an only call to createRadialGradient at init.
-Remove that buffer canvas and drawing every stars canvas to the main one
That last optimisation was the best i could achieve so i wrote a fiddle displaying how is the code right now.
//Buffering the star image
this.scanvas = document.createElement('canvas');
this.scanvas.width=2*this.r;
this.scanvas.height=2*this.r;
this.scon=this.scanvas.getContext('2d');
g = this.scon.createRadialGradient(this.r,this.r,0,this.r,this.r,this.r);
g.addColorStop(0.0, 'rgba(255,255,255,0.9)');
g.addColorStop(this.stop, 'rgba('+this.color.r+','+this.color.g+','+this.color.b+','+this.stop+')');
g.addColorStop(1.0, 'rgba('+this.color.r+','+this.color.g+','+this.color.b+',0)');
this.scon.fillStyle = g;
this.scon.fillRect(0,0,2*this.r,2*this.r);
That's the point where I need you:
-A way to adjust the number of shining stars according to the user perfomance
-Optimisation tips
Thanks in advance to everyone minding to help me and I apologize if I made grammar mistakes, my english isn't perfect.
EDIT
Thanks for your feedbacks,
Let me explains the whole process,
Every stars has it's own different gradient and size, that's why I stored it into a personal canvas, the shining effect is only done by scaling that canvas on the main one with drawImage.
I think the best would be to prerender 50 or 100 different stars in a buffer canvas then picking and drawing a random one, don't you think?
EDIT2
Updated fiddle according to Warlock great advises, one prerendered star, scaled to match the current size. The stars are less pretty, but the whole thing runs a lot smoother.
EDIT3
Updated fiddle to use a sprite sheet. Gorgeous!!!!
//generate the star strip
var len=(ttlm/rint)|0;
scanvas = document.createElement('canvas');
scanvas.width=len*2*r;
scanvas.height=2*r;
scon=scanvas.getContext('2d');
for(var i=0;i<len;i++){
var newo = (i/len);
var cr = r*newo;
g = scon.createRadialGradient(2*r*i+r,r,0,2*r*i+r,r,(cr <= 2 ? 2 : cr));
g.addColorStop(0.0, 'rgba(200,220,255,'+newo+')');
g.addColorStop(0.2, 'rgba(200,220,255,'+(newo*.7)+')');
g.addColorStop(0.4, 'rgba(150,170,205,'+(newo*.2)+')');
g.addColorStop(0.7, 'rgba(150,170,205,0)');
scon.fillStyle = g;
scon.fillRect(2*r*i,0,2*r,2*r);
}
EDIT 4(Final)
Dynamic stars creations
function draw() {
frameTime.push(Date.now());
con.clearRect(0,0,WIDTH,HEIGHT);
for(var i = 0, len = pxs.length; i < len; i++) {
pxs[i].fade();
pxs[i].draw();
}
requestAnimationFrame(draw);
if(allowMore === true && frameTime.length == monitoredFrame)
{
if(getAvgTime()<threshold && pxs.length<totalStars )
{
addStars();
}
else
{
allowMore=false;
static=true;
fillpxs(totalStars-pxs.length,pxss);
drawstatic();
static=false;
}
}
}
Here is the updated and final fiddle, with spritesheet, dynamic stars creation and several optimisations. If you see anything else i should update don't hesitate.
POST EDIT Reenabled shooting stars/Prototyped object/got rid of Jquery
http://jsfiddle.net/macintox/K8YTu/32/
Thanks everyone who helped me, that was really kind and instructive, and I hope it will help somebody sometimes.
Aesdotjs.
PS: I'm so happy. After testing, that script run smoothly on every browser even IE9. Yatta!!
Adopting to browser performance
To measure capability of the user's setup you can implement a dynamic star creator which stops at a certain threshold.
For example, in your code you define a minimum number of stars to draw. Then in your main loop you measure the time and if the time spent drawing the stars are less than your max threshold you add 10 more stars (I'm just throwing out a number here).
Not many are aware of that requestAnimationFrame gives an argument (DOMHighResTimeStamp) to the function it calls with time in milliseconds spent since last request. This will help you keep track of load and as we know that 60 fps is about 16.7 ms per frame we can set a threshold a little under this to be optimal and still allow some overhead for other browser stuff.
A code could look like this:
var minCount = 100, /// minimum number of stars
batchCount = 10, /// stars to add each frame
threshold= 14, /// milliseconds for each frame used
allowMore = true; /// keep adding
/// generate initial stars
generateStarts(minCount);
/// timeUsed contains the time in ms since last requestAnimationFrame was called
function loop(timeUsed) {
if (allowMore === true && timeUsed < threshold) {
addMoreStars(batchNumber);
} else {
allowMore = false;
}
/// render stars
requestAnimationFrame(loop);
}
Just note that this is a bit simplified. You will need to run a few rounds first and measure the average to have this work better as you can and will get peak when you add stars (and due to other browser operations).
So add stars, measure a few rounds, if average is below threshold add stars and repeat.
Optimizations
Sprite-sheets
As to optimizations sprite-sheets are the way to go. And they don't have to just be the stars (I'll try to explain below).
The gradient and arc is the costly part of this applications. Even when pre-rendering a single star there is cost in resizing so many stars due to interpolation etc.
When there becomes a lot of costly operations it is better to do a compromise with memory usage and pre-render everything you can.
For example: render the various sizes by first rendering a big star using gradient and arc.
Use that star to draw the other sizes as a strip of stars with the same cell size.
Now, draw only half of the number of stars using the sprite-sheet and draw clipped parts of the sprite-sheet (and not re-sized). Then rotate the canvas 90 degrees and draw the canvas itself on top of itself in a different position (the canvas becoming a big "sprite-sheet" in itself).
Rotating 90 degrees is not so performance hungry as other degrees (0, 90, 180, 270 are optimized). This will give you the illusion of having the actual amount of stars and since it's rotated we are not able to detect a repetitive pattern that easy.
A single drawImage operation of canvas is faster than many small draw operations of all the stars.
(and of course, you can do this many times instead of just once up to a point right before where you start see patterns - there is no key answer to how many, what size etc. so to find the right balance is always an experiment).
Integer numbers
Other optimizations can be using only integer positions and sizes. When you use float numbers sub-pixeling is activated which is costly as the browser need to calculate anti-alias for the offset pixels.
Using integer values can help as sub-pixeling isn't needed (but this doesn't mean the image won't be interpolated if not 1:1 dimension).
Memory bounds
You can also help the underlying low-lowel bitmap handling a tiny bit by using sizes and positions dividable on 4. This has to do with memory copy and low-level clipping. You can always make several sprite-sheet to variate positions within a cell that is dividable on 4.
This trick is more valuable on slower computers (ie. typical consumer spec'ed computers).
Turn off anti-aliasing
Turn off anti-aliasing for images. This will help performance but will give a little more rough result of the stars. To turn off image anti-aliasing do this:
ctx.webkitEnableImageSmoothing = false;
ctx.mozEnableImageSmoothing = false;
ctx.enableImageSmoothing = false;
You will by doing this see a noticeable improvement in performance as long as you use drawImage to render the stars.
Cache everything
Cache everything you can cache, being the star image as well as variables.
When you do this stars.length the browser's parser need to first find stars and then traverse that tree to find length - for each round (this may be optimized in some browsers).
If you first cache this to a variable var len = stars.length the browser only need to traverse the tree and branch once and in the loop it will only need to look up the local scope to find variable len which is faster.
Resolution reduction
You can also reduce resolution in half, ie. do everything at half the target size. In the final step draw your render enlarged to full size. This will save you initially 75% render area but give you a bit low-res look as a result.
From the professional video world we often use low-resolution when things are animated (primarily moving) as the eye/brain patch up (or can't detect) so much details when objects are moving and therefor isn't so noticeable. If this can help here must be tested - perhaps not since the stars aren't actually moving, but worth a try for the second benefit: increased performance.
How about just creating a spritesheet of a star in its various stages of radial glow.
You could even use canvas to initially create the spritesheet.
Then use context.drawImage(spritesheet,spriteX,spriteY,starWidth,starHeight) to display the star.
Spritesheet images can be drawn to the screen very quickly with very little overhead.
You might further optimize by breaking the spritesheet into individual star images.
Good luck on your project :)
1. Minimize operations, related to the DOM;
In the LINE 93 you are creating canvas:
this.scanvas = document.createElement('canvas');
You need only one canvas instead of this. Move canvas creation to the initialization step.
2. Use integer coordinates for canvas;
3. Use Object Pool design pattern to improve performance.
4. In for loops cache the length variable:
for(var i = 0; i < pxs.length; i++) {...
}
Better:
for(var i = 0, len = pxs.length; i < len; i++) {
...
}
Note: don't mix jquery with native js.
I have this piece of canvas animation that is exhibiting some weird characteristics:
http://jsbin.com/olasol/2/edit
I'm on the latest version of Chrome. I'm using Chrome's inbuilt FPS monitor (you can activate it by going to about:flags).
I have marked the line in the JavaScript section which I think is the potential culprit:
fallingctx.clear();
This line does nothing special. It calls a function which in-turn calls clearRect().
The "weird" things I notice are:
The clear(); function causes very noticeable FPS drop on my laptop (Core 2 Duo), but not on my desktop (i5 2500k).
Removing that line alone is sufficient to produce 60fps on my laptop as well. As expected, the canvas doesn't clear after each frame, but still, it produces stable 60fps.
The FPS drop happens only when my Chrome window is on the larger side! When I shrink the window and reload, it doesn't happen! (Is it more expensive to clear a larger rectangle?).
I tried replacing the clear() with a drawImage() of a full white JPEG to cover the canvas. The JavaScript is able to do 200 drawImage() executions each cycle for the smaller image particles (evident from the second point). However, when I add one single drawImage for the overall canvas, it lags again! (Make sure the output occupies the entire screen in order to reproduce the result.)
Why is all this happening? How do I fix it?
It really depends on the hardware, but think about what the invocation of clearRect has to do! Something essentially must zero-out a piece of memory large enough to handle the canvas contents. That can be costly. Think about how much memory has to hold RGBA at HD resolutions... That's over two million pixels of data, around 8 MB in bytes Admittedly, it's not all that much these days in general, but if there's any bandwidth or caching issues related to pushing memory to the video card or something you are doing 60 times a second... well, expect problems.
What I've heard often works is just to clear around where the image is formerly drawn. See http://jsbin.com/olasol/6/edit
I made the following changes for you.
for (var i=0; i< noOfDrops; ++i)
{
fallingctx.clearRect(
fallingDrops[i].x-1,
fallingDrops[i].y-1,
fallingDrops[i].image.width+2,
fallingDrops[i].image.height+2);
}
for (var i=0; i< noOfDrops; ++i)
{
fallingDrops[i].y += fallingDrops[i].speed; //Set the falling speed
fallingctx.drawImage (fallingDrops[i].image, fallingDrops[i].x, fallingDrops[i].y);
}
There's probably a good reason that I need to clearRect around where the image was rendered but a simple reason escapes me. (It is something to do with things being rendered not quite at the pixel specified... I forget exactly).
You also need to do something about the fact you are starting the render loop before the image is loaded (also in the jsbin) so I added
var imgSource = "http://lorempixel.com/20/20/sports/";
var imgObj = new Image();
and replaced superinit
function superinit()
{
imgObj.onload = function(){
flowerfallsetup();
requestAnimFrame(flowerfall);
}
imgObj.onerror = function (){
alert("could not load image");
}
imgObj.src = imgSource;
}
Edit: I forgot to mention because of the prior image setup, I did change the line in your flowerfallsetup :
fallingDr["image"] = imgObj;
There are many ways to handle the asynchronous loading of images, I just chose one that was easy for this example.
Edit: I have to confess, there might be a bit more to this. It works fine on desktop browsers, but on the iPhone, there are clipping issues. If I can figure out what's causing the problem I'll try to post an update.
I'm using the Canvas object with javascript. Just doing some tests to see how fast I can set pixels in a draw loop.
On mac, it works great in FF, safari, chrome. On windows, I get a flickering effect on FF and chrome. It looks like somehow the canvas implementation on windows is different than on mac for the different browsers? (not sure if that's true).
This is the basic code I'm using to do the drawing (taken from the article below - I've optimized the below to tighten the draw loop, it runs pretty smooth now):
var canvas = document.getElementById('myCanvasElt');
var ctx = canvas.getContext('2d');
var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (var x = 0; x < canvasData.width; x++) {
for (var y = 0; y < canvasData.height; y++) {
// Index of the pixel in the array
var idx = (x + y * canvas.width) * 4;
canvasData.data[idx + 0] = 0;
canvasData.data[idx + 1] = 255;
canvasData.data[idx + 2] = 0;
canvasData.data[idx + 3] = 255;
}
}
ctx.putImageData(canvasData, 0, 0);
again, browers on windows will flicker a bit. It looks like the canvas implementation is trying to clear the canvas to white before the next drawing operation takes place (this does not happen on mac). I'm wondering if there is a setting I can change in the Canvas object to modify that value (double-buffering, clear before draw, etc)?
This is the article I am using as reference:
http://hacks.mozilla.org/2009/06/pushing-pixels-with-canvas/
Thanks
I think it's fairly clear that browsers who implement the Canvas object use DIBS (device independent bitmaps). The fact that you have access to the pixelbuffer without having to lock the handle first is proof of this. And Direct2D has nothing to do with JS in a browser thats for sure. GDI is different since it uses DDBs (device dependent bitmaps, i.e allocated from video memory rather than conventional ram). All of this however has nothing to do with optimal JS rendering speed. I think writing the RGBA values as you do is probably the best way.
The crucial factor in the code above is the call to putImageData(). This is where browsers can differ in their implementation. Are you in fact writing directly to the DIB, and putImageData is simply a wrapper around InvalidateRect? Or are you in fact writing to a duplicate in memory, which in turn is copied into the canvas device context? If you use linux or mac then this is still a valid question. Although device contexts etc. are typically "windows" terms, most OS'es deal with handles or structures in pretty much the same way. But once again, we are at the mercy of the browser vendor.
I think the following can be said:
If you are drawing many pixels in one go, then writing directly to the pixelbuffer as you do is probably the best. It is faster to "bitblt" (copy) the pixelbuffer in one go after X number of operations. The reason for this is that the native graphics functions like FillRect also calls "invalidate rectangle" which tells the system that a portion if the screen needs a re-draw (refresh). So if you call 100 line commands, then 100 update's will be issued - slowing down the process. Unless (and this is the catch) you use the beginPath/EndPath methods as they should be used. Then it's a whole different ballgame.
It's here that the Begin/End path "system" comes into play, and also the Stroke/Outline commands. They allow you to execute X number of drawing operations within a single update. But a lot of people get this wrong and issue a redraw for each call to line/fillrect etc.
Also, have you tried creating an invisible canvas object, drawing to that, and then copying to a visible canvas? This could be faster (proper double-buffering).
The problem is with the way the browsers use the native graphics APIs on the different OSes. And even on the same OS, using different APIs (for example GDI vs. Direct2D in Windows) would also produce different results.