HTML/Javascript Ascending Number - javascript

I'm trying to make a simple javascript game. Basically, you get pancakes at a certain amount per second. In the code, I call this value (the rate at which you get pancakes) pps. I want to make it so that as the HTML span tag that shows the total amount of pancakes gets more pancakes, (at the rate of pps), it ascends so it looks nicer.
For example, if I get pancakes at 5 pps, right now it just goes 0, 5, 10, etc... every second. I want it to go 0,1,2,3,4,5(1 second), next second 6,7,8,9,10, etc...
Here is the code that I have so far, for the pancake counter:
pps = 100;
tp = 0;
window.setInterval(function(){
tp += parseInt(pps);
document.getElementById("test").innerHTML = tp;
}, 1000);
Anyone know how to do this?

This is a problem common to all games, and one that you need to solve correctly for your game to work outside of your own computer.
The correct solution is that you need to measure the elasped time between each iteration of your game loop, or between each frame render. This is, in practice, going to be a very small number; you can think of this number as a "scaling factor".
If your game was about moving a space ship, and you wanted it to move 5 screen units per second, your game loop would:
Find the time elapsed since the last interval, in seconds. In a game rate-limited to 60 frames-per-second, this would be around 1/60th of a second
Multiply the ship's speed (5 units per second) by 1/60; the ship would move 0.8333... units this tick
move the ship by that amount.
By the time 1 full second has passed, the ship will have moved 5 units.
The exact same principal applies to your PPS.
The important part is that, in the real world, it will not be exactly 1/60th of a second between frames. If you're not computing the "scaling factor" each iteration of your loop, your game will slowly accrue error. setInterval is particularly bad for this, and not at all suitable as a source for time in a game.
The implementation in JavaScript is simple: Each game loop, record the current time from whatever source of time is available to you; in your case, you can use get Date().getTime(), which returns the time since the UNIX epoch in milliseconds. Record this value.
In the subsequent redraw, you will again call get Date().getTime(), and then subtract the previous value to the the elapsed time. That is your scaling factor, in milliseconds. You can multiply pps by that value to determine how many pancakes to add.
It's important that you still follow this approach, even if you're using setInterval. You might think you can simply setInterval(..., 1000 / 60) to invoke your callback 60 times per second, but setInterval (and setTimeout) are not accurate - they invoke your callback at least that far in the future, but potentially much further. You still need to scale pps by the elapsed times since the last redraw.
Here's a simple implementation:
var PPS = 5;
var lastTime = new Date().getTime();
var cakes = 0;
setInterval(function () {
var currentTime = new Date().getTime()
var elapsedTime = currentTime - lastTime;
lastTime = currentTime;
cakes += (PPS * (elapsedTime / 1000)) // elapsedTime is in milliseconds, divide by 1000 to get fractional seconds
document.getElementById('pps').innerText = cakes;
}, 10);
<div id="pps"></div>
As an aside, the incorrect solution is one you find in a lot of old games: Increment things as fast as you can. On old computers this was a viable solution; the game redrew slowly enough that the game would advance smoothly. As computers got faster, the game would run faster, until it became unplayable.

A simple interval timer would do the trick. Something like this:
function incrementToNumber(tag, currentNumber, targetNumber) {
var numTicks = targetNumber - currentNumber;
var interval = setInterval(function() {
currentNumber++;
tag.innerText = currentNumber;
if(currentNumber == targetNumber) {
clearInterval(interval);
}
}, 1000 / numTicks);
}
That particular function increments over the course of one second. To change the time it takes to increment, swap out the 1000 with whatever milliseconds you want it to take.
For a version that increases forever:
function inrementForever(tag, currentPancakes, pancakesPerSecond) {
setInterval(function() {
currentPancakes++;
tag.innerText = currentPancakes;
}, 1000 / pancakesPerSecond);
}

Related

Making a clock that takes 2560ms to complete a revolution in JavaScript

I'm trying to make a clock for a Libet task - a cognitive task used by psychologists. By convention these clocks take 2560 ms to complete a revolution. Mine seems to be running quite a lot slower and I can figure out why.
I have made an example of the issue here: https://jsfiddle.net/Sumoza/re4v7Lcj/8/
On each iteration of a setInterval with a 1ms delay, I increase the angle of rotation of the hand by (Math.PI*2)/2560, i.e. 1/2560th of a circle in radians. So, incrementing one of these each ms should complete the circle in that number of ms. As you can see from the clock, this runs a fair bit slower. Can anyone shed any light as to why. Interestingly, *10 seems to look a lot closer to what I want - but that doesn't make much sense to me so I'm wary of the fix. Relevant JS from the fiddle below. Thanks for any help.
var time = 0;
var rad_tick = (Math.PI*2)/2560; //radians per tick: last number is ms per revolution
// Draw Clock
const clock = document.getElementById('clock')
const clock_ctx = clock.getContext('2d')
clock_ctx.translate(clock.width/2, clock.height/2);
radius = (clock.height/2) * 0.7;
function drawClock() {
clock_ctx.clearRect(-clock.width/2, -clock.height/2, clock.width, clock.height);
clock.style.backgroundColor = 'transparent';
clock_ctx.beginPath();
clock_ctx.arc(0, 0, radius, 0 , 2 * Math.PI);
clock_ctx.stroke();
}
drawClock();
// Draw Hand
const hand = document.getElementById('hand')
const hand_ctx = hand.getContext('2d')
hand_ctx.translate(hand.width/2, hand.height/2);
function drawHand(time){
hand_ctx.clearRect(-hand.width/2, -hand.height/2, hand.width, hand.height);
hand_ctx.beginPath();
hand_ctx.moveTo(0,0);
hand_ctx.rotate(time);
hand_ctx.lineTo(0, -radius*0.9);
hand_ctx.stroke();
hand_ctx.rotate(-time);
}
function drawTime(){
time+=rad_tick;//*10
drawHand(time)
}
var intervalId = setInterval(drawTime, 1);
Try this
var starttime = new Date().getTime();
function drawTime() {
var elapsed = new Date().getTime() - starttime;
var time = elapsed % 2560;
time = time * rad_tick;
console.log(elapsed + "->" + time);
drawHand(time)
}
Base things on the current time, and don't depend on setInterval to fire at the exact time you set it to fire. If there are things in-queue at the time that setInterval is supposed to fire, it will do those things in-queue first and only then run the function you've wired to setIterval (causing the delays you see --which build up more and more over time). Learn about the event loop and requestAnimationFrame.
Instead, get the time (freshly) prior to doing something, and calculate how much time has passed since the start time. Base your animations on these calculations. This way, even if there are slight delays, your last action will always sync things to the way they're supposed to look right now.
When you do it this way, you might lose some frames (skipping things the engine didn't have time to do), but it will likely complete much closer to the expected completion time.
The bottom line is, if you tell the browser to do more than it can do in a particular amount of time, there will be delays. However, you can skip frames of an animation to make things complete much closer to the expected completion time.
You ask setInterval to call your callback every 1ms, but does it actually call it every 1ms?
Try this code:
let cnt = 0;
setInterval(() => cnt++, 1);
setTimeout(() => { console.log(cnt); }, 1000);
For me, it prints something between 230 and 235. So looks like the actual (average) minimum interval for me is a bit over 4ms.
Adding on to Stig, since javascript isn't super fast when it comes to these things, it won't be super accurate. Using something like getTime() will allow proper timing. How about you call getTime in your drawTime function, so that around every millisecond, it will check the actual time and use that value.

get a smooth animation for a canvas game

How to get a better animation, dinamically, even when browser is busy or idle, for different devices which have different hardware capacity.
I have tried many ways and still cannot find the right way to make the game to display a better animation.
This is what i tried:
var now;
var then = Date.now();
var delta;
window.gamedraw = function(){
now = Date.now();
delta = now - then;
if(delta > 18){
then = now - (delta % 18);
game_update();
}
}
window.gameloop = setInterval(window.gamedraw,1);
18 is the interval value to update the game, but when browser is busy this interval is not good, and it needs to lower. How to get a better animation dinamically, even when browser is idle or busy ?
I suppose that the interval value is the problem, because if interval is lower then game animation is very fast, if this value is 18 then game animation is good but not when browser is busy, and I do not have idea how to change it dinamically.
To get a smooth animation, you must :
• Synchronise on the screen.
• Compute the time elapsed within your game.
• Animate only using this game time.
Synchronizing with the screen is done by using requestAnimationFrame (rAF).
When you write :
requestAnimationFrame( myCalbBack ) ;
You are registering myCalbBack to be called once, the next time the screen is available to draw on.
( If you know about double buffering (canvas are always double-buffered), this time is the next time the GPU will swap the draw buffer with the display buffer. )
If, on the other hand, you don't use rAF but a interval/timeout to schedule the draws, what will happen is that the draws won't get actually displayed until next display refresh. Which might happen (on a 60Hz display) any time from right now to 16.6 ms later.
Below with a 20ms interval and a 16 ms screen, you can see that the images actually displayed will be alternatively 16ms away OR 2*16ms away - never 20, for sure-. You just can't know, from the interval callback, when the actual draw will show. Since both rAF and Intervals are not 100% accurate, you can even have a 3 frames delta.
So now that you are on sync with the screen, here's a bad news : the requestAnimationFrame does not tick exactly regularly. For various reasons the elapsed time in between two frames might change of 1ms or 2, or even more. So if you are using a fixed movement each frame, you'll move by the same distance during a different time : the speed is always changing.
(expl : +10 px on each rAF,
16.6 display -->> rAF time of 14, 15, 16 or 17 ms
--> the apparent speed varies from 0.58 to 0.71 px/ms. )
Answer is to measure time... And use it !
Hopefully requestAnimationFrame provides you the current time so you don't even have to use Date.now(). Secondary benefit is that this time will be very accurate on Browsers having an accurate timer (Chrome desktop).
The code below shows how you could know the time elapsed since last frame, and compute an application time :
function animate(time) {
// register to be called again on next frame.
requestAnimationFrame(animate);
// compute time elapsed since last frame.
var dt = time-lastTime;
if (dt<10) return;
if (dt >100) dt=16; // consider only 1 frame elapsed if unfocused.
lastTime=time;
applicationTime+=dt;
//
update(dt);
draw();
}
var lastTime = 0;
var applicationTime = 0;
requestAnimationFrame(animate);
Now last step is to always use time inside all you formulas. So instead of doing
x += xSpeed ;
you'll have to do :
x += dt * xSpeed ;
And now not only the small variations in between frames will be taken into account, but your game will run at the same apparent speed whatever the device's screen (20Hz, 50Hz, 60 Hz, ...).
I did a small demo where you can choose both the sync and if using fixed time, you'll be able to judge of the differences :
http://jsbin.com/wesaremune/1/
You should use requestAnimationFrame instead of setInterval.
Read this article or this one.
The latter one is in fact exactly discussing your approach (with the delta time), and then introduces the animation frame as a more reliable alternative.
The first article is really a great resource. For starters, with your interval of 18 ms you're apparently aiming for something close to 60 fps. This is in fact the default for requestAnimationFrame, so you don't need to write anything special:
function gamedraw() {
requestAnimationFrame(gamedraw); //self-reference
game_update(); //your update logic, propably needs to handle time intervals internally
}
gamedraw(); //this starts the animation
If you want to set the update interval explicitly, you can do so by wrapping the requestAnimationFrame inside a setInterval, like this:
var interval = 18;
function gamedraw() {
setTimeout(function() {
requestAnimationFrame(gamedraw);
game_update(); //must handle time difference internally
}, interval);
}
gamedraw();
Note that the game_update() function must keep track of when it was last called in order to e.g. move everything twice as far as normal in case a frame had to be skipped.
Actually, this means you could (and probably should) refactor your game_update() function to take the time that has actually passed as an argument instead of determining that internally. (There's no functional difference, it just is better, clearer code IMO because it doesn't hide the timing magic.)
var time;
function gamedraw() {
requestAnimationFrame(gamedraw);
var now = new Date().getTime(),
dt = now - (time || now);
time = now; //reset the timer
game_update(dt); //update with explicit and variable time step
}
gamedraw();
(Here I dropped the explicit frames again.)
Still, I urge you to read the first article because it also deals with cross-browser issues that I haven't gotten into here.
You should use requestAnimationFrame. It will queue up a callback to run on the next time the browser renders a frame. To achieve constant updating, call the update function recursively.
var update = function(){
//Do stuff
requestAnimationFrame(update)
}

call function on the minute every minute

The code I wrote to call a function on the minute every minute, I think is flawed, as It's good for a while, but tends to lag behind by about 15 seconds for every hour since the page was loaded. To be honest I can't figure out what's causing the lagging, maybe it's the time it takes the functions to execute, small lapses all adding up and accumulating. Is there a way to auto-correct the lapses within the function as it's called. Or maybe someone knows a better method of achieving on the minute function calls. Any help or ideas much appreciated. Thanks.
var now = new Date();
var delay = 60 * 1000; // 1 min in msec
var start = delay - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function setTimer() {
onTheMinFunc();
setTimeout(setTimer, delay);
}, start);
First of all, the DOM Timers API does not guarantee accuracy. I quote:
This API does not guarantee that timers will run exactly on schedule. Delays due to CPU load, other tasks, etc, are to be expected.
Second, you have a lag on each round caused by the time onTheMinFunc() is executed (you only set the timeout when it's done).
So, let's say onTheMinFunc takes half a second to execute - you get half a second delay at each minute and it accumulates - after only 10 minutes it'll lag quite a bit. (Note, functions should usually not take more than 15ms to execute anyway to avoid noticeable lag)
Try:
setInterval(onTheMinFunc, delay);
It still won't be very accurate. You can poll on much shorter intervals and keep track of a date variable - but again - no guarantees.
What you probably want is setInterval:
setInterval(onTheMinFunc, delay);
As is, your code using setTimeout means that the time it takes to execute your onTheMinFunc is being added into your delay before the next one is started, so over time, this extra delay will add up.
Using setInterval will be more accurate, since the delay is between calls to execute the function, rather than starting the timer only after the function is finished.
Timers and javascript times aren't very accurate, and I would think the only way to make sure a function is executed every whole minute over time, is to check the seconds every second
setInterval(function() {
if ( new Date().getSeconds() === 0 ) onTheMinFunc();
},1000);
FIDDLE
Here is a slight modification to your code:
function everyMinute(fn) {
arguments[1] && fn();
var now = new Date();
var delay = 60 * 1000 - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function(){
everyMinute(fn, true);
}, start);
}
everyMinute(onTheMinFunc);
It recalculates the number of milliseconds to wait till the next minute every time so it is as accurate as possible to the top of the minute.
I think you want something closer to this:
function setNextMinute() {
// figure out how much time remains before the end of the current minute
var d = new Date().getTime()%60000;
//set a timeout to occur when that expires.
setTimeout(function () {
// recalculate a new timeout so that your timer doesn't lag over time.
doWhateverYouWantToHere();
// note that calling doWhateverYouWantToHere() will
// not offset the next minute, since it is recalculated in setNextMinute()
setNextMinute();
},60000-d);
}
setNextMinute();
caveat: I did not thoroughly test this for timing. But it appeared to work for 1 sec intervals and 1 min intervals well enough.
This has the advantage of not recalculating every second, and also not just starting a 60 second timer from whatever the current time is.
The current accepted answer may overkill
Executing if ( new Date().getSeconds() === 0 ) onTheMinFunc(); on each second (and forever) seems to not be a good idea.
I will not benchmark it against the following propositions, it's not necessary.
Clues
Use whatever logic is necessary to calculate the start moment.
On the start moment
Use setInterval for remaning executions
Execute the first call
Note setInterval is called ASAP to avoid that time lapses.
If you want that new Date().getSeconds() === 0:
var id = setInterval(function() {
if ( new Date().getSeconds() === 0 ) {
setInterval(onTheMinFunc, delay);
onTheMinFunc();
clearInterval(id);
}
},1000);
Alternatively, you could use your own logic:
var now = new Date();
var delay = 60 * 1000; // 1 min in msec
var start = delay - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function() {
setInterval(onTheMinFunc, delay);
onTheMinFunc();
}, start);
Please check both examples working on jsfiddle
The second (Example B) seems more accurate.

JavaScript Duration of animation isn't exact

First of all I want to mention two things,
One: My code isn't perfect (esspechially the eval parts) - but I wanted to try something for my self, and see if I could duplicate the jQuery Animation function, so please forgive my "bad" practices, and please don't suggest that I'll use jQuery, I wanted to experiment.
Two: This code isn't done yet, and I just wanted to figure out what makes it work badly.
So the animation runs for about 12 seconds while the duration parameter I entered was 15 seconds, What am I doing wrong?
function animate(elem, attr, duration){
if(attr.constructor === Object){//check for object literal
var i = 0;
var cssProp = [];
var cssValue = [];
for(key in attr) {
cssProp[i] = key;
cssValue[i] = attr[key];
}
var fps = (1000 / 60);
var t = setInterval(function(){
for(var j=0;j<cssProp.length;j++){
if(document.getElementById(elem).style[cssProp[j]].length == 0){
//asign basic value in css if the object dosn't have one.
document.getElementById(elem).style[cssProp[j]]= 0;
}
var c = document.getElementById(elem).style[cssProp[j]];
//console.log(str +" | "+c+"|"+cssValue[j]);
if(c > cssValue[j]){
document.getElementById(elem).style[cssProp[j]] -= 1/((duration/fps)*(c-cssValue[j]));
}else if(c < cssValue[j]){
document.getElementById(elem).style[cssProp[j]] += 1/((duration/fps)*(c-cssValue[j]));
}else if(c == cssValue[j]){
window.clearInterval(t);
}
}
},fps);
}
}
animate('hello',{opacity:0},15000);
html:
<p id="hello" style="opacity:1;">Hello World</p>
Note: I guess there is a problem with the
(duration/fps)*(c-cssValue[j])
Part or/and the interval of the setInterval (fps variable).
Thanks in advance.
I'm not gonna try and refactor that and figure it out, cause it's pretty wonky. That said... a few things.
Don't rely on the value you are animating to let you know animation progress
In general your approach is unsound. You are better off keeping track of progress yourself. Also, as a result of your approach your math seems like it's trying too hard, and should be much simpler.
Think of it like this: your animation is complete when the time has elapsed, not when the animated value seems to indicate that it's at the final position.
Don't increment, set
Floating point math is inexact, and repeated addition cumulation like this is going accumulate floating point errors as well. And it's far more readable to make some variables to keep track of progress for you, which you can use in calculations.
animatedValue += changeOnThisFrame // BAD!
animatedValue = valueOnThisFrame // GOOD!
Don't do the positive/negative conditional dance
It turns out that 10 + 10 and 10 - (-10) is really the same thing. Which means you can always add the values, but the rate of change can be negative or positive, and the value will animate in the appropriate direction.
timeouts and intervals aren't exact
Turns out setTimeout(fn, 50) actually means to schedule the fn to be call at least 50ms later. The next JS run loop to execute after those 50ms will run the function, so you can't rely on it to be perfectly accurate.
That said it's usually within a few milliseconds. But 60fps is about 16ms for frame, and that timer may actually fire in a variable amount of time from 16-22ms. So when you do calculations based on frame rate, it's not matching the actual time elapsed closely at all.
Refactor complex math
Deconstructing this line here is gonna be hard.
document.getElementById(elem).style[cssProp[j]] -= 1/((duration/fps)*(c-cssValue[j]));
Why for more complex break it up so you can easily understand what's going on here. refactoring this line alone, I might do this:
var style = document.getElementById(elem).style;
var changeThisFrame = duration/fps;
var someOddCalculatedValue = c-cssValue[j];
style[cssProp[j]] -= 1 / (changeThisFrame * someOddCalculatedValue);
Doing this makes it clearer what each expression in your math means and what it's for. And because you didn't do it here, I had a very hard time wondering why c-cssValue[j] was in there and what it represents.
Simple Example
This is less capable than what you have, but it shows the approach you should be taking. It uses the animation start time to create the perfect value, depending on how complete the animation should be, where it started, and where it's going. It doesn't use the current animated value to determine anything, and is guaranteed to run the full length of the animation.
var anim = function(elem, duration) {
// save when we started for calculating progress
var startedAt = Date.now();
// set animation bounds
var startValue = 10;
var endValue = 200;
// figure out how much change we have over the whole animation
var delta = endValue - startValue;
// Animation function, to run at 60 fps.
var t = setInterval(function(){
// How far are we into the animation, on a scale of 0 to 1.
var progress = (Date.now() - startedAt) / duration;
// If we passed 1, the animation is over so clean up.
if (progress > 1) {
alert('DONE! Elapsed: ' + (Date.now() - startedAt) + 'ms');
clearInterval(t);
}
// Set the real value.
elem.style.top = startValue + (progress * delta) + "px";
}, 1000 / 60);
};
anim(document.getElementById('foo'), 5000);
​
JSFiddle: http://jsfiddle.net/DSRst/
You cannot use setInterval for accurate total timing. Because JS is single threaded and multiple things compete for cycles on the one thread, there is no guarantee that the next interval call will be exactly on time or that N intervals will consume the exact duration of time.
Instead, pretty much all animation routines get the current time and use the system clock to measure time for the total duration. The general algorithm is to get the start time, calculate a desired finish time (starttime + duration). Then, as you've done, calculate the expected step value and number of iterations. Then, upon each step, you recalculate the remaining time left and the remaining step value. In this way, you ensure that the animation always finishes exactly (or nearly exactly) on time and that you always get exactly to the final position. If the animation gets behind the ideal trajectory, then it will self correct and move slightly more for the remaining steps. If it gets ahead for any reason (rounding errors, etc...), it will dial back the step size and likewise arrive at the final position on time.
You may also need to know that browsers don't always support very small timing amounts. Each browser has some sort of minimum time that they will allow for a timer operation. Here's an article on minimum timer levels.
Here's an article on tweening (the process of continually recalculating the step to fit the duration exactly).
I'd also suggest that you look at the code for doing animation in some libraries (jQuery, YUI or any other one you find) as they can all show you how this is done in a very general purpose way, including tweening, easing functions, etc...

Emulate MIDI beat clock in Javascript

The task is to emulate the MIDI player work in js, but just for emulate delays between beats. There is an array with beat starting times in beat clock format, for example [960, 1280, 2200, ...], and the formula I'm using for calculate millisecond time for each beat tick:
var beatTickTime = 60000 / (tempo * 96);
The problem is in very slow tick real time generation. Even if I use 1 second delay, it is still very slow. Here is how it was implemented:
var tickTimer = setInterval(function() {
...
tickCount += 1;
}, beatTickTime); // or just 1 ms
Should I pass some beat ticks doing tickCount += someNumber? Or there is more common way to solve this problem? Also I'm not sure about 96 (PPQ * 4 time) in my formula.
P. S. beat ticks comes from parsed guitar pro file
There's no guarantee that setInterval() will run as fast as you ask it to. Even if the timeout is set to 1, you can't count on the function being called 1,000 times each second.
You'll most likely need to do something like the following:
var startTime = (new Date()).getTime();
setInterval(function() {
var relTime = (new Date()).getTime() - startTime;
// relTime is now the number of ms since the script started
/*
In here, you'll need to see if relTime is large enough to indicate the next
beat has been reached. So that means keeping some sort of external marker
to indicate the most recent beat that has occurred -- when relTime is big
enough to move that marker to the next beat, also run any code that is
necessary to handle that beat.
*/
}, 1);
The loop runs "as fast as it can" but still much more slowly than desired. It measures the current time relative to the start of the script, and determines the difference at each iteration. You could divide that time by the tempo of the song, and you'll have an indicator of where in the song you currently are.

Categories