Thursday, October 4, 2007

Javascript Animation : Make things move smoothly (4)

We have seen so far many ways to move a div property : you know how to make its width, height, top, left, opacity change over a chosen time and a number of frames. You also know the basic of easing and how it works, everything starts from one linear equation : y=mx+b.
Thanks to this, we are know able to create bunches of easing effects.
Let's explore them !

Easing by example : the square was the source


Let's start by a little animation that can handle what we have seen so far and some cool easing.
Obviously, they are many things we can improve and we will !



Cool Text
reset
Normal motion
Normal motion high quality
Normal motion low quality
Slow motion
Fast motion
Fade In
Fade Out
SlideIn
SlideOut


Easing equations


I hope you enjoyed this little basic effects because we will see how to do even more soon!
If you remember the last entry about easing, we have seen that everything was in relation with some core elements:
- object to animate
- start
- end - duration

From there we could built some other elements:

- object's property (we shall say properties soon!)
- frame per second or fps
- change in animation being the end-start

All these elements are linked thru one basic liner equation:
property value =(change/duration)*frame + start
or
y=mx+b
Where m is (change/duration) or c/d

We know that this is the shortest path to go from the start to the end as this equation will draw a line but we also know that life is not a straight line but something curved, changing all the time !

We tried to change the equation to something else by keeping the proportions of the equation, which means that if we had somewhere, we must remove somewhere else.

y=m*x*(x/d)+b

This is not a straight line but a curved line and that correspond to a quadratic easing !
If you multiply again by (x/d), it will be quintic...
We could create our own functions and I'm sure you will but let's for now, use the equations from Robert Penner from whom I have learn a lot !
He has spent a lot of time finding some cool equations creating effects, they are all based upon the following components:
- frame (time or t)
- change (c)
- start (begin or b)
- duration (d)

As it's not about reinventing the wheel all the time, we will use them!
I have kept Robert Penner abbreviation as you will certainly find them in many javascript frameworks like mootools, jQuery and the like !
So here we go!
easeInQuad = function (t, b, c, d) {
 return c*(t/=d)*t + b;
};
easeOutQuad = function (t, b, c, d) {
 return -c *(t/=d)*(t-2) + b;
};
easeInOutQuad = function (t, b, c, d) {
 if ((t/=d/2) < 1) return c/2*t*t + b;
 return -c/2 * ((--t)*(t-2) - 1) + b;
};
easeInCubic = function (t, b, c, d) {
 return c*(t/=d)*t*t + b;
};
easeOutCubic = function (t, b, c, d) {
 return c*((t=t/d-1)*t*t + 1) + b;
};
easeInOutCubic = function (t, b, c, d) {
 if ((t/=d/2) < 1) return c/2*t*t*t + b;
 return c/2*((t-=2)*t*t + 2) + b;
};
easeInQuart = function (t, b, c, d) {
 return c*(t/=d)*t*t*t + b;
};
easeOutQuart = function (t, b, c, d) {
 return -c * ((t=t/d-1)*t*t*t - 1) + b;
};
easeInOutQuart = function (t, b, c, d) {
 if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
 return -c/2 * ((t-=2)*t*t*t - 2) + b;
};
easeInQuint = function (t, b, c, d) {
 return c*(t/=d)*t*t*t*t + b;
};
easeOutQuint = function (t, b, c, d) {
 return c*((t=t/d-1)*t*t*t*t + 1) + b;
};
easeInOutQuint = function (t, b, c, d) {
 if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
 return c/2*((t-=2)*t*t*t*t + 2) + b;
};
easeInSine = function (t, b, c, d) {
 return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
};
easeOutSine = function (t, b, c, d) {
 return c * Math.sin(t/d * (Math.PI/2)) + b;
};
easeInOutSine = function (t, b, c, d) {
 return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
};
easeInExpo = function (t, b, c, d) {
 return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
};
easeOutExpo = function (t, b, c, d) {
 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
};
easeInOutExpo = function (t, b, c, d) {
 if (t==0) return b;
 if (t==d) return b+c;
 if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
 return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
};
easeInCirc = function (t, b, c, d) {
 return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
};
easeOutCirc = function (t, b, c, d) {
 return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
};
easeInOutCirc = function (t, b, c, d) {
 if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
 return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
};
//a: amplitude (optional), p: period (optional)
easeInElastic = function (t, b, c, d, a, p) {
 if (t==0) { return b; } 
 if ((t/=d)==1) { return b+c; }
 if (!p) { p=d*.3; }
 if (a < Math.abs(c)) {  a=c; s=p/4; }
 else { a=Math.abs(c); s = p/(2*Math.PI) * Math.asin(c/a);}
 return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
};
easeOutElastic = function (t, b, c, d, a, p) {
 if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
 if (a < Math.abs(c)) { a=c; var s=p/4; }
 else {   a=Math.abs(c); var s = p/(2*Math.PI) * Math.asin (c/a);}
 return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
};
easeInOutElastic = function (t, b, c, d, a, p) {
 if (t==0) return b;  
 if ((t/=d/2)==2) return b+c;  
 if (!p) p=d*(.3*1.5);
 if (a < Math.abs(c)) { a=c; var s=p/4; }
 else {a=Math.abs(c);var s = p/(2*Math.PI) * Math.asin (c/a);}
 if (t < 1) {return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;}
 return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
};
easeInBack = function (t, b, c, d, s) {
 if (s == undefined) s = 1.70158;
 return c*(t/=d)*t*((s+1)*t - s) + b;
};
easeOutBack = function (t, b, c, d, s) {
 if (s == undefined) s = 1.70158;
 return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
};
easeInOutBack = function (t, b, c, d, s) {
 if (s == undefined) s = 1.70158; 
 if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
 return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
};
easeInBounce = function (t, b, c, d) {
 return c - easeOutBounce (d-t, 0, c, d) + b;
};
easeOutBounce = function (t, b, c, d) {
 if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b;} 
        else if (t < (2/2.75)) { return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;} 
        else if (t < (2.5/2.75)) { return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;}
        else { return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; }
};
easeInOutBounce = function (t, b, c, d) {
 if (t < d/2) return easeInBounce (t*2, 0, c, d) * .5 + b;
 return easeOutBounce (t*2-d, 0, c, d) * .5 + c*.5 + b;
};

Wow, this was huge!
Finding evolutions to the animation thru equations might not be as easy but it gives you full control of what you want to do!
Remember that all these functions start from only one equation:
y=mx+b
So don't fear to try yourself!
3D games and much complexed animations are all based upon equations related to physics.
Why not had a friction coefficient ? an hypothetic wind ? some random evolutions too ?

Conclusion


I guess the animation function is starting to be interesting but as always, there are still many drawbacks:
- You can not animate several properties of an element! It's one property at a time.
- We do not cover all the properties possible like colors, borders and a bunch of other css properties!

6 comments:

Davis Peixoto said...

Dude, this blog is one of the most mind-blowing I've ever seen about Javascript programming.

Really. Nice language, excellent articles, fully interesting topics...

Congrats. Dunno how I ignored this blog so long.

shiriru said...

Hi
Thank you for reading and your comment

Unknown said...
This comment has been removed by the author.
Unknown said...
This comment has been removed by the author.
Anonymous said...

Hi

Very good explanation about javascript animation, mainly easy functions.


Do you have a complete js file with this functions?

Thanks

Pil said...

Hmm, this seems to be a good solution for click events.
But if you implement it on mouseover and mouseout to change - for example - the opacity of a tooltip, the result is far away from being smooth because it flickers.

Maybe you can extend your tutorial to explain how to avoid flickering on mouseover and mouseout.