Animation Principle
The so-called animation is an animation formed by the movement of some columns. On the webpage, We can continuously change the css value of elements to achieve the dynamic effect.
Formula Used
Total distance S = total time T * speed V: V = S/T
Current distance s = S/T * elapsed time t that is: s = S * (t/T)
That is, the current distance = total distance * (consumed time/total time)
That is, the animation element start value + (animation element end value-animation element start value) * (current time-Start Time)/(time required for the animation) + value format
With the above formula, we can use setInterval or setTimeout of javascript to make a simple animation.
However, to create an animation library, you have to consider other factors. For example, an animation of the same element must be executed sequentially. Animations of different elements can be run simultaneously.
In this way, you must use another object to manage these animations. I started with the idea that every element is placed in an array, and several setInterval functions are used to cyclically retrieve the animation functions in the array for execution in sequence.
Animate1 = [{elem, fn}, {elem, fn}];
Animate2 = [{elem, fn}, {elem, fn}];
In this way, the same element animation can be executed sequentially, while different animations can run simultaneously. Then there is a problem, that is, if there are more than 10 elements of the animation. The program requires ten setInterval.
In order to avoid this situation, we have made some improvements on the above. So that no matter how many animations. All use a setInterval. The modified structure is as follows.
Copy codeThe Code is as follows :[
[Elem, [fn, fn],
[Elem, [fn, fn],
[Elem, [fn, fn]
]
In this way, you can use setInterval to complete all animations. All you need to do is to cyclically retrieve elem and execute the first fn of an element after elem. After fn is executed, the fn is deleted. Call the next fn. If fn is empty, elem is deleted from the large array. If elem is empty, setInterval is clear. In this way, the logic will work.
However, the most important factor in animation is easing. If there is no easing, the animation effect looks very rigid. Same. Currently, many slow motion algorithms are used for js animation, which are roughly divided into two types.
One is the flash class and the other is the prototype class.
Flash requires four parameters. They are,
1. Time t of the first time
2. animation Initial Value B
3. animation end value c
4. animation duration d
The following is a uniform motion Algorithm for flash.
Linear: function (t, B, c, d) {return c * t/d + B ;}
The other is prototype. Only one parameter of this type is required, that is, the ratio of the current time t to the duration d (t/d)
I used the second method because of its convenient parameters. It is also more suitable for the above animation formula. Below is a prototype constant speed motion algorithm.
Linear: function (t) {return t ;}.
The above formula changes
Animation element start value + (animation element end value-animation element start value) * easing function (current time-Start Time)/(animation takes time) + value format.
This is the end of the animation design. I have referenced some other blogs. Thank you!
Finally, paste the detailed code.Copy codeThe Code is as follows :/**
* Create time 2012/08/29
* @ Author lynx cat.
* @ Version 0.77beta.
*/
(Function (win, doc ){
Var win = win | window;
Var doc = doc | win.doc ument,
Pow = Math. pow,
Sin = Math. sin,
PI = Math. PI,
BACK_CONST = 1.70158;
Var Easing = {
// Constant motion
Linear: function (t ){
Return t;
},
EaseIn: function (t ){
Return t * t;
},
EaseOut: function (t ){
Return (2-t) * t;
},
EaseBoth: function (t ){
Return (t * = 2) <1?
. 5 * t:
. 5 * (1-(-- t) * (t-2 ));
},
EaseInStrong: function (t ){
Return t * t;
},
EaseOutStrong: function (t ){
Return 1-(-- t) * t;
},
EaseBothStrong: function (t ){
Return (t * = 2) <1?
. 5 * t:
. 5 * (2-(t-= 2) * t );
},
EaseOutQuart: function (t ){
Return-(pow (t-1), 4)-1)
},
EaseInOutExpo: function (t ){
If (t = 0) return 0;
If (t = 1) return 1;
If (t/= 0.5) <1) return 0.5 * pow (* (t-1 ));
Return 0.5 * (-pow (2,-10 * -- t) + 2 );
},
EaseOutExpo: function (t ){
Return (t = 1 )? 1:-pow (2,-10 * t) + 1;
},
SwingFrom: function (t ){
Return t * (BACK_CONST + 1) * t-BACK_CONST );
},
SwingTo: function (t ){
Return (t-= 1) * t * (BACK_CONST + 1) * t + BACK_CONST) + 1;
},
Sinusoidal: function (t ){
Return (-Math. cos (t * PI)/2) + 0.5;
},
Flicker: function (t ){
Var t = t + (Math. random ()-0.5)/5;
Return this. sinusoidal (t <0? 0: t> 1? 1: t );
},
BackIn: function (t ){
If (t = 1) t-=. 001;
Return t * (BACK_CONST + 1) * t-BACK_CONST );
},
BackOut: function (t ){
Return (t-= 1) * t * (BACK_CONST + 1) * t + BACK_CONST) + 1;
},
Bounce: function (t ){
Var s = 7.5625, r;
If (t <(1/2.75 )){
R = s * t;
}
Else if (t <(2/2.75 )){
R = s * (t-= (1.5/2.75) * t +. 75;
}
Else if (t <(2.5/2.75 )){
R = s * (t-= (2.25/2.75) * t +. 9375;
}
Else {
R = s * (t-= (2.625/2.75) * t +. 984375;
}
Return r;
}
};
/**
* The cornerstone is used to return an object that contains the dialog method.
* @ Param elem
* @ Return {Object}
*/
Function catfx (elem ){
Elem = typeof elem = 'string '? Doc. getElementById (elem): elem;
Return new fx (elem );
}
/**
* The internal cornerstone is used to return an object containing the dialog method.
* @ Param elem
* @ Return {Object}
*/
Function fx (elem ){
This. elem = elem;
Return this;
}
/**
* The basic class contains some basic methods and immutations.
*/
Var fxBase = {
Speed :{
Slow: 600,
Fast: 200,
Defaults: 400
},
FxAttrs: [],
FxMap: [],
/**
* Return the css value of the object element.
* @ Param elem
* @ Param p
* @ Return css value
*/
GetStyle: function (){
Var fn = function (){};
If ('getcomputedstyle' in win ){
Fn = function (elem, p ){
Var p = p. replace (/\-(\ w)/g, function (I, str ){
Return str. toUpperCase ();
});
Var val = getComputedStyle (elem, null) [p];
If (~ (''+ P +''). indexOf ('left right top bottom') & val = 'auto '){
Val = '0px ';
}
Return val;
}
} Else {
Fn = function (elem, p ){
Var p = p. replace (/\-(\ w)/g, function (I, str ){
Return str. toUpperCase ();
});
Var val = elem. currentStyle [p];
If (~ (''+ P +''). indexOf ('width height') & val = 'auto '){
Var rect = elem. getBoundingClientRect ();
Val = (p = 'width '? Rect. right-rect. left: rect. bottom-rect. top) + 'px ';
}
If (p = 'opacity '){
Var filter = elem. currentStyle. filter;
If (/opacity/. test (filter )){
Val = filter. match (/\ d +/) [0]/100;
Val = (val = 1 | val = 0 )? Val. toFixed (0): val. toFixed (1 );
} Else if (val === undefined ){
Val = 1;
}
}
If (~ (''+ P +''). indexOf ('left right top bottom') & val = 'auto '){
Val = '0px ';
}
Return val;
}
}
Return fn;
}(),
/**
* Return the css value of the object element.
* @ Param color value (currently, red, pink, and blue are not supported)
* @ Return rgb (x, x, x)
*/
GetColor: function (val ){
Var r, g, B;
If (/rgb/. test (val )){
Var arr = val. match (/\ d +/g );
R = arr [0];
G = arr [1];
B = arr [2];
} Else if (/#/. test (val )){
Var len = val. length;
If (len = 7 ){
R = parseInt (val. slice (1, 3), 16 );
G = parseInt (val. slice (3, 5), 16 );
B = parseInt (val. slice (5), 16 );
}
Else if (len = 4 ){
R = parseInt (val. charAt (1) + val. charAt (1), 16 );
G = parseInt (val. charAt (2) + val. charAt (2), 16 );
B = parseInt (val. charAt (3) + val. charAt (3), 16 );
}
} Else {
Return val;
}
Return {
R: parseFloat (r ),
G: parseFloat (g ),
B: parseFloat (B)
}
},
/**
* Return the parsed css.
* @ Param prop
* @ Return {val: val, unit: unit}
*/
ParseStyle: function (prop ){
Var val = parseFloat (prop ),
Unit = prop. replace (/^ [\-\ d \.] + /,'');
If (isNaN (val )){
Val = this. getColor (unit );
Unit = '';
}
Return {val: val, unit: unit };
},
/**
* Set the transparency of elements.
* @ Param elem
* @ Param val
*/
SetOpacity: function (elem, val ){
If ('getcomputedstyle' in win ){
Elem. style. opacity = val === 1? '': Val;
} Else {
Elem. style. zoom = 1;
Elem. style. filter = val === 1? '': 'Alpha (opacity = '+ val * 100 + ')';
}
},
/**
* Set the css value of the element.
* @ Param elem
* @ Param prop
* @ Param val
*/
SetStyle: function (elem, prop, val ){
If (prop! = 'Opacity '){
Prop = prop. replace (/\-(\ w)/g, function (I, p ){
Return p. toUpperCase ();
});
Elem. style [prop] = val;
} Else {
This. setOpacity (elem, val );
}
},
/**
* Returns the parsed prop.
* @ Param prop
* @ Return {prop}
*/
ParseProp: function (prop ){
Var props = {};
For (var I in prop ){
Props [I] = this. parseStyle (prop [I]. toString ());
}
Return props;
},
/**
* Modify user Parameters
* @ Param elem
* @ Param duration
* @ Param easing
* @ Param callback
* @ Return {options}
*/
SetOption: function (elem, duration, easing, callback ){
Var options = {};
Var _ this = this;
Options. duration = function (duration ){
If (typeof duration = 'number '){
Return duration;
} Else if (typeof duration = 'string' & _ this. speed [duration]) {
Return _ this. speed [duration];
} Else {
Return _ this. speed. defaults;
}
} (Duration );
Options. easing = function (easing ){
If (typeof easing = 'function '){
Return easing;
} Else if (typeof easing = 'string' & Easing [easing]) {
Return Easing [easing];
} Else {
Return Easing. linear;
}
} (Easing );
Options. callback = function (callback ){
Var _ this = this;
Return function (){
If (typeof callback = 'function '){
Callback. call (elem );
}
}
} (Callback)
Return options;
},
/**
* Maintain the setInterval function and start the animation.
*/
Tick: function (){
Var _ this = this;
If (! _ This. timer ){
_ This. timer = setInterval (function (){
For (var I = 0, len = _ this. fxMap. length; I <len; I ++ ){
Var elem = _ this. fxMap [I] [0];
Var core = _ this. data (elem) [0];
Core (elem );
}
}, 16 );
}
},
/**
* Stop all animations.
*/
Stop: function (){
If (this. timer ){
ClearInterval (this. timer );
This. timer = undefined;
}
},
/**
* Store or take out the queue
* @ Param elem
*/
Data: function (elem ){
For (var I = 0, len = this. fxMap. length; I <len; I ++ ){
Var data = this. fxMap [I];
If (elem = data [0]) {
Return data [1];
}
}
This. fxMap. push ([elem, []);
Return this. fxMap [this. fxMap. length-1] [1];
},
/**
* Delete a queue
* @ Param elem
*/
RemoveData: function (elem ){
For (var I = 0, len = this. fxMap. length; I <len; I ++ ){
Var data = this. fxMap [I];
If (elem = data [0]) {
This. fxMap. splice (I, 1 );
If (this. isDataEmpty ()){
This. stop ();
}
}
}
},
IsDataEmpty: function (){
Return this. fxMap. length = 0;
}
}, $ = FxBase;
/**
* Core object, used to generate animation objects.
* @ Param elem
* @ Param props
* @ Param options
* @ Return {Object}
*/
Function fxCore (elem, props, options ){
This. elem = elem;
This. props = props;
This. options = options;
This. start ();
}
FxCore. prototype = {
Constructor: fxCore,
/**
* Add the animation function to the queue and start the animation.
*/
Start: function (){
Var cores = $. data (this. elem );
Cores. push (this. step ());
$. Tick ();
},
/**
* The core method controls the status of each frame element.
* @ Return function
*/
Step: function (){
Var _ this = this;
Var fn = function (elem ){
Var t = Date. now ()-this. startTime;
If (Date. now () <this. startTime + this. options. duration ){
If (t <= 1) {t = 1 ;}
For (var I in this.tar get ){
If (typeof this. source [I] ['val '] = 'number '){
Var val = parseFloat (this. source [I] ['val '] + (this.tar get [I] ['val']-this. source [I] ['val ']) * this. options. easing (t/this. options. duration )). toFixed (7 ));
} Else {
Var r = parseInt (this. source [I] ['val'] ['R'] + (this.tar get [I] ['val'] ['R']-this. source [I] ['val'] ['R']) * this. options. easing (t/this. options. duration ));
Var g = parseInt (this. source [I] ['val'] ['G'] + (this.tar get [I] ['val'] ['G']-this. source [I] ['val'] ['G']) * this. options. easing (t/this. options. duration ));
Var B = parseInt (this. source [I] ['val'] ['B'] + (this.tar get [I] ['val'] ['B']-this. source [I] ['val'] ['B']) * this. options. easing (t/this. options. duration ));
Var val = 'rgb ('+ r +', '+ g +', '+ B + ')';
}
$. SetStyle (this. elem, I, val + this. source [I] ['unit ']);
}
} Else {
For (var I in this.tar get ){
If (typeof this.tar get [I] ['val '] = 'number '){
Var val = this.tar get [I] ['val'];
} Else {
Var val = 'rgb ('+ this.tar get [I] ['val'] ['R'] + ', '+ this.tar get [I] ['val'] ['G'] +', '+ this.tar get [I] ['val'] [' B '] + ') ';
}
$. SetStyle (elem, I, val + this. source [I] ['unit ']);
}
Var cores = $. data (elem );
Cores. shift ();
This. options. callback ();
If (cores. length = 0 ){
$. SetStyle (elem, 'overflow', this. overflow );
$. RemoveData (elem );
}
}
}
Return function (elem ){
If (! _ This. startTime ){
Var source = {};
_This.tar get = _ this. props;
For (var I in _ this. props ){
Var val = $. getStyle (_ this. elem, I );
Source [I] = $. parseStyle (val );
}
_ This. source = source;
_ This. startTime = Date. now ();
_ This. overflow = $. getStyle (elem, 'overflow ');
$. SetStyle (elem, 'overflow', 'den den ');
}
Fn. call (_ this, elem );
}
}
}
/**
* External interface class.
*/
Fx. prototype = {
Constructor: fx,
/**
* Animation Method
* @ Param prop
* @ Param duration
* @ Param easing
* @ Param callback
* @ Return {Object}
*/
Animate: function (prop, duration, easing, callback ){
If (arguments. length = 3 & typeof easing = 'function') {// in most cases, the third parameter of the user is the callback
Callback = easing;
Easing = undefined;
}
Var props = $. parseProp (prop );
Var options = $. setOption (this. elem, duration, easing, callback );
Var core = new fxCore (this. elem, props, options );
Return this;
},
/**
* Stop Animation
* Usage method catjs ('your element id'). stop ();
*/
Stop: function (){
$. RemoveData (this. elem );
}
}
Win. catfx = catfx;
}) (This, document );
It is easy to use. direct catfx ('id '). animate ({'margin-left': 200, 'background-color': '# ff0000'}, 600, 'easeout', function (){});
It is similar to jquery's usage method. If the second parameter is not input, the default value is 400 milliseconds. If the third parameter is not set, the default speed is constant. The third parameter is a function, and there are only three parameters in total. The third parameter is callback.
Example: catfx ('id '). animate ({'margin-left': 200, 'background-color': '# ff0000'}, 600, function () {alert ('sprinkling home is a callback function ~ ')});