RequestAnimationFrame call time changes depending on my canvas size - javascript

On my application, I have a canvas which CSS size (CSS, not html) updates depending on the window size.
I have a main gameplayLoop which looks like this :
run = function(){
console.log(timerDiff(frameTime));
game.inputManage();
game.logics();
game.graphics.animate();
game.graphics.render();
frameTime = timerInit();
requestAnimFrame(run);
}
My requestAnimFrame function is the one from Paul Irish :
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
So basically the problem is that the timer that I log which measures the time between the requestAnimFrame call and the effective function call totally changes.
If my browser is in fullscreen I get like 50/60 ms, and if I have a small window I can get to something like 10 ms.
As the requestAnimFrame call is supposed to call constantly the function under a 60fps rythm (which is something like 30 ms I think) I shouldn't have this kind of result, since there is basically nothing happening between the timer's creation and its check, except the requestAnimFrame
I also have some recurring micro-freezes (less than a second) which happens like every 2/3 seconds. But the timer doesn't detect any change in the time (like even the time counter of javascript is blocked)
My timers functions are like this but it doesn't really matter here
timerInit = function()
{
return new Date();
}
timerDiff = function(datePrev)
{
return new Date().getTime() - datePrev.getTime();
}

Well, the standard basically says that requestAnimationFrame will "do its best" to run at a "consistent" frame rate. That doesn't guarantee a 60fps; it just states that it will animate as fast as it can.
My experience with it has been as dim as yours so far unfortunately. I ended up going back to setTimeout. It goes at about the same rate, and graphical updates are accurate and don't skip a beat as they did with requestAnimationFrame. Sure it wasn't 60fps, but at least you could tell what was going on.
I'm sure the performance will only improve as browser developers optimize the new function, but for the time being, consider going back to timeouts.
Edit: I would like to remind people that this was answered a year ago, and the times have changed :)

"So basically the problem is that the timer that I log which measures the time between the requestAnimFrame call and the effective function call totally changes..."
Of course it does, that's why you need to measure it and BASED ON THIS TIME DIFFERENCE VALUE you calculate your next frame.
Let's assume you want to animate the 'left' css property of a div from 0px to 120px in 1 second with 60 fps.
using setTimeout, because YOU set the number of frames, you know how much you must increment the 'left' property: 120px/60frames = 2px/frame
using requestAnimationFrame, you have no ideea when the next frame will happen, until it happens; then you measure the time difference between this and the previous frame an you calculate the value you must increment the css 'left value' with; if the frame happens after 500ms, increment distance = (120px * 500ms)/1000ms = 60px; the ideea is that at the start of the animation you can't have a fixed, predetermined value to increment with at each frame, you need to calculate it dynamically based on the time difference
So even if the animation won't be at the advertised 60fps, if frames are skipped, every frame that is not skipped will draw the accurate updated situation.
You have to fiddle a bit to decide when the animation should end.

Related

Is there a better solution than setInterval when I need the interval to be very precise over time?

I'm building a browser game similar to Guitar Hero. The way it works is setInterval is set to execute every 16th note depending on the tempo, which is usually 100-150 milliseconds. The function it executes just checks if there's a note that needs to be checked against the user's keypresses.
But I've been reading about how setInterval can suffer from drift and may not be very accurate. For a song that lasts 3-4 minutes and needs 100ms precision (if the timing mechanics are even slightly off, that might be very frustrating for the users), it seems like it may not be the greatest solution.
So is there an alternative that's more precise? T
It probably would be a better idea to calculate everything in absolute time. So have a var starttime = Date.now();, and then calculate where every note should be var expected_note_time = starttime+beat_interval*beat_number. You can then add a listener on keypress and then log the exact time the keypress was hit. If abs(time_pressed-expected_note_time) < ALLOWED_MISTAKE_VARIANCE, then add that point to the user's score. Good luck.
Agree, setInterval have some issues, like it doesn't care whether the callback is still running or not and isn't that flexible.
you can implement you own method something like this :
function interval(func, wait, times){
var interv = function(w, t){
return function(){
if(typeof t === "undefined" || t-- > 0){
setTimeout(interv, w);
try{
func.call(null);
}
catch(e){
t = 0;
throw e.toString();
}
}
};
}(wait, times);
setTimeout(interv, wait);
};
this function has an internal function called interv which gets invoked automatically via setTimeout, within interv is a closure that checks the the repetition times, invokes the callback and invokes interv again via setTimeout. In case an exception bubbles up from the callback call the interval calling will stop and the exception will be thrown.
you can use it like this :
interval(function(){
// Code block goes here
}, 1000, 10);
which execute a piece of code 5 times with an interval or 10 seconds and you can do something in between.
You could cache the ticks at the start of the song, get the number of ticks since then, see if a note exists at that point.
Return the number of milliseconds since 1970/01/01:
var d = new Date();
var n = d.getTime();
The result of n could be:
1502156888172
From: https://www.w3schools.com/jsref/jsref_gettime.asp
and needs 100ms precision
The setInterval() function will drift because of the way javascript is built (event loop) and if you block it with a heavy CPU intensive task, it will delay. However, the drift will be very small (less that a ms) if you do it correctly.
A good way to avoid that would be to use multiple thread, but that is not easily achieved in JavaScript.

Is there any advantage to using requestAnimationFrame instead of setTimeout for debounce/throttle

I've come across this example on MDN that uses requestAnimationFrame instead of setTimeout:
// Reference: http://www.html5rocks.com/en/tutorials/speed/animations/
var last_known_scroll_position = 0;
var ticking = false;
function doSomething(scroll_pos) {
// do something with the scroll position
}
window.addEventListener('scroll', function(e) {
last_known_scroll_position = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(function() {
doSomething(last_known_scroll_position);
ticking = false;
});
}
ticking = true;
});
And it got me thinking whether we can use requestAnimationFrame instead of setTimeout(fn, 0) and if there is any advantage to this? I've googled and it seems that all comparisons are done in the context of animations, but what about unrelated functionality - like debounce/throttle or simply if you need to run code after repaint?
RequestAnimationFrame is good if you want to make sure you don't do unnecessary work because you changed something two times between 2 frame updates.
In itself, the requestAnimationFrame is not useful for anything other than animation, because it can only wait 16.666 milliseconds (if there's no lag), thus you need to chain multiple together. But if you pair it with setTimeout, then you will be able to wait a sepcific amount of milliseconds and also make sure you draw everything in the correct time.
requestAnimationFrame is called at the time when the browser is in the repaint step. So in theory it should avoid flickering/jittering if you try to sync properties of elements (like the position) with the scroll position.
setTimeout will be called after a minimum of n milliseconds, so you callback might be called multiple times before the next repaint and you will wast CPU usage, or multiple successive repaints happen without that the callback is called which will result into flickering/jittering if you try to sync properties of the elements with the scroll position.
Apart from above answers,
When using requestAnimationFrame your animations will only run when the tab (or window) is visible to the user. This means less CPU, GPU and memory usage, hence Battery Friendly. This is especially important for mobile devices that typically have a relatively short battery life.
But for smooth animations we have to take care that our frame rendering happens in less than 16ms, so callback should be as small as possible for 60 fps animations.

Is it safe to assume 60 fps for browser rendering?

I want to make a JavaScript animation take 5 seconds to complete using requestAnimationFrame().
I don't want a strict and precise timing, so anything close to 5 seconds is OK and I want my code to be simple and readable, so solutions like this won't work for me.
My question is, is it safe to assume most browsers render the page at 60 fps? i.e. if I want my animation to take 5 seconds to complete, I'll divide it to 60 * 5 = 300 steps and with each call of function draw() using requestAnimationFrame(), draw the next step of animation. (Given the fact the animation is pretty simple, just moving a colored div around.)
By the way, I can't use jQuery.
Edit: Let me rephrase the question this way: Do all browsers 'normally' try to render the page at 60 fps? I want to know if Chrome for example renders at 75 fps or Firefox renders at 70 fps.
(Normal condition: CPU isn't highly loaded, RAM is not full, there are no storage failures, room is properly ventilated and nobody tries to throw my laptop out the window.)
Relying on 60 frames per second is very unsafe, because the browser isn't always in the same conditions, and even if it tries to render the page at the maximum fps possible, there's always a chance of the processor/cpu/gpu being busy doing something else, causing the FPS to drop down.
If you want to rely on FPS (although I wouldn't suggest you so), you should first detect the current fps, and adjust the speed of your animation frame per frame. Here's an example:
var lastCall, fps;
function detectFps() {
var delta;
if (lastCall) {
delta = (Date.now() - lastCall)/1000;
lastCall = Date.now();
fps = 1/delta;
} else {
lastCall = Date.now();
fps = 0;
}
}
function myFunc() {
detectFps();
// Calculate the speed using the var fps
// Animate...
requestAnimationFrame(myFunc);
}
detectFps(); // Initialize fps
requestAnimationFrame(myFunc); // Start your animation
It depends on the GPU and monitor combination. I have a good GPU and a 120 hertz monitor, so it renders at 120 fps. During the render, If I move to 60 hertz monitor, it will max out at 60 fps.
Another factor, that happens in some browsers/OS, is the iGPU being used instead of the discrete gpu.
As already stated by others, it isn't.
But if you need to end your animation in approximately 5 seconds and it's not crucial not to miss any frames in the animation, you can use the old setTimeout() way. That way you can miss a target by a few milliseconds, and some of the frames in your animation will be skipped (not rendered) because of the fps mismatch, but this can be a "good enough" solution, especially if your animation is simple as you state it is, there's a chance that users won't even see the glitch.
It's not safe to assume everyone can handle animation.
People will have different needs.
A lot of common animations, and common web design practices, give me awful migraines, so I set my browser to 1 frame per second to kill the animation without causing too much fast flashing.

Smooth div width change in javascript

I have a simple function which is increasing the width of a div but its not doing it smoothly its kinda of "juddery".
I'm using request animation frame to do this on Chrome.. and I decided not to round the numbers so I could get decimal width increments.. but I can't get it to be smooth at all was wondering how I can improve on my method.
This is my function:
function test(data){
var start = parseInt(data.start);
var length = parseInt(data.length); //total length in minutes to complete
var dif = (new Date().getTime() / 1000) - start; //start holds seconds since epoch
var minutes = dif / 60; //convert seconds past into minutes past
var percentage = (minutes/length) * 100;
if(percentage > 100){ percentage = 100; }
if( percentage != 100 ){
document.getElementById('r').style.width = percentage+'%';
document.getElementById('rt').innerHTML = Math.round(percentage)+'%';
} else if (percentage == 100 ){
document.getElementById('r').style.width = percentage+'%';
document.getElementById('rt').innerHTML = 'COMPLETED';
}
}
My function is called like this:
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback, element){
window.setTimeout(callback, 200 / 100);
};
})();
function Update(){
requestAnimFrame( Update );
test();
}
JSFIddle: http://jsfiddle.net/RmXr9/7/
Any suggestions on ways to improve the smoothness of div width incrementing ?
try using css transitions. You get way smoother animations, but requires you to structure your code a bit differently. An example of a css transition property is this:
transition:300ms linear;
then whatever property you change (the width for example) will make a smooth linear 300 millisecond transition towards it.
so making a smooth width change is as simple as setting up the transition then doing something like this in javascript:
div.style.width="400px";
Here's a quick example i mocked up:
http://jsfiddle.net/nTMsC/1/
here's a nice tutorial to get you started:
http://www.css3.info/preview/css3-transitions/
One of the biggest causes of 'juddery' animations for me has always been frame rate. If your frame rate is too slow, obviously the animation 'judders'. But if it is too fast for the browser to handle, the browser gets confused, and you get a different kind of 'juddery'.
I'd recommend a frame rate of between 13 and 30 milliseconds. JQuery is supposed to use 13ms, but I've found that that is sometimes still too fast. I generally start with 16ms, and experiment from there.
The key is to ensure that you time it so that one frame starts as or after the previous frame is finished. This will depend on the code you process. I notice that you call the next frame before you begin processing the current frame, so it may be possible that you're still getting backed up. Perhaps try:
function Update(){
test();
requestAnimFrame( Update );
}
Your fallback function has a frame rate of 200 / 100, which is 2ms. It is extremely unlikely that your browser can complete the animation in 2ms, so it is likelyto get backed up. requestAnimationFrame uses a maximum frame rate of 16ms.
UPDATE:
The problem you're having, according to your jsfiddle, is that, while you're calculating your percentage often, the changes to the percentage are very small, and they don't translate into a change in the width of the div. This http://jsfiddle.net/RmXr9/13/ should demontrate the changes in the percentage, and show the corrsponding changes in actual width. So, although you do a calculation often (maybe 60 times a second), the actual visual change only happens once every 16 frames or so. So, your actual frame rate is only about 4 frames per second, which makes a 'juddery' animation. Your only options, I'm afraid, are to make the animation run faster (perhaps by decreasing your length variable), or to make the div much longer (or both).
As an aside, I notice you don't have a way to stop the animation at the end, and I've added that into the jsfiddle as well.

How to tell what's causing slow HTML5 Canvas performance?

How can I tell if the canvas's slow performance is caused by the drawing itself, or the underlying logic that calculates what should be drawn and where?
The second part of my question is: how to calculate canvas fps? Here's how I did it, seems logical to me, but I can be absolutely wrong too. Is this the right way to do it?
var fps = 0;
setInterval(draw, 1000/30);
setInterval(checkFps, 1000);
function draw() {
//...
fps++;
}
function checkFps() {
$("#fps").html(fps);
fps = 0;
}
Edit:
I replaced the above with the following, according to Nathan's comments:
var lastTimeStamp = new Date().getTime();
function draw() {
//...
var now = new Date().getTime();
$("#fps").html(Math.floor(1000/(now - lastTimeStamp)));
lastTimeStamp = now;
}
So how's this one? You could also calculate only the difference in ms since the last update, performance differences can be seen that way too. By the way, I also did a side-by-side comparison of the two, and they usually moved pretty much together (a difference of 2 at most), however, the latter one had bigger spikes, when performance was extraordinarily low.
Your FPS code is definitely wrong
setInterval(checkFps, 1000);
No-one assures this function will be called exactly every second (it could be more than 1000ms, or less - but probably more), so
function checkFps() {
$("#fps").html(fps);
fps = 0;
}
is wrong (if fps is 32 at that moment it is possible that you have 32 frames in 1.5s (extreme case))
beter is to see what was the real time passes since the last update and calculate the realtimepassed / frames (I'm sure javascript has function to get the time, but I'm not sure if it will be accurate enough = ms or better)
fps is btw not a good name, it contains the number of frames (since last update), not the number of frames per second, so frames would be a better name.
In the same way
setInterval(draw, 1000/30);
is wrong, since you want to achieve a FPS of 30, but since the setInterval is not very accurate (and is probably going to wait longer than you say, you will end up with lower FPS even if the CPU is able to handle the load)
Webkit and Firebug both provide profiling tools to see where CPU cycles are being spent in your javascript code. I'd recommend starting there.
For the FPS calculation, I don't think your code is going to work, but I don't have any good recommendation :(
Reason being: Most (all?) browsers use a dedicated thread for running javascript and a different thread for running UI updates. If the Javascript thread is busy, the UI thread won't be triggered.
So, you can run some javascript looping code that'll "update" the UI 1000 times in succession (for instance, setting the color of some text) - but unless you add a setTimeout to allow the UI thread to paint the change, you won't see any changes until the 1000 iterations are finished.
That said, I don't know if you can assertively increment your fps counter at the end of the draw() routine. Sure, your javascript function has finished, but did the browser actually draw?
Check if you dont use some innerHTML method to debug your project. This can slow your project in a way you can't imagine, especially if you do some concatenation like this innerHTML += newDebugValues;
Or like desau said, profile your cpu usage with firebug or webkit inner debugger.

Categories

Resources