Animate count down/up to specific number - javascript

I currently count down/up to a target number. The small snippet of code I currently use to do this seems to produce inconsistent results. There must be a bullet proof way of doing this without writing pages of code. Any advice would be greatly appreciated.
DEMO https://jsfiddle.net/d6anjubd/
var currentNum = $('p').text();
var targetNum = 0;
$({countNum: parseInt(currentNum)}).animate({countNum: parseInt(targetNum)}, {
duration: 500,
easing:'linear',
step: function() {
$('p').text(Math.floor(this.countNum));
},
complete: function() {
console.log('finished count');
}
});
You will notice that if you press 'run' within the jsfiddle, it will not always end in the target number, in this instance 0.

changing the line easing:'linear' to easing:'swing' makes this work for me.
Guessing there might be a problem with linear easing on such short intervalls as when I increase the duration of the animation (2000+) linear works to.
An added note: The amount of error between target and result also seems to vary with the size of the difference between the two numbers (the bigger X the more error: (ABS(startValue - target) = X). This makes sense as a greater distance means it needs to travel further to get to the target.
It would also be logical for the countdown to take more time to countdown when the distance between target and value is greater. ie: duration: 15 * currentNum
Increasing the allowed time to reach the target with the distance needed to travel makes the calculation work every time for me, no matter the target or number.

Related

Should I use requestAnimationFrame to fade in several elements? To achieve 60fps animation?

Trying to maintain 60 fps animation. Currently, I'm getting a lot of spikes of slow frames that are happening at like 30~ fps and the choppiness is noticeable to my eye.
Significant Edits: Throwing out old obsolete code, adding in new code with explanation
fadeIn: function(ele){
raf = window.requestAnimationFrame(function() {
console.log(ele);
var opacity = 0;
function increase () {
opacity += 0.05;
if (opacity >= 1){
// complete
ele.style.opacity = 1;
return true;
}
ele.style.opacity = opacity;
requestAnimationFrame(increase);
}
increase();
});
},
fadeInElements: function(elements, properties, speed, delay){
var raf;
var ele;
for (i = 0; i < properties.length; i++){
ele = elements[properties[i]];
console.log('outside loop ' + ele);
instance.fadeIn(ele);
}
},
My new code is above. It is successfully:
Iterating through several elements (each as ele) and then calling fadeIn(ele)
So, all elements fade in.
However, I want a 50ms delay between each "fade in" (each triggering of fadeIn() on a new element
The good news is that it's not actually recursion — it's more like a timeout. You provide a function that draws a frame, and the browser calls it for you.
Here's an answer showing excerpts from a complete JSFiddle. It doesn't try to replicate your exact code, but instead tries to explain what you need to know so you can adapt your code accordingly. The code was written to be easy to understand, so I'm sure there are other ways to do it faster!
This answer works from the top level down, which means I describe the end of the JSFiddle source and work my way backwards. I personally think this makes it easier to understand than does starting with the details.
You need to start the animation somewhere. So the very last thing in the JSFiddle is
window.requestAnimationFrame(eachFrame); //start the animation
This will call a function called eachFrame() when it's time for the next frame, e.g,. on the next multiple of 60 times per second. It will only do it once, though.
You need eachFrame() to keep track of where we are in the animation.
var startTime = -1.0; // -1 = a flag for the first frame.
function eachFrame()
{
// Render this frame ------------------------
if(startTime<0) {
// very first frame (because of the -1.0): save the start time.
startTime = (new Date()).getTime();
render(0.0);
// the parameter to render() is the time within the
// animation.
} else {
// every frame after the first: subtract the saved startTime
// to determine the current time.
render( (new Date()).getTime() - startTime );
}
// Now we're done rendering one frame. ------
//Start the timer to call this function again
//when it's time for the next frame.
window.requestAnimationFrame(eachFrame);
}; //eachFrame
eachFrame() determines what the current time is with respect to the beginning of the animation. getTime() gives you the time in milliseconds.
The other thing eachFrame() does is to call window.requestAnimationFrame(eachFrame); again. This isn't recursion. Instead, eachFrame() will finish running, and then after that, the next time a frame comes around, the browser will call eachFrame() again.
The last function you need is something to actually draw the frame! That is render(current time). Assume that, e.g., head1 and head2 refer to two heading elements you want to animate, e.g., <h1> elements declared in your HTML. The clamp(x) function returns x but clamped below at 0 and above at 1.
function render(currTime)
{ // *** Put your rendering code here ***
// How opaque should head1 be? Its fade started at currTime=0.
var opacity1 = clamp(currTime/FADE_DURATION);
// over FADE_DURATION ms, opacity goes from 0 to 1
// How opaque should head2 be?
var opacity2 = clamp( (currTime-FADE_SPACING)/FADE_DURATION );
// fades in, but doesn't start doing it until
// FADE_SPACING ms have passed.
// Apply the changes
head1.style.opacity = opacity1;
head2.style.opacity = opacity2;
} //render
In render(), you figure out opacity based on the current time. You don't have to worry about delaying between frames, because requestAnimationFrame handles that for us. You stagger the transitions by offsetting the time. In this example, opacity1 depends on currTime and opacity2 depends on currTime minus a constant FADE_SPACING, so the opacity change for element 2 will start later than the opacity change for element 1 by FADE_SPACING ms.
The JSFiddle has all the details filled in. It animates the opacity of two <h1> elements, with a spacing between the beginning of the animation for each element. I hope this helps!

How to animate a style property in plain vanilla javascript?

So for a while I've been moving away from jQuery, and in general just reducing my library use where-every possible to a) write leaner code, and b) really understand at a low level whats going on, particularly around the UI. While I've moved the majority of my UI animation to CSS3, theres often times when you need a little more control, but for a single tiny animation I'd prefer not to always have to pull in velocity.js or greensock etc.
Looking at you-might-not-need-jquery theres a fadeIn function they demostate that looks like this :
function fadeIn(el) {
el.style.opacity = 0;
var last = +new Date();
var tick = function() {
el.style.opacity = +el.style.opacity + (new Date() - last) / 400;
last = +new Date();
if (+el.style.opacity < 1) {
(window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16);
}
};
tick();
}
fadeIn(el);
I generally understand this function, but have a few questions on very specific items :
What does the + before new Date() and el.style.opacity on lines 3,5,6 & 8 indicate? is it something like += ?
On line 5, why the division by 400?
Is there anything inherently wrong with this recursive requestAnimationFrame technique for general quick animations?
If I understand the concept behind this pattern :
we set our starting position (force feeding) and time we're beginning,
we then update the style relevant to the amount of time thats passed,
until the final state is satisfied, call tick again, on the next animation frame.
Is this correct?
A unary + is a quick way to force a value to be interpreted as a number.
The division by 400 is the way that code sets the rate of fade-in. A bigger number would make the fade take longer, and a smaller number would make it faster. The number gives the number of milliseconds that will elapse (more or less) before the element is fully opaque.
It's not recursive. The reference to the function is passed to the timer mechanism (either setTimeout() or requestAnimationFrame()) but by the time the timer fires the original call will have exited.

How to get smooth counter animations

I should display an animated counter. Ideally, there should be an continous animation of the number change. The situation looks like the following:
I get every minute a new value.
The counter should show the real value (no fake).
There are times where there is no value change.
Sometimes there counter should spin faster and sometimes slower (depending on the value change).
My idea was to use the last two values and animate between them. Depending on the difference the speed has to change. But I didn't find a counter which allows to change the speed afterwards. Or do you have another idea?
How are all the other websites with a counter doing it? Do they fake?
Edit:
I tried different counter - currently I'm using jOdometer. My current code base is:
var counter = $('.counter').jOdometer({
counterStart: '0',
numbersImage: '/img/jodometer-numbers-24pt.png',
widthNumber: 32,
heightNumber: 54,
spaceNumbers: 0,
offsetRight:-10,
speed:10000,
delayTime: 300,
maxDigits: 10,
});
function update_odometer() {
var jqxhr = $.getJSON("/number.php?jsonp=?")
.done(function(data){
console.log(data);
total = data['Total'];
counter.goToNumber(total);
})
.fail(function(data){
console.log(data);
});
};
update_odometer();
setInterval(update_odometer, 60000);
The counter currently counts from zero to my number, but I want to change this behavior (counting from the next-to-last to the last value - but I have problems with my AJAX call [see here]). It is also possible that I change the library if needed. I looked into jquery.jodometer.js and perhaps I get it working to create a function which changes the speed on the fly.
But the reason why I didn't attached the code was that I search for the programming procedure behind the code. Is it possible to get such a live counter? How does the procedure does look like?
Solution:
Setting the speed solves the problem because the library does the animation for me:
speed:60000
You only have to match the speed of the animation with the update interval.
A simple for loop from current value to value to be updated should solve your problem.
Inside your done function,
for(var i=current_odo_value; i<=data['Total']; i++) {
counter.goToNumber(i);
}
If you can control the speed in your library, this ought to do it. Since the speed is directly proportional to the difference between current value and updated value, that difference can be sent to plugin as the required speed value.
I'm assuming only increase in fetched total value. If it can decrease as well, you need to handle that condition as well.

Update 'duration' of .animate function within the 'onChange'?

I'm trying to improve an animation that I've been working on where things move across the screen.
Currently the object moves at a set speed and has no variance.
I'm trying to include two features that will ultimately end up doing the same thing; changing the speed of the animated object.
I'd like the user to be able to change the speed and also for the object to slow down or speed up depending on where it is on the screen.
I'm not sure if I'm looking in the right place as currently I've been unable to update the duration once the animation loop has started. I first thought I could replace the number with a function that would return an int. This works in that the value of 'speed' changes but the animate loop is not updated.
Any help is hugely appreciated, thanks.
Code snippets below.
function moveObj () {
//initially the duration was set here. I understand that will not work as the animation is only
//being called once.
//animation process
obj.animate('top', '+=' + canvas.height, {
duration: speedOfObj(0),
abort: function () {
},//end abort callback
onChange: function () {
//testing only//
speedOfObj(1000);
}
//test function to see what the results would be. speed changes when called within the on change but the animation is not affected.
function speedOfObj(modifier){
var speed = 10000 / (new Number(speedControl.value));
if(modifier == 0){
console.log("speed: "+speed);
return speed;
}else{
speed *= modifier;
console.log("speedBBBB: "+speed);
return speed;
}
}
Once a jQuery animation is off and running, it's pretty much off on its own. If you want to change how it works, you can .stop(true) it and then start up a new animation that starts again from where it is now at your new speed.
It's also possible to implement a custom step function in the animation that might takes some queues for how to work from outside influences that can change during the animation, but I think that would end being much more complicated than just stopping the original animation and starting a new one that moves at the newly desired speed.
Working demo: http://jsfiddle.net/jfriend00/tzxca/

why jquery can't animate number accurately?

i am trying to use the following code to increment number in a textbox
// Animate the element's value from 0 to 1100000:
$({someValue: 0}).animate({someValue: 1100000}, {
duration: 1000,
step: function() { // called on every step
// Update the element's text with value:
$('#counterx').text(Math.floor(this.someValue+1));
}
});
it is working with small numbers like from 0 to 100
but when it comes to large number like in the mentioned code,
it is not giving the target number,
it is animating to numbers like 1099933 or 1099610 or .....
and every time it changes.
so how can i make it to animate to the number i specify?
I have the same issue. The reasoning is because animate function uses a mathematical formula that is time based. You don't really notice this when animating something css based because close enough in pixels is good enough. It will get close to the final value but may not always be exactly the end value. Solution is to use the complete event to set that last value.
Here is what you need to do:
function animateNumber(ele,no,stepTime){
$({someValue: 0}).animate({someValue: no}, {
duration: stepTime,
step: function() { // called on every step. Update the element's text with value:
ele.text(Math.floor(this.someValue+1));
},
complete : function(){
ele.text(no);
}
});
}
animateNumber($('#counterx'),100,10000);
animateNumber($('#countery'),100,1000)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
counterx(slow): <span id=counterx>--</span>
<br/>
countery(fast): <span id=countery>--</span>
1) Javascript is a single threaded application. Timeouts and animations ONLY push the event to the end of the stack based on an ideal stacking order. A long running section of script can cause the actual firing time of that event well past the accuracy you are looking for.
2) Animation approximates how much to increment, and on larger numbers that resolution is very inaccurate.
3) jQuery only has one animation buffer. You might run into some serious rendering issues if you invoke more than one "counter" using animation. Make sure to stop the previous animation before making any adjustments that effect it.
4) Even with a timeout of 0, you can expect the real world delay of ~15. Even if that is the only "thread" you have running.
Solution:
take a snapshot of the DTG
set your interval to something within the human experience, say ~200
on each interval, check how much time has passed from the original DTG
set your text field to that delta number.
stop the interval with the original DTG + "your target number" > the new DTG
Animate is not designed to increment a counter as text (though it may work by accident, which could change with any new version of jQuery), it's designed to animate one or more CSS properties. You should be using setInterval instead.
http://jsfiddle.net/jbabey/mKa5r/
var num = 0;
var interval = setInterval(function () {
document.getElementById('result').innerHTML = num;
num++;
if (num === 100) {
clearInterval(interval);
}
}, 100);​
Here's a solution that doesn't use .animate().
DEMO: http://jsfiddle.net/czbAy/4/
It's just a linear modification; you don't get the easing options if that's what you were after.
var counterx = $('#counterx'), // cache the DOM selection! :)
i = 0,
n = 1100000,
dur = 1000, // 1 second
int = 13,
s = Math.round(n / (dur / int));
var id = setInterval(function() {
counterx.text(i += s);
if (i >= n) {
clearInterval(id);
counterx.text(n);
}
}, int);
Here is a jquery plugin to animate numbers reliably, ut uses the complete callback to set the correct final number once the animation has finished:
https://github.com/kajic/jquery-animateNumber

Categories

Resources