Efficient ways to do a timer which updates every second in JavaScript - javascript

I had a task to make a progress bar and a process duration timer along with it. So, not thinking twice I did this:
<div class="mainInfo">
<div id="timer"><span id="elapsedText">0:00</span>/<span id="durationText">3:00</span></div>
<div id="progressBar"><div id="progress" style="width:0;"></div></div>
</div>​
And the JS:
var time = 1000;
var duration = 180;
var $progress = $("#progress");
var $elapsedText = $("#elapsedText");
updateTime();
function updateTime() {
var elapsed = time / 1000;
$elapsedText.text(Math.floor(elapsed / 60) + ":" + Math.floor(elapsed % 60));
$progress.css('width', (elapsed * 100 / duration) + "%");
time = time + 1000;
setTimeout("updateTime()", 1000);
}​
Time is actually retrieved from another variable - this ones for the demo (to illustrate that I actually have the value in miliseconds).
And it worked (not only on my PC), and still does, but the procmon shows a CPU spike on browser (chrome, ff) process when this cycle is running - 30-40% instead of regular 0,5%.
Is there a more efficient way to do this?

There is a standard function for that: SetInterval(function, delay_in_ms).
It calls a function in millisecond intervals.

Instead of
setTimeout("updateTime()", 1000);
use
setTimeout(updateTime, 1000);
The fact that you're invoking the compiler each second could really hurt performance. Passing a string to setTimeout is basically causing an eval within the setTimeout.

There is a default for that, and that is setInterval.
Be careful, the function passed as the first argument to setInterval is always executed in global scope.
Number two, a progress bar is usually created along-side expensive processes. You are using it for display purposes only and forcing a delay, which I don't particularly find useful, but if you like the layout, I guess you can go for it.
The way you would usually use it is:
executeFirstPotentiallyExpensiveProcess();// this is a call to a big function.
// then update the value of the progress bar in percentage style.
executeSecondPotentiallyExpensiveFunction()// this is the second part of your process.
// then again update..
// repeat until you have 100%.
// Basically, you logically divide the expenses of your various executions
// into numerable bits, preferably equal to one another for your convenience,
// but you chunk your loading process or whatever type of process and increment
// the progress after each chunk is complete.

Your use of jQuery disturbs me...
var time = 1000;
var duration = 180;
var $progress = document.getElementById("progress");
var $elapsedText = document.getElementById("elapsedText");
var beginTimestamp = new Date().getTime();
updateTime();
setInterval(updateTime,1000);
function updateTime() {
var now = new Date().getTime();
var elapsed = now-beginTimeStamp + time;
$elapsedText.firstChild.nodeValue = Math.floor(elapsed / 60) + ":" + Math.floor(elapsed % 60);
$progress.style.width = (elapsed * 100 / duration) + "%";
}​
Maybe without jQuery your browser might run better ;)

Try with the function setInterval.

Related

Variable inside setInterval(function, var) is not changing / updating with increment

I am building a browser pet raising game. My plan is to use if/else to determine what the HP drop rate should be for the pet. If below a certain hunger / happiness, the HP will drop faster and faster.
I am using a variable setInterval, and the value inside is not update, but the value is updating when printing to the console.
let hp = 100;
let dropHPWeight = 6000;
let dropHPCall = setInterval(dropHP, dropHPWeight);
function dropHP() {
hp = (hp % 360) - 1;
console.log(dropHPWeight);
dropHPWeight = dropHPWeight + 6000;
}
Here I am dropping the HP every 6 seconds, and then as a test, I am seeing if it will increase, but it does not.
Where am I going wrong here?
As some are mentioning in the comments, you cannot change the interval of a setInterval once it has been initiated. You are better off using setTimeout. Like this:
let hp = 100;
let dropHPWait = 6000;
function dropHP() {
hp = (hp % 360) - 1;
dropHPWeight = dropHPWait + 6000;
if (somecondition){
setTimeout(dropHP, dropHPWait)
}
}
dropHP()
Now you call dropHP, which changes the interval, and then uses setTimeout to call itself again after the given increment. I stuck the setTimeout inside some conditional, because you don't want it to run forever (so you have to decide at what point you want it to stop running).

How can I count form fields with the same id?

What I want should be very simple I think, but I end up with too complex situations if I search here, or on Google.
<script language="javascript">
// putten tellen
$(document).ready(function () {
$("input[type='number']").keyup(function () {
$.fn.myFunction();
});
$.fn.myFunction = function () {
var minute_value = $("#minute").val();
var second_value = $("#second").val();
if ((minute_value != '')) {
var productiesec_value = (1 / (parseInt(minute_value) * 60 + parseInt(second_value)));
var productiemin_value = productiesec_value * 60;
var productieuur_value = productiesec_value * 3600;
var productiedag_value = productiesec_value * 86400;
var productieweek_value = productiesec_value * 604800;
var productiemaand_value = productiesec_value * 2629700;
var productiejaar_value = productiesec_value * 31556952;
productiesec_value = (productiesec_value).toFixed(5);
productiemin_value = (productiemin_value).toFixed(2);
productieuur_value = (productieuur_value).toFixed(2);
productiedag_value = (productiedag_value).toFixed(0);
productieweek_value = (productieweek_value).toFixed(0);
productiemaand_value = (productiemaand_value).toFixed(0);
productiejaar_value = (productiejaar_value).toFixed(0);
$("#productiesec").val(productiesec_value.toString());
$("#productiemin").val(productiemin_value.toString());
$("#productieuur").val(productieuur_value.toString());
$("#productiedag").val(productiedag_value.toString());
$("#productieweek").val(productieweek_value.toString());
$("#productiemaand").val(productiemaand_value.toString());
$("#productiejaar").val(productiejaar_value.toString());
}
};
});
</script>
The thing I'd like to accomplish is:
Calculate the production time of a gem in multi-types of time (seconds, minutes, hours etc.) - (Done)
Calculate the production of gems by multiple pits.
Preview: http://hielke.net/projecten/productie/edelsteenput.htm
The idea is that you fill in the minutes in the first field and the seconds in the second field. Then the script should count the production in seconds, minutes, hours etc. on the right side.
After that it must be possible to fill in the second row of minutes and seconds and then counts the total production time. The same for the rest of the rows.
Welcome to SO!
A caveat about your setup: whenever possible, avoid having elements share IDs on your page. IDs are generally for elements which only occur once on your page; otherwise use a class. This practice is why document.getElementById() returns a single element, while document.getElementsByClassName() returns an array, which makes the answer to your question as easy as getting that array's .length.
This being said -- counting the number of elements with the same ID in Javascript is generally considered invalid, as getElementById() will only return one element, and (as far as I know) there isn't a way to iterate over instances of the same ID on a page.
Try changing those IDs to class names if you can, the run a document.getElementsByClassName().length on them to get the count.

How can one force the browser to redraw an image?

I'm working on a JavaScript game that involves throwing a snowball. I need the snowball to render as often as possible during its flight path. Chrome does all the calculations, including setting the style.left and style.top properties, but doesn't actually redraw the snowball until it reaches its destination. Opera doesn't have this problem.
A relevant point is that putting in an alert() after renderSnowball() fixes the problem, except using the alert() is an obvious issue.
Here's my code so far:
function throwSnowball()
{
var theta = parseFloat(angleField.value) * Math.PI/180 ;
var Vir = parseFloat(velocityField.value) ;
if (!isNaN(Vir) && !isNaN(theta) )
{
Vix = Math.cos(theta) * Vir * 50;
Viy = Math.sin(theta) * Vir * 50;
time = new Date() ;
var timeThrown = time.getTime() ;
while (snowballPosY > 0)
{
current = new Date() ;
var currentTime = current.getTime() ;
var timeElapsed = (currentTime - timeThrown)/5000 ;
snowballPosX += Vix * timeElapsed;
snowballPosY += Viy * timeElapsed;
Viy -= GRAVITY * timeElapsed ;
renderSnowball() ; //renderSnowball() sets the style.left
// and style.top properties to snowballPosX pixels
// and snowballPosY pixels respectively
timeThrown = currentTime ;
}
snowballPosX = 0 ;
snowballPosY = 50 ;
renderSnowball() ;
}
}
You're totally blocking the main thread. Have you tried using a setTimeout (even with a zero timeout) to allow other things to happen during your animation?
If you're willing to use experimental technology, requestAnimationFrame would be even better.
Edit: the setTimeout approach would look something like this (replacing the while loop):
var drawAndWait = function() {
if (snowballPosY > 0) {
// movement/drawing code here
setTimeout(drawAndWait, 20 /* milliseconds */);
} else {
// reset code that would normally go after your while loop
}
};
drawAndWait();
So each time the drawing finishes, it arranges for itself to be invoked again, if appropriate. Note that your throwSnowball function will return quickly; the throwing isn't actually done until later on. This takes awhile to get used to doing correctly; don't be too concerned if it's not intuitive at first.
Try getting out of the tight loop. Chrome may not want to redraw until your function exits. Try using setInterval or setTimeout to give Chrome a chance to repaint.

Which is better between setTimeout and setInterval?

The following code is from 'JavaScript by Example Second Edition',I think the code below is better
function scroller() {
str = str.substring(1, str.length) + str.substring(0, 1);
document.title = str;
window.status = str;
}
setInterval(scroller, 300);
The old code is recursive and will continue to call itself every 0.3 seconds until the program ends, I think the old code maybe cause stack overflow, right?
<html>
<!-- This script is a modification of a free script found at
the JavaScript source.
Author: Asif Nasir (Asifnasir#yahoo.com)
-->
<head>
<script type="text/javascript">
var today = new Date();
var year = today.getFullYear();
var future = new Date("December 25, " + year);
var diff = future.getTime() - today.getTime();
// Number of milliseconds
var days = Math.floor(diff / (1000 * 60 * 60 * 24));
// Convert to days
var str =
"Only " + days + " shopping days left until Christmas!";
function scroller() {
str = str.substring(1, str.length) + str.substring(0, 1);
document.title = str;
window.status = str;
setTimeout(scroller, 300); // Set the timer
}
</script>
</head>
<body onload="scroller()">
<b>
<font color="green" size="4">
Get Dizzy. Watch the title bar and the status bar!!
<br />
<image src="christmasscene.bmp">
</font>
</body>
</html>
setInterval is good if you don't care too much about accuracy, e.g. polling for some condition to be met.
setTimeout is good if you want a one–off event or need to adjust the interval between calls, e.g. a clock that should update as close as possible to just after the next whole second.
Both can be used for events that run continuously at approximately the specified interval, both can be cancelled, both only run at about (as soon as possible after) the designated time interval.
Incidentally, the first code example in the OP should not cause a stack overflow, though it is otherwise not very well written.
Have a look here:
'setInterval' vs 'setTimeout'
setTimeout runs the code/function once after the timeout.
setInterval runs the code/function in intervals, with the length of
the timeout between them.
For what you're doing, you should be using setInterval.

How to make a real Javascript timer

I'm looking for a way to manipulate animation without using libraries
and as usual I make a setTimeout in another setTimout in order to smooth the UI
but I want to make a more accurate function to do it, so if I want to make a 50ms-per-piece
animation, and I type:
............
sum=0,
copy=(new Date()).getMilliseconds()
function change(){
var curTime=(new Date()).getMilliseconds(),
diff=(1000+(curTime-copy))%1000 //caculate the time between each setTimeout
console.log("diff time spam: ",diff)
sum+=diff
copy=curTime
var cur=parseInt(p.style.width)
if (sum<47){//ignore small error
//if time sum is less than 47,since we want a 50ms-per animation
// we wait to count the sum to more than the number
console.log("still wating: ",sum)
}
else{
//here the sum is bigger what we want,so make the UI change
console.log("------------runing: ",sum)
sum=0 //reset the sum to caculate the next diff
if(cur < 100)
{
p.style.width=++cur+"px"
}
else{
clearInterval(temp)
}
}
}
var temp=setInterval(change,10)
I don't know the core thought of my code is right,anyone get some ideas about how to make a more accurate timer in most browser?
Set the JsFiddle url:
http://jsfiddle.net/lanston/Vzdau/1/
Looks too complicated to me, use setInterval and one start date, like:
var start = +new Date();
var frame = -1;
var timer = setInterval(checkIfNewFrame, 20);
function checkIfNewFrame () {
var diff = +new Date() - start;
var f = Math.floor(diff / 50);
if (f > frame) {
// use one of these, depending on whether skip or animate lost frames
++frame; // in case you do not skip
frame = f; // in case you do skip
moveAnimation();
}
}
function moveAnimation () {
... do whatever you want, there is new frame, clear timer past last one
}

Categories