To produce an animation that will draw a circle using 1 minute there is no need to use rAF as this just generate extra load even though I personally recommend rAF for most cases.
In cases like this however where monitor sync is not that critical setInterval
(and setTimeout
) are probably better choices in regard to load.
Here is modified code that draws a circle per minute. It is based on actual time stamp so the timing is pretty accurate. The interval here is set to 120 ms but this should really be related to the circle's circumference as this would determine how many pixels are to be draw within that time frame as overlapping pixels won't be that visible (disregarding sub-pixeling here). Feel free to adjust the time out as needed.
The setup is now as follows (window.onload is not needed on fiddle so I removed it, but of course you need to put it back if you load the scripts in header in your final page). The var names could be better but I kept somewhat the original names:
start_angle = 1.5 * Math.PI, /// common offset (north)
end_angle = 2 * Math.PI, /// ends full circle in radians
increase_end_angle = 0, /// current angle incl. offset
radius = 50,
startTime = (new Date()).getTime(), /// get current timestamp
diff; /// used for timestamp diff
We also move static settings outside the loop to save some CPU cycles (actually setting stroke style etc. do have an effect if set all the time so this is more optimal). There is neither any need to use save
/ restore
as we are not changing a lot of variables during out loop that is needed elsewhere:
context.lineWidth = 6;
context.lineCap = "round";
The main function is resetting the circles based on actual time:
setInterval(anim, 120); /// 120 for demo, use Ø and time to find optimal timeout
function anim() {
/// calc difference between initial and current timestamp
diff = (new Date()).getTime() - startTime;
diff = diff / 60000; /// 60000ms = 60s, now we have [0, 1] fractions
/// final angle
increase_end_angle = start_angle + end_angle * diff;
/// draw circle
context.beginPath();
context.arc(x, y, radius, start_angle, increase_end_angle);
context.stroke();
/// check diff fraction
if (diff >= 1) { /// if diff >= 1 we have passed 1 minute
/// update time and new radius
startTime = (new Date()).getTime();
radius += 10; /// add to current radius
};
}
Ideally you would clear the current circle for each draw to keep the ant-aliasing pixels to get a smoother look as redrawing on top will eventually remove this due to the alpha channel.
Of course that would mean you needed to do some extra steps when radius is increased such as drawing the current content to a canvas in the back to keep the already drawn circles.
Update: You can also make the canvas "high-resolution" to reduce the crud-ness of the arc method by setting the canvas:
canvas.width = wantedWidth * 2;
canvas.height = wantedHeight * 2;
canvas.style.width = wantedWidth + 'px'
canvas.style.height = wantedHeight + 'px';
Just remember to scale all coordinates and sizes accordingly (x2).
Updated fiddle running with high-resolution canvas
Update: to address the additional questions:
1)how does multiplying everything by 2(i.e scale) makes everything sharper.
What happens in the "high-resolution mode" is that we are using a canvas twice the size of the initial one, but by applying the extra styling (CSS) we scale the whole canvas back down to the first size.
However, now there are twice as many pixels in the same area and due to sub-pixeling we can utilize this to gain better "resolution". But at the same time as there are twice as many we also need to scale everything to get it back in the same position as before we used a double sized canvas.
It's like an image of lets say 400x400. If you show this at 400x400 then the pixel ratio is 1:1. If you instead used an image of 800x800 but forced its size down to 400x400 there would still be 800x800 pixels in the image but those that can't be shown (as a monitor can't show half pixels) are interpolated to make it appear there is a half pixel there.
For shapes however you can achieve a better detailed version of the shape as it is first anti-aliased then interpolated with what surrounds it making it look smoother (as you can see in the demo).
2)i dint understood the setInterval(anim, 120);//this part..ratio..is this why circle gets completed at 60 seconds? and again i am always doubtful when using setInterval.Reason being it does provides jerk after some time.Not in this case though.I dint wanted my animation to stop which always happened when using RAF.But again raf would be great for optimization.So a little confusion was there,but i guess i will go by the setInterval way.
The jerks to take that first is due to setInterval
and setTimeout
are not capable of syncing to the monitor's VBLANK. VBLANK comes from the old CRT TVs when the beam that scanned the monitor screen on the inside, started a new frame. To sync properly you sync to the VBLANK gap. This goes for all equipment related to video incl. computers.
However as this would require a float resolution of the timer (16.7ms in case of 60Hz) this is not possible with these timers. In between for time to time you will get a rounding error sort of causing a frame to be skipped in the loop so-to-speeak - resulting in jerks. In comes rAF (requestAnimationFrame
) which is capable of syncing to the monitor's refresh rate, but not only for this reason. It is more low-level and efficient and can also reduce power consumption as a result.
There is nothing wrong in using inaccurate timers if you don't need monitor sync (up to) 60 times per seconds - as in this case where you are more dependent on the radius of the circle drawn over a complete minute. If you used rAF you would probably draw over the same pixel many times during that time not able to see any change (though sub-pixeling would kick in and still allow a small visual change for some of those pixels). So here rAF doesn't serve a purpose as you would do many unnecessary draws that would not make a difference on the screen.
setInterval
is not bad per-SE but for animation it is usually too inaccurate where you need constant update. What it does is to create an event and put it in the browser's event queue. When possible the event is executed at about the time it timed out for (the queue contains many sorts of events such as repaint, function callings etc.). Not super accurate but accurate enough here as we don't depend on when it times out but using the actual time when we update the arc. So for this purpose this works as we draw in very small increments that will mask the subtle inaccuracies.
That doesn't mean other factors may trigger jerks in the update but that goes for rAF as well. Nothing is perfect.
As to the time 120ms - this is just a number I started with. It's a number that didn't change the smoothness too much. It's not what completes the circle however.
What completes the circle is the difference in time "Now time" - "Start time"
. This would give a difference of 60,000 milliseconds in this case as we use 60 seconds per round. So to get a useful number we divide on 60000 so the number we get is between 0.0 and 1.0 which we can multiply directly with the angle to get 100% angle when diff is at 1.
The ideal timer is to use time in relation to the circumference of the circle. This would dictates how much time there would be between each new pixel so you could for example divide this on 10 again to consider sub-pixeling. This will be optimal as there won't be overlaps at the end point for the update but each time the loop is triggered a new pixel will be drawn.
3)this question is a little difficult and i am working on it for now.In case I am not able to do it,i would take your advice.It deals with some json,creating multiple instances and stopping the animation when data stops coming.I will try that tomorrow,too tiredd now.
For this I would suggest opening a new question. And it seems as the meta-answer out-grew the main answer.. :-o :-)