1

What I'm after:

There are 5 counters on my page, they should all start from 0 and count upwards (increment) at the same time to different values that are set at different speeds. So for example I set 10, 25, 1500, 400, 500 in my variables I would like them all to reach these values.

• start at the same time • all increment at different speeds • stop at value set for each

My Problem

I can't figure out a good way to write some simple very efficient javascript to do this (going to be used on a mobile), currently I'm using setinterval which seems to start all the counters at different times and jerks/not smooth..

how far I've got so far http://jsfiddle.net/95smH/

setInterval(f1,500);
setInterval(f2,1500);
setInterval(f3,500);
setInterval(f4,50);
var v1 = 0
, v2 = 0
, v3 = 0
, v4 = 0;
function f1() {
 $('.count.v1').text(v1);
 if ( v1 < 450 )
 {
 v1 ++;
 }
}
function f2() {
 $('.count.v2').text(v2);
 if ( v2 < 50 )
 {
 v2 ++; 
 }
}
function f3() {
 $('.count.v3').text(v3);
 if ( v3 < 23 )
 {
 v3 ++; 
 }
 }
function f4() {
 $('.count.v4').text(v4);
 if ( v4 < 4 )
 {
 v4 ++; 
 }
}
asked Sep 8, 2013 at 18:36
1
  • One suggestion: Calculate the value first and then set it. Currently you're doing the other way round. Not sure if this is how your application logic works! Commented Sep 8, 2013 at 18:40

4 Answers 4

1

This would be my shot at a slightly more general way. It will also kill the setInterval once every counter has finished. Of course this will still have the typical inaccuracy of Javascript intervals.

var counter = new (function () {
 'use strict';
 var counters = [],
 handler = null,
 precision = 50;
 this.addCounter = function (maxValue, interval, selector) {
 counters.push({
 maxValue: maxValue,
 currentValue: 0,
 interval: interval / precision,
 intervalCounter: 0,
 $output: $(selector)
 });
 return this;
 };
 this.start = function () {
 handler = setInterval(function () {
 var stillRunning = false;
 for (var i = 0; i < counters.length; i++) {
 var counter = counters[i];
 counter.intervalCounter++;
 if (counter.intervalCounter === counter.interval) {
 counter.intervalCounter = 0;
 counter.currentValue++;
 if (counter.currentValue <= counter.maxValue) {
 stillRunning = true;
 counter.$output.val(counter.currentValue);
 }
 } else {
 stillRunning = true;
 }
 }
 if (!stillRunning) {
 clearInterval(handler);
 }
 }, precision);
 };
 return this;
})();
counter
 .addCounter(50, 250, '.v1')
 .addCounter(25, 500, '.v2')
 .start();

Demo fiddle: http://jsfiddle.net/s9AYz/1/ Note that precision must be set such that the interval of each counter is a multiple of it (Boris solution has the same requirement).

answered Sep 8, 2013 at 19:18
Sign up to request clarification or add additional context in comments.

Comments

1

This is based on your own code style:

setInterval(tick,50);
var cusers = 0
, cawardshows = 0
, ccountries = 0
, cregions = 0;
var susers = 500
, sawardshows = 1500
, scountries = 500
, sregions = 50;
var i = 0;
function tick() { 
 $('.count.users').text(cusers);
 if ( cusers < 450 && i % susers == 0)
 {
 cusers ++;
 }
 $('.count.awardshows').text(cawardshows);
 if ( cawardshows < 50 && i % sawardshows == 0 )
 {
 cawardshows ++; 
 }
 $('.count.countries').text(ccountries);
 if ( ccountries < 23 && i % scountries == 0 )
 {
 ccountries ++; 
 }
 $('.count.regions').text(cregions);
 if ( cregions < 4 && i % sregions == 0)
 {
 cregions ++; 
 }
 i += 50;
}

The idea is to have only one timer, so all updates will be synchronized to the same clock. Since values have different "speeds", you update some of them only every nth step.

answered Sep 8, 2013 at 18:49

3 Comments

Yes this is the same solution I was also about to post. Perhaps just add the updating of text in the if condition to make it more smooth on less powerful devices.
@RobSchmuecker This can be written much differently, I purposely kept the original style so that the difference would be more visible.
Yep I quite agree - it was more of an addition to your answer to make it smoother for the OP rather than a critique of your answer ;)
1

Here is a solution quite cpu-friendly :
- you only have one setInterval using a base period that you decide.
- On each interval you iterate through your all counters doing very little work each time. (i even avoided to use % ).
- The elements are cached.

jsbin is here : http://jsbin.com/urUxojo/2/edit?html,js,console,output

window.onload=function() {
// this is the period of the base Interval in ms. 
// The counters will be aligned on this base period
Counter.basePeriod = 50 ;
// constructor of a Counter
// ! initializes the counter display.
function Counter(id, period_ms, max, initialValue) {
 this.element = document.getElementById(id);
 this.period = Math.ceil(period_ms / Counter.basePeriod) || 1;
 this.max = max ;
 this.value = initialValue || 0 ;
 this.last = 0;
 this.element.value = (this.value);
 return this;
};
// called on each basePeriod tick
// updates the element every period until it reaches its max value.
Counter.prototype.tick = function() {
 this.last++;
 if (this.last == this.period && 
 this.value != this.max ) {
 this.value++;
 this.element.value = (this.value);
 this.last=0;
 } 
};
Counter.counters = []; 
Counter.start= function () { 
 function handleCounters() {
 for (var i=0; i< Counter.counters.length; i++) { Counter.counters[i].tick(); }
 }
 Counter._interval = setInterval(handleCounters, Counter.basePeriod); 
};
Counter.add = function() {
 var newCounter = Object.create(Counter.prototype);
 Counter.counters.push( Counter.apply(newCounter, arguments) );
}
// -------------------------------
Counter.add('cnt1' , 500 , 450 );
Counter.add('cnt2' , 400 , 40 );
Counter.add('topCounter', 1000 , 5 );
Counter.start();
}
answered Sep 8, 2013 at 19:26

3 Comments

Same idea(s) as me, just a little too late. Sorry ;)
Don't be : my implementation is better. (just kidding :-)- )
This is really cool, thank you so much but I get "illegal constructor" in the console from this script?
0

Well, you always want to get rid of duplicity, so you can do:

function IntervalClass(selector, sleepTime, maxValue, startValue) {
 var curValue = startValue;
 this.tick = function () {
 if (curValue < maxValue) {
 curValue++;
 }
 $(selector).html(curValue);
 };
 this.start = function () {
 setInterval(this.tick, sleepTime);
 };
}
new IntervalClass(".count.users", 1000, 35, 0).start();
new IntervalClass(".count.awardshows", 500, 20, 0).start();
new IntervalClass(".count.countries", 1500, 15, 0).start();
new IntervalClass(".count.regions", 200, 100, 0).start();

JSFIDDLE

answered Sep 8, 2013 at 20:08

1 Comment

I'm afraid this script lets all the counters start at different times

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.