javascript動(dòng)畫淺析
更新時(shí)間:2012年08月30日 17:17:37 作者:
最近一直在弄手機(jī)端的游戲,接觸到各種動(dòng)畫。加之對(duì)之前的自己那個(gè)動(dòng)畫類不滿意,就有心想寫個(gè)新的。然后翻看各種博客,查資料。也學(xué)到一些新的東西
動(dòng)畫原理
所謂的動(dòng)畫,就是通過(guò)一些列的運(yùn)動(dòng)形成的動(dòng)的畫面。在網(wǎng)頁(yè)中,我們可以通過(guò)不斷的改變?cè)氐腸ss值,來(lái)達(dá)到動(dòng)的效果。
用到的公式
總距離S = 總時(shí)間T * 速度V 即: V = S/T
當(dāng)前距離s = S/T * 已耗時(shí)t 即: s = S * (t/T)
即:當(dāng)前距離 = 總距離 * (已耗時(shí)/總時(shí)間)
即:動(dòng)畫元素開始值 + (動(dòng)畫元素結(jié)束值 - 動(dòng)畫元素開始值) * (當(dāng)前時(shí)間-開始時(shí)間) / (動(dòng)畫需要時(shí)間) + 值的格式
有了上面這些公式,我們就能利用javascript的setInterval或者setTimeout來(lái)做一個(gè)簡(jiǎn)單的動(dòng)畫了。
然而想要做一個(gè)動(dòng)畫庫(kù),就不得不考慮另外一些因素了。 比如同一個(gè)元素的動(dòng)畫,必須要有順序的執(zhí)行。不同元素的動(dòng)畫可以同步運(yùn)行。
如此一來(lái),就必須得用另外一個(gè)對(duì)象來(lái)管理這些動(dòng)畫了。我開始的想法是講每個(gè)元素都放在一個(gè)數(shù)組里,用幾個(gè)setInterval來(lái)循環(huán)取出數(shù)組中的動(dòng)畫函數(shù)依次執(zhí)行。
animate1 = [{elem,fn},{elem,fn}];
animate2 = [{elem,fn},{elem,fn}];
這樣就能達(dá)到,相同的元素動(dòng)畫,是有順序的執(zhí)行,而不同的則可以同時(shí)運(yùn)行了。然后這樣卻存在一個(gè)問(wèn)題,那就是如果超過(guò)10個(gè)元素的動(dòng)畫。程序就要開十個(gè)setInterval。
為了避免這樣的情況發(fā)生,就在上面的基礎(chǔ)上做了一些改進(jìn)。使得,不論多少個(gè)動(dòng)畫。都使用一個(gè)setInterval來(lái)完成。修改后結(jié)構(gòu)如下。
[
[elem,[fn,fn,fn,fn]],
[elem,[fn,fn,fn,fn]],
[elem,[fn,fn,fn,fn]]
]
這樣一來(lái),就可以用一個(gè)setInterval來(lái)完成所有動(dòng)畫了。 所需要做就是,循環(huán)取出elem,并執(zhí)行elem后面一個(gè)元素的頭一個(gè)fn,fn執(zhí)行完畢后刪除fn。調(diào)用下一個(gè)fn,如果fn全部為空則從大的數(shù)組中刪除elem,如果elem為空時(shí),則清楚setInterval。這樣一來(lái),邏輯上便可以走得通了。
然而動(dòng)畫最關(guān)鍵的因素還有一個(gè),那就是緩動(dòng)。 如果沒(méi)有緩動(dòng),那么動(dòng)畫效果看起來(lái)就非常的死板。千篇一律。目前做js動(dòng)畫用到的緩動(dòng)算法是很多的,大致分為兩類。
一種是flash類,一種是prototype類。
flash的需要四個(gè)參數(shù)。分別是,
1.時(shí)間初始話的時(shí)間t
2.動(dòng)畫的初始值b
3.動(dòng)畫的結(jié)束值c
4.動(dòng)畫持續(xù)的時(shí)間d
下面是一個(gè)flash 類的勻速運(yùn)動(dòng)算法
Linear: function(t,b,c,d){ return c*t/d + b; }
另一種則是prototype,這一類的參數(shù)只需要一個(gè),那就是當(dāng)前時(shí)間t與持續(xù)時(shí)間d的比值 (t/d)
我采用了第二種,因?yàn)樗膮?shù)方便。也更加適合上面的動(dòng)畫公式,下面是一個(gè)prototype類的勻速運(yùn)動(dòng)算法
linear : function(t){ return t;}.
加入緩動(dòng)后上面的公式變?yōu)?
動(dòng)畫元素開始值 + (動(dòng)畫元素結(jié)束值 - 動(dòng)畫元素開始值) * 緩動(dòng)函數(shù)((當(dāng)前時(shí)間-開始時(shí)間) / (動(dòng)畫需要時(shí)間)) + 值的格式。
至此便是整個(gè)動(dòng)畫類設(shè)計(jì)便結(jié)束了。其中參考了一些其它人的博客,在此表示感謝!
最后,還是貼一下詳細(xì)代碼吧。
/**
* 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 = {
// 勻速運(yùn)動(dòng)
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 * t;
},
easeOutStrong : function (t) {
return 1 - (--t) * t * t * t;
},
easeBothStrong: function (t) {
return (t *= 2) < 1 ?
.5 * t * 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;
}
};
/**
* 基石 用于返回一個(gè)包含對(duì)話方法的對(duì)象
* @param elem
* @return {Object}
*/
function catfx(elem){
elem = typeof elem === 'string' ? doc.getElementById(elem) : elem;
return new fx(elem);
}
/**
* 內(nèi)部基石 用于返回一個(gè)包含對(duì)話方法的對(duì)象
* @param elem
* @return {Object}
*/
function fx(elem){
this.elem = elem;
return this;
}
/**
* 基礎(chǔ)類 包含一些基礎(chǔ)方法,和不變量
*/
var fxBase = {
speed : {
slow : 600,
fast : 200,
defaults : 400
},
fxAttrs : [],
fxMap:[],
/**
* 返回對(duì)象元素的css值
* @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;
}(),
/**
* 返回對(duì)象元素的css值
* @param 顏色值(暫不支持red,pink,blue等英文)
* @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)
}
},
/**
* 返回解析后的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};
},
/**
* 設(shè)置元素的透明度
* @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 + ')';
}
},
/**
* 設(shè)置元素的css值
* @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);
}
},
/**
* 返回解析后的prop
* @param prop
* @return {prop}
*/
parseProp : function(prop){
var props = {};
for(var i in prop){
props[i] = this.parseStyle(prop[i].toString());
}
return props;
},
/**
* 修正用戶的參數(shù)
* @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;
},
/**
* 維護(hù)setInterval的函數(shù),動(dòng)畫的啟動(dòng)
*/
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);
}
},
/**
* 停止所有動(dòng)畫
*/
stop : function(){
if(this.timer){
clearInterval(this.timer);
this.timer = undefined;
}
},
/**
* 存儲(chǔ)或者拿出隊(duì)列
* @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];
},
/**
* 刪除隊(duì)列
* @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;
/**
* 核心對(duì)象,用于生成動(dòng)畫對(duì)象。
* @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,
/**
* 將動(dòng)畫函數(shù)加入到隊(duì)列中,并啟動(dòng)動(dòng)畫。
*/
start : function(){
var cores = $.data(this.elem);
cores.push(this.step());
$.tick();
},
/**
* 核心方法,控制每一幀元素的狀態(tài)。
* @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 / this.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);
}
}
}
/**
* 外部接口類。
*/
fx.prototype = {
constructor : fx,
/**
* 動(dòng)畫方法
* @param prop
* @param duration
* @param easing
* @param callback
* @return {Object}
*/
animate : function(prop, duration, easing, callback){
if(arguments.length == 3 && typeof easing === 'function'){ //多數(shù)時(shí)候用戶第三個(gè)參數(shù)是回調(diào)
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;
},
/**
* 停止動(dòng)畫方法
* 使用方法 catjs('your element id').stop();
*/
stop : function(){
$.removeData(this.elem);
}
}
win.catfx = catfx;
})(this,document);
使用起來(lái)也比較簡(jiǎn)單.直接catfx('ID').animate({'margin-left':200,'background-color':'#ff0000'},600,'easeOut',function(){});
跟jquery的使用方法差不多,如果不傳第二個(gè)參數(shù),則默認(rèn)為400毫秒。不傳第三個(gè)參數(shù)則默認(rèn)勻速。第三個(gè)參數(shù)為函數(shù),并且總共只有三個(gè)參數(shù)時(shí)。第三個(gè)參數(shù)為回調(diào)。
例:catfx('ID').animate({'margin-left':200,'background-color':'#ff0000'},600,function(){alert('灑家是回調(diào)函數(shù)~')});
所謂的動(dòng)畫,就是通過(guò)一些列的運(yùn)動(dòng)形成的動(dòng)的畫面。在網(wǎng)頁(yè)中,我們可以通過(guò)不斷的改變?cè)氐腸ss值,來(lái)達(dá)到動(dòng)的效果。
用到的公式
總距離S = 總時(shí)間T * 速度V 即: V = S/T
當(dāng)前距離s = S/T * 已耗時(shí)t 即: s = S * (t/T)
即:當(dāng)前距離 = 總距離 * (已耗時(shí)/總時(shí)間)
即:動(dòng)畫元素開始值 + (動(dòng)畫元素結(jié)束值 - 動(dòng)畫元素開始值) * (當(dāng)前時(shí)間-開始時(shí)間) / (動(dòng)畫需要時(shí)間) + 值的格式
有了上面這些公式,我們就能利用javascript的setInterval或者setTimeout來(lái)做一個(gè)簡(jiǎn)單的動(dòng)畫了。
然而想要做一個(gè)動(dòng)畫庫(kù),就不得不考慮另外一些因素了。 比如同一個(gè)元素的動(dòng)畫,必須要有順序的執(zhí)行。不同元素的動(dòng)畫可以同步運(yùn)行。
如此一來(lái),就必須得用另外一個(gè)對(duì)象來(lái)管理這些動(dòng)畫了。我開始的想法是講每個(gè)元素都放在一個(gè)數(shù)組里,用幾個(gè)setInterval來(lái)循環(huán)取出數(shù)組中的動(dòng)畫函數(shù)依次執(zhí)行。
animate1 = [{elem,fn},{elem,fn}];
animate2 = [{elem,fn},{elem,fn}];
這樣就能達(dá)到,相同的元素動(dòng)畫,是有順序的執(zhí)行,而不同的則可以同時(shí)運(yùn)行了。然后這樣卻存在一個(gè)問(wèn)題,那就是如果超過(guò)10個(gè)元素的動(dòng)畫。程序就要開十個(gè)setInterval。
為了避免這樣的情況發(fā)生,就在上面的基礎(chǔ)上做了一些改進(jìn)。使得,不論多少個(gè)動(dòng)畫。都使用一個(gè)setInterval來(lái)完成。修改后結(jié)構(gòu)如下。
復(fù)制代碼 代碼如下:
[
[elem,[fn,fn,fn,fn]],
[elem,[fn,fn,fn,fn]],
[elem,[fn,fn,fn,fn]]
]
這樣一來(lái),就可以用一個(gè)setInterval來(lái)完成所有動(dòng)畫了。 所需要做就是,循環(huán)取出elem,并執(zhí)行elem后面一個(gè)元素的頭一個(gè)fn,fn執(zhí)行完畢后刪除fn。調(diào)用下一個(gè)fn,如果fn全部為空則從大的數(shù)組中刪除elem,如果elem為空時(shí),則清楚setInterval。這樣一來(lái),邏輯上便可以走得通了。
然而動(dòng)畫最關(guān)鍵的因素還有一個(gè),那就是緩動(dòng)。 如果沒(méi)有緩動(dòng),那么動(dòng)畫效果看起來(lái)就非常的死板。千篇一律。目前做js動(dòng)畫用到的緩動(dòng)算法是很多的,大致分為兩類。
一種是flash類,一種是prototype類。
flash的需要四個(gè)參數(shù)。分別是,
1.時(shí)間初始話的時(shí)間t
2.動(dòng)畫的初始值b
3.動(dòng)畫的結(jié)束值c
4.動(dòng)畫持續(xù)的時(shí)間d
下面是一個(gè)flash 類的勻速運(yùn)動(dòng)算法
Linear: function(t,b,c,d){ return c*t/d + b; }
另一種則是prototype,這一類的參數(shù)只需要一個(gè),那就是當(dāng)前時(shí)間t與持續(xù)時(shí)間d的比值 (t/d)
我采用了第二種,因?yàn)樗膮?shù)方便。也更加適合上面的動(dòng)畫公式,下面是一個(gè)prototype類的勻速運(yùn)動(dòng)算法
linear : function(t){ return t;}.
加入緩動(dòng)后上面的公式變?yōu)?
動(dòng)畫元素開始值 + (動(dòng)畫元素結(jié)束值 - 動(dòng)畫元素開始值) * 緩動(dòng)函數(shù)((當(dāng)前時(shí)間-開始時(shí)間) / (動(dòng)畫需要時(shí)間)) + 值的格式。
至此便是整個(gè)動(dòng)畫類設(shè)計(jì)便結(jié)束了。其中參考了一些其它人的博客,在此表示感謝!
最后,還是貼一下詳細(xì)代碼吧。
復(fù)制代碼 代碼如下:
/**
* 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 = {
// 勻速運(yùn)動(dòng)
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 * t;
},
easeOutStrong : function (t) {
return 1 - (--t) * t * t * t;
},
easeBothStrong: function (t) {
return (t *= 2) < 1 ?
.5 * t * 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;
}
};
/**
* 基石 用于返回一個(gè)包含對(duì)話方法的對(duì)象
* @param elem
* @return {Object}
*/
function catfx(elem){
elem = typeof elem === 'string' ? doc.getElementById(elem) : elem;
return new fx(elem);
}
/**
* 內(nèi)部基石 用于返回一個(gè)包含對(duì)話方法的對(duì)象
* @param elem
* @return {Object}
*/
function fx(elem){
this.elem = elem;
return this;
}
/**
* 基礎(chǔ)類 包含一些基礎(chǔ)方法,和不變量
*/
var fxBase = {
speed : {
slow : 600,
fast : 200,
defaults : 400
},
fxAttrs : [],
fxMap:[],
/**
* 返回對(duì)象元素的css值
* @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;
}(),
/**
* 返回對(duì)象元素的css值
* @param 顏色值(暫不支持red,pink,blue等英文)
* @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)
}
},
/**
* 返回解析后的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};
},
/**
* 設(shè)置元素的透明度
* @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 + ')';
}
},
/**
* 設(shè)置元素的css值
* @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);
}
},
/**
* 返回解析后的prop
* @param prop
* @return {prop}
*/
parseProp : function(prop){
var props = {};
for(var i in prop){
props[i] = this.parseStyle(prop[i].toString());
}
return props;
},
/**
* 修正用戶的參數(shù)
* @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;
},
/**
* 維護(hù)setInterval的函數(shù),動(dòng)畫的啟動(dòng)
*/
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);
}
},
/**
* 停止所有動(dòng)畫
*/
stop : function(){
if(this.timer){
clearInterval(this.timer);
this.timer = undefined;
}
},
/**
* 存儲(chǔ)或者拿出隊(duì)列
* @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];
},
/**
* 刪除隊(duì)列
* @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;
/**
* 核心對(duì)象,用于生成動(dòng)畫對(duì)象。
* @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,
/**
* 將動(dòng)畫函數(shù)加入到隊(duì)列中,并啟動(dòng)動(dòng)畫。
*/
start : function(){
var cores = $.data(this.elem);
cores.push(this.step());
$.tick();
},
/**
* 核心方法,控制每一幀元素的狀態(tài)。
* @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 / this.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);
}
}
}
/**
* 外部接口類。
*/
fx.prototype = {
constructor : fx,
/**
* 動(dòng)畫方法
* @param prop
* @param duration
* @param easing
* @param callback
* @return {Object}
*/
animate : function(prop, duration, easing, callback){
if(arguments.length == 3 && typeof easing === 'function'){ //多數(shù)時(shí)候用戶第三個(gè)參數(shù)是回調(diào)
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;
},
/**
* 停止動(dòng)畫方法
* 使用方法 catjs('your element id').stop();
*/
stop : function(){
$.removeData(this.elem);
}
}
win.catfx = catfx;
})(this,document);
使用起來(lái)也比較簡(jiǎn)單.直接catfx('ID').animate({'margin-left':200,'background-color':'#ff0000'},600,'easeOut',function(){});
跟jquery的使用方法差不多,如果不傳第二個(gè)參數(shù),則默認(rèn)為400毫秒。不傳第三個(gè)參數(shù)則默認(rèn)勻速。第三個(gè)參數(shù)為函數(shù),并且總共只有三個(gè)參數(shù)時(shí)。第三個(gè)參數(shù)為回調(diào)。
例:catfx('ID').animate({'margin-left':200,'background-color':'#ff0000'},600,function(){alert('灑家是回調(diào)函數(shù)~')});
您可能感興趣的文章:
- 深入探究使JavaScript動(dòng)畫流暢的一些方法
- 淺析JavaScript動(dòng)畫
- javascript轉(zhuǎn)換靜態(tài)圖片,增加粒子動(dòng)畫效果
- JavaScript實(shí)現(xiàn)動(dòng)畫打開半透明提示層的方法
- 推薦10 款 SVG 動(dòng)畫的 JavaScript 庫(kù)
- javascript制作loading動(dòng)畫效果 loading效果
- javascript動(dòng)畫對(duì)象支持加速、減速、緩入、緩出的實(shí)現(xiàn)代碼
- javascript動(dòng)畫之圓形運(yùn)動(dòng),環(huán)繞鼠標(biāo)運(yùn)動(dòng)作小球
- javascript動(dòng)畫算法實(shí)例分析
相關(guān)文章
微信小程序天氣預(yù)報(bào)功能實(shí)現(xiàn)(支持自動(dòng)定位,附源碼)
對(duì)于一個(gè)經(jīng)常出門在外的人,關(guān)注天氣是至關(guān)重要的,下面這篇文章主要給大家介紹了關(guān)于微信小程序天氣預(yù)報(bào)功能實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),支持自動(dòng)定位,需要的朋友可以參考下2022-04-04JS實(shí)現(xiàn)判斷數(shù)組是否包含某個(gè)元素示例
這篇文章主要介紹了JS實(shí)現(xiàn)判斷數(shù)組是否包含某個(gè)元素,涉及javascript屬性操作與正則判斷相關(guān)使用技巧,需要的朋友可以參考下2019-05-05一分鐘學(xué)會(huì)JavaScript中的try-catch
這篇文章主要給大家介紹了關(guān)于如何通過(guò)一分鐘學(xué)會(huì)JavaScript中try-catch的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12javascript設(shè)置頁(yè)面背景色及背景圖片的方法
這篇文章主要介紹了javascript設(shè)置頁(yè)面背景色及背景圖片的方法,涉及JavaScript動(dòng)態(tài)操作頁(yè)面元素屬性的相關(guān)技巧,需要的朋友可以參考下2015-12-12javascript+css3開發(fā)打氣球小游戲完整代碼
這是一個(gè)簡(jiǎn)單但是印象深刻的小游戲,打氣球小游戲的實(shí)現(xiàn)代碼,主要基于js和css3,基于css3畫氣球,具體實(shí)現(xiàn)代碼大家參考下本文2017-11-11js addDqmForPP給標(biāo)簽內(nèi)屬性值加上雙引號(hào)的函數(shù)
這篇文章主要介紹了js addDqmForPP給標(biāo)簽內(nèi)屬性值加上雙引號(hào)的函數(shù),需要的朋友可以參考下2016-12-12