requestAnimationFrame javascript alert increment jumps from 1 to 9 or 13 - javascript

I've got this weird problem, I'm incrementing by 1, and yet, the increment that appears when the javascript window pops up, shows that I have incremented either by 9 or 13, the either comes from whether I am incrementing by 1 or -1 respectively. what is up with that?
This the function being called by the requestAnimationFrame
function stream1() {
if (y > origin_y){
var xOffset = -1;
} else if (y == origin_y){
var xOffset = 1;
} else {
var xOffset = 1;
}
var offset = $( "#widget1" ).offset();
var x = offset.left;
var y = offset.top;
console.log(' X - '+x+' Y - '+y);
$( "#widget1" ).offset({ top: y-yOffset, left: xInitial+xOffset });
}
This is the animation frame
var globalID;
function repeatOften() {
stream1();
requestAnimationFrame(repeatOften);
}
It probably doesn't make sense that in the time for the alert to disappear and reappear, 9 iterations have been complete right? It's supposed to be 60 times a second supposedly and it has been like 1 second so shouldn't it be 60 and not 9 or 13? I don't know where these arbitrary numbers come from.
To summarize again, initially xInitial is located at 1114 px, then it goes to 1105 or 1103 and then 9 or 13 gaps subsequentially every time so why is that?

First, avoid expensive operation in frame callback. Like $( "#widget1" ).offset(). Mind that it is DOM operation, it can be slow and broke all timing. You can get DOM id and offset before animation starts and then remember just current offset left and top.
Second, if you want to be super precise, you can use handler urgument, which is timestamp and if you store animation start timestamp you can compute exact position regardless of real frame ratio.

Related

How to run a requestAnimationFrame animation for a certain duration?

I have the following function that scrolls some elements "up" out of view by adjusting their style every "tick":
const incr = 1;
let moved = 0;
function changeHeight( children, duration, setTop) {
// duration = 1500
const height = children.clientHeight; // in this case, 166px
let moved = 0;
const slideUp = function (timestamp) {
// we're done if the amount moved is larger than height
if ( moved < height ) {
children.style.top = `${ setTop( moved, height ) }px`;
moved = moved + incr // move by some amount
requestAnimationFrame(slideUp)
} else {
// reset
moved = 0;
}
};
// start sliding
slideUp();
}
If requestAnimationFrame triggers roughly every 16ms or so, I would like to use duration to dictate how long the animation will be running for, so the formula seems to be height * 16.5 / duration
I'm confused by requestAnimationFrame - why is the time per tick not constant? I'd like to use timestamp that's generated by requestAnimationFrame but the first few cycles take much longer than the average of ~16.5
Is the 16.5 going to look different on a different machine or screen?
How do I make the height change take exactly the amount of time specified?
What you want is called delta-time.
The formula is Math.min((now - start) / duration, 1) * final_amount.
Using this delta-time, you don't need to care at which frequency your interval fires, every step is rendered "where it should be".
As for your questions,
why is the time per tick not constant
Certainly because the browser has a lot of things to do during the first frames and couldn't do everything in the 16.7ms frame. It will thus move your callback to be executed a bit later, and may even skip frames if under too much pressure.
Is the 16.5 going to look different on a different machine or screen?
Yes, requestAnimationFrame will basically try to follow the monitor's refresh rate. So on a 60Hz monitor you'll indeed have 16.7ms per frame, but on a 120Hz monitor you'd have only half of it.
How do I make the height change take exactly the amount of time specified?
Use a delta-time:
const elem = document.querySelector("div");
let moved = 0;
changeHeight(elem, 200, 5000);
function changeHeight(elem, height, duration) {
const start = performance.now();
const step = function () {
const now = performance.now();
const delta = Math.min((now - start) / duration, 1);
elem.style.height = (delta * height) + "px";
if (delta < 1) {
requestAnimationFrame(step);
}
};
step();
}
div { width: 50px; background: green; }
<div></div>

How do I generate a random X value for each "projectile" in my falling objects game using Javascript?

I am coding a game that is currently in its very early stages for a project to try to learn more about coding. In my game, objects generate randomly (green squares), and the player (red square), avoids them. I am having trouble trying to get the green squares to generate from a random position on the x-axis. I already have a formula to generate a random number for X, but after it selects a number randomly, all the "projectiles" generate there, rather than all generating from a different area. How would I get all the "projectiles" to generate from different positions on the x-axis randomly?
var randomX = Math.floor(Math.random() * 480) + 15;
function updateGameArea() {
var x, y;
for (i = 0; i < projectiles.length; i += 1) {
if (player.crashWith(projectiles[i])) {
gameArea.stop();
return;
}
}
gameArea.clear();
gameArea.frameNo += 1;
if (gameArea.frameNo == 1 || everyinterval(150)) {
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
for (i = 0; i < projectiles.length; i += 1) {
projectiles[i].y += -1; // the shape is using its coordinates to build downward from its y position
projectiles[i].update();
}
player.newPos();
player.update();
}
function everyinterval(n) {
if ((gameArea.frameNo / n) % 1 == 0) {return true;}
return false;
Expected: Green squares generate in random positions on the x- axis every 3 seconds and move upwards
Actual: Green squares all generate from the same random position on the X-axis.
You should reset X every time you're adding a new projectile:
if (gameArea.frameNo == 1 || everyinterval(150)) {
randomX = Math.floor(Math.random() * 480) + 15;
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
Otherwise, the randomX value stays constant as the value originally evaluated on line 1 when the interpreter reached it.
Here's your problem:
var randomX = Math.floor(Math.random() * 480) + 15;
// Generates a random number and stores it to randomX
// Called using 'randomX'
You need to turn it into a function if you want it to run each time:
var randomX = function() { Math.floor(Math.random() * 480) + 15 };
// Returns a new number each time
// Called using 'randomX()'
Both shivashriganesh mahato and natelouisdev have, essentially responded to how to fix the issue but since you are learning coding here is a tip. When you code, the code will run in a particular order. If you want something to be reassigned repeatedly, in this case a randomized number being used, and you want it to occur only after an event, you need to make sure that it gets trigger within each event.
natelouisdev has a good approach because, by using it as a function, you can call your randomizer more cleanly in your code and make it reassign the value of x each time.
Since you are building a game, it is also a good idea to compartmentalize your code. It'll make it easier to keep your ideas in order for each event trigger.
Example:
function gameLoss(){} - Define event return upon game loss. You can
then create editable rules to reason for loss without having to edit
the loss
function gameActive(){} - Defines what is normal gameplay. everything that occurs during normal gameplay should be managed here.
function gameArea(){} - Defines game canvas that function more for UI than for gameplay (scores, lifes, size of screen, etc)
Had you created individual functions you'd realize you only need a randomized 'x' value upon regular play thus you'd assign it within the gameActive() function and not as a global variable. Then you'd call the gameActive() function as many times as needed within a time interval to ensure a unique value is created each time.
-Side note: Don't litter unnecessary global variables. It'll make a mess off of your code when debugging. -

Javascript move target around screen?

So I'm working on a basic shooter, part of which involves moving a target around the screen. I'm using babylon.js as the engine and my goal is to have the target appear for 0.75 seconds on the screen, then disappear for 0.5 seconds, then reappear at a different random location. The current code I have for that is this:
function moveTarget(canvas, scene){
setTimeout( function (){
scene.meshes[10].visibility = 0; //how I access the target object
randX = genRandNum(minX, maxX); //This is a separate function that works
randY = genRandNum(minY, maxY);
scene.meshes[10].position = new BABYLON.Vector3(randX, randY,
scene.meshes[10].position.z);
scene.meshes[10].visibility = 1;
x ++;
if (x < amount){
moveTarget(canvas, scene);
}
}, tarDuration * 1000)
}
which succeeds in everything except the 0.5 second delay between appearances of the target, ie currently it flashes from location to location with no space in between. I'm thinking that I need a second setTimeout but I'm not entirely sure how to include that or where it would go. Any pushes in the right direction would be much appreciated.
The way I would do this is to set a timeout for the full cycle time (0.75 s + 0.5 s) and then another timeout inside that for the 0.5 s delay.
function moveTarget(canvas, scene){
setTimeout( function (){
setTimeout( function(){
// Your other code
x ++;
if (x < amount){
moveTarget(canvas, scene);
}
}, yourDelayHere)
}, tarDuration * 1000)
}
Where yourDelayHere gives the desired 0.5 s delay. I created a Babylon.js playround which shows a simplified example here.

HTML5 Canvas performance - calculating loops/frames per second

I know a few questions have been asked like this one before, such as this: Check FPS in JS? - which did work to some degree, I was able to find out how long each loop took to complete.
What I am looking for though is something more readable and controllable. I want to be able to set the refresh rate for the FPS counter to make it slow so it is human readable or as fast as the application can run, so I can use it on some kind of speedometer.
Anyway so here is the code I have right now:
var lastLoop = new Date().getTime();
function updateStage()
{
clearCanvas();
updateStageObjects();
drawStageObjects();
var thisLoop = new Date().getTime();
var fps = (thisLoop - lastLoop);
$('#details').html(fps);
lastLoop = thisLoop;
iteration = setTimeout(updateStage, 1);
}
Am I right to be setting the setTimeout function to a speed of 1 millisecond? I was thinking this will just make it loop as fast as it possibly can.
Should I count every 100 frames or so, find out how many milliseconds it took to run 100 frames then make a calculation to find out how many frames it would have done if the milliseconds were 1000? What would this calculation be?
To make the result more accurate I am guessing I need to display averages as one frame can vary a significant amount, how should I do this?
Any tips are greatly appreciated.
Thanks.
Note that the faster you update your output, the more you will affect your measurement. Although minimal, I try to update my fps output once per second or less unless it's necessary to go faster.
I like to have a low-pass filter on my results so that a temporary hiccup doesn't affect the values too strongly. This is easier to compute and write than a moving average, and doesn't have the problem of an overall average where your 'current' readings are affected by total performance over the entire run (e.g. anomalous readings during startup).
Put together, here's how I usually measure FPS:
var fps = 0, now, lastUpdate = (new Date)*1;
// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;
function drawFrame(){
// ... draw the frame ...
var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
if (now!=lastUpdate){
fps += (thisFrameFPS - fps) / fpsFilter;
lastUpdate = now;
}
setTimeout( drawFrame, 1 );
}
var fpsOut = document.getElementById('fps');
setInterval(function(){
fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);
Ive tried something out,
If you change the
lastUpdate = now
to
lastUpdate = now * 1 - 1;
Your NaN problem is solved! This is also used where the lastUpdate is defined. Probably because it is not able to convert the date to unix timestamp.
The new result will be:
var fps = 0, now, lastUpdate = (new Date)*1 - 1;
// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;
function drawFrame(){
// ... draw the frame ...
var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
fps += (thisFrameFPS - fps) / fpsFilter;
lastUpdate = now * 1 - 1;
setTimeout( drawFrame, 1 );
}
var fpsOut = document.getElementById('fps');
setInterval(function(){
fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);
I've taken the solution(s) posted and enhanced them a little. Have a look here - http://jsfiddle.net/ync3S/
I fixed that NaN error by using Date.now() instead of constructing a new date object each time and trying to reference it. This also prevents some garbage collection necessity.
I neatened up the variable and function names a bit and added some extra commenting - not necessary but nice to have.
I included some drawing code for testing.
I added fpsDesired as a test var for the engine loop.
I started fpsAverage at fpsDesired so with the fpsFilter it doesn't work up from 0 to the real FPS, rather starting at the desired FPS and adjusting from there.
Drawing now blocks incase it already was drawing, and this can be used for pausing and other control functions.
The main block is as follows:
var fpsFilter = 1; // the low pass filter to apply to the FPS average
var fpsDesired = 25; // your desired FPS, also works as a max
var fpsAverage = fpsDesired;
var timeCurrent, timeLast = Date.now();
var drawing = false;
function fpsUpdate() {
fpsOutput.innerHTML = fpsAverage.toFixed(2);
}
function frameDraw() {
if(drawing) { return; } else { drawing = true; }
timeCurrent = Date.now();
var fpsThisFrame = 1000 / (timeCurrent - timeLast);
if(timeCurrent > timeLast) {
fpsAverage += (fpsThisFrame - fpsAverage) / fpsFilter;
timeLast = timeCurrent;
}
drawing = false;
}
setInterval(fpsUpdate, 1000);
fpsUpdate();
setInterval(frameDraw, 1000 / fpsDesired);
frameDraw();
Going to have a tinker and see if I can come up with something smoother, as this thread is near the top in Google results.
Let's see what we can all come up with as a team, and I think it's always neat to not use 3rd party libraries, making the code portable for anyone :)
-Platima
Just set a interval that is resetting the fps counter every second.
var fpsOut, fpsCount;
var draw = function () {
fpsCount++;
..Draw To Canvas..
..Get the fps value: fpsOut
requestAnimationFrame(draw);
};
setInterval(function () {
fpsOut = fpsCount;
fpsCount = 0;
}, 1000);
draw();
If you want real-time updates, consider making it loop again and again in real time. To make it affect the performance less, only update the controlled variable, in this case, the FPS. You can have optional Frame Latency, which I will put here, just in case. Just copy, paste and tweak the code to your needs.
Take note that a single frame lasts for 16.66 miliseconds.
setInterval(function(){var latencybase1 = parseFloat(new Date().getTime());
var latencybase2 = parseFloat(new Date().getTime());
var latency = latencybase2-latencybase1;
var fps = Math.round(1000/latency);
if (latency<16.66)
{document.getElementById("FPS").innerHTML = fps+"
FPS";}
else {document.getElementById("FPS").innerHTML = ""+fps+" FPS";}
document.getElementById("Latency").innerHTML = latency+" ms";}, 0);

JavaScript: Achieving precise animation end values?

I'm currently trying to write my own JavaScript library. I'm in the middle of writing an animation callback, but I'm having trouble getting precise end values, especially when animation duration times are smaller.
Right now, I'm only targeting positional animation (left, top, right, bottom). When my animations complete, they end up having an error margin of 5px~ on faster animations, and 0.5px~ on animations 1000+ ms or greater. Here's the bulk of the callback, with notes following.
var current = parseFloat( this[0].style[prop] || 0 )
// If our target value is greater than the current
, gt = !!( value > current )
, delta = ( Math.abs(current - value) / (duration / 13) ) * (gt ? 1 : -1)
, elem = this[0]
, anim = setInterval( function(){
elem.style[prop] = ( current + delta ) + 'px';
current = parseFloat( elem.style[prop] );
if ( gt && current >= value || !gt && current <= value ) clearInterval( anim );
}, 13 );
this[0] and elem both reference the target DOM element.
prop references the property to animate, left, top, bottom, right, etc.
current is the current value of the DOM element's property.
value is the desired value to animate to.
duration is the specified duration (in ms) that the animation should last.
13 is the setInterval delay (which should roughly be the absolute minimal for all browsers).
gt is a var that is true if value exceeds the initial current, else it is false.
How can I resolve the error margin?
You can run into rounding errors that will end up adding up to a different value than you expected. You might take a look at a really simple animation library and see the method they use to overcome this (as well as some easing techniques). Here is Thomas Fuchs' Emile which is about 50 lines of code:
http://github.com/madrobby/emile/blob/master/emile.js
In addition to what Alex says, you're usually better off using time-based calculation rather than trying to make discrete steps. This ensures accuracy and allows the animation to run at a reasonable speed when the browser's not fast enough to reliably call you back every n microseconds.
function animate(element, prop, period, value) {
var v0= parseFloat(element.style[prop] || 0); // assume pre-set in px
var dv= value-v0;
var t0= new Date().getTime();
var anim= setInterval(function() {
var dt= (new Date().getTime()-t0)/period;
if (dt>=1) {
dt= 1;
clearInterval(anim);
}
element.style[prop]= v0+dv*dt+'px';
}, 16);
}

Categories

Resources