Animation principle
The so-called animation, is through the movement of some columns to form a moving picture. In the Web page, we can change the elements of the CSS value, to achieve the effect of moving.
The formula to use
Total distance S = Total time T * Speed v that is: v = s/t
Current distance s = s/t * Elapsed time T that is: s = S * (t/t)
That is: current distance = total distance * (time consuming/total)
That is: Animation element start value + (animation element end value-Animation element start value) * (Current time-start time)/(animation takes time) + value format
With these formulas, we can use JavaScript setinterval or settimeout to do a simple animation.
However, to do an animation library, you have to consider a number of other factors. For example, the animation of the same element must be performed sequentially. Animations of different elements can run synchronously.
In this way, you have to use another object to manage these animations. I started with the idea that each element was placed in an array, and several setinterval were used to iterate through the animation functions in the array to execute sequentially.
Animate1 = [{ELEM,FN},{ELEM,FN}];
Animate2 = [{ELEM,FN},{ELEM,FN}];
This can be achieved, the same element animation, is the sequential execution, and the different can be run at the same time. Then there is the problem, that is, if more than 10 elements of animation. The program will open 10 setinterval.
To avoid such a situation, some improvements have been made on the basis of the above. Make, no matter how many animations. is done using a setinterval. The modified structure is as follows.
Copy Code code as follows:
[
[ELEM,[FN,FN,FN,FN]],
[ELEM,[FN,FN,FN,FN]],
[ELEM,[FN,FN,FN,FN]]
]
This way, you can use a setinterval to complete all animations. All you need to do is to loop out the Elem and execute the Elem after the first fn,fn of the following element is executed and the FN is deleted. Invoke the next FN, remove Elem from the large array if the FN is all empty, and clear setinterval if Elem is empty. In this way, logic can make sense.
But one of the key factors in animation is the ease of movement. If there is no easing, then the animation effect looks very rigid. Same. Currently do JS animation to use the easing algorithm is a lot of, roughly divided into two categories.
One is the Flash class, one is the prototype class.
Flash requires four parameters. The difference is,
1. Time of the initial words T
2. Animation's initial value B
3. End value of animation C
4. Time of animation duration D
The following is a uniform motion algorithm for Flash class
Linear:function (t,b,c,d) {return c*t/d + B;}
The other is prototype, which requires only one parameter, which is the ratio of the current time t to the duration D (t/d)
I used the second because of its convenient parameters. Also more suitable for the above animation formula, the following is a prototype class of uniform motion algorithm
Linear:function (t) {return t;}.
After adding the easing, the formula above becomes
Animation element Start value + (animation element end value-Animation element start value) * Easing function (current time-start time)/(animation takes time)) + value format.
So far, the whole animation design is over. It is a reference to some other people's blog, here to express thanks!
Finally, put the detailed code.
Copy Code code as follows:
/**
* Create Time 2012/08/29
* @author Lynx Cat.
* @version 0.77beta.
*/
(function (Win,doc) {
var win = Win | | Window
var doc = doc | | Win.document,
POW = Math.pow,
Sin = Math.sin,
PI = Math.PI,
Back_const = 1.70158;
var easing = {
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 * t:
.5 * (1-(--T) * (t-2));
},
Easeinstrong:function (t) {
return T * t * t;
},
Easeoutstrong:function (t) {
Return 1-(--T) * t * t * t;
},
Easebothstrong:function (t) {
Return (T *= 2) < 1?
.5 * t * t * t:
.5 * (2-(t-= 2) * t * t * 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 (2,10 * (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*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 * 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 * 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;
}
};
/**
* Cornerstone used to return an object that contains a dialog method
* @param elem
* @return {Object}
*/
function Catfx (elem) {
Elem = typeof Elem = = ' String '? Doc.getelementbyid (elem): Elem;
return new FX (Elem);
}
/**
* Internal Cornerstone used to return an object that contains a dialog method
* @param elem
* @return {Object}
*/
Function fx (elem) {
This.elem = Elem;
return this;
}
/**
* The base class contains some basic methods, and invariants
*/
var fxbase = {
Speed: {
slow:600,
FAST:200,
defaults:400
},
Fxattrs: [],
Fxmap:[],
/**
* Returns 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;
}(),
/**
* Returns the CSS value of the object element
* @param color value (temporarily do not support red,pink,blue, etc.)
* @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 the element
* @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 CSS values for elements
* @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;
},
/**
* Fixed user's 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;
},
/**
* Maintenance of SetInterval functions, animation of the start
*/
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 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;
/**
* The core object that is used to generate the animated object.
* @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 an animation function to the queue and start the animation.
*/
Start:function () {
var cores = $.data (This.elem);
Cores.push (This.step ());
$.tick ();
},
/**
* Core method that controls the state 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.target) {
if (typeof this.source[i][' val '] = = = ' number ') {
var val = parsefloat (this.source[i][' Val '] + (this.target[i][' Val ']-this.source[i][' val ') * this.options.easing (t/t his.options.duration)). ToFixed (7));
}else{
var r = parseint (this.source[i][' Val '] [' R '] + (this.target[i][' Val '] [' R ']-this.source[i][' Val '] [' r ']) * This.options.easing (t/this.options.duration));
var g = parseint (this.source[i][' Val '] [' g '] + (this.target[i][' Val '] [' g ']-this.source[i][' Val '] [' g ']) * This.options.easing (t/this.options.duration));
var B = parseint (this.source[i][' Val '] [' B '] + (this.target[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.target) {
if (typeof this.target[i][' val '] = = = ' number ') {
var val = this.target[i][' Val '];
}else{
var val = ' rgb (' + this.target[i][' val '] [' r '] + ', ' + this.target[i][' val '] [' g '] + ', ' + this.target[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.target = _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 ', ' hidden ');
}
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 ') {//Most of the time the user's third parameter is a 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 method
* Use Method Catjs (' Your Element ID '). Stop ();
*/
Stop:function () {
$.removedata (This.elem);
}
}
Win.catfx = Catfx;
}) (this,document);
It is also simpler to use. Direct Catfx (' ID '). Animate ({' Margin-left ': MB, ' background-color ': ' #ff0000 '},600, ' easeout ', function () {});
As with jquery, if you don't pass the second argument, the default is 400 milliseconds. No third argument is the default uniform. The third parameter is a function, and there are only three parameters in total. The third parameter is a callback.
Example: Catfx (' ID '). Animate ({' Margin-left ': MB, ' background-color ': ' #ff0000 '},600,function () {alert (' Batting practice is callback function ~ ')});