最近做一个广告项目,里面涉及很多动画效果,由于不能使用jquery,所以只能构建自己的动画方法。
数据结构:
elem是DOM对象,给它设置一个属性对象,用来记录elem的动画对象,比如'left','opacity'等。该对象的属性又有自己的动画队列,队列的每一项是一个对象,该对象有两个属性start,end,分别用来存储开始的值和动画结束的值,该对象有三个方法,begin,finish是开始结束动画,callbak用来保存该动画结束时需要执行的函数,见下图:
animation方法:
分词器,用来解析value表达式,比如:"+=300px"
function expr(key,value){ var r=/^(?:(+|-)=)?(-?d+(?:.d+)?)(px|)$/; var matches=r.exec(value); if(!matches){ throw new Error("传入参数不能运行动画"); }else{ return { "operator":matches[1]?matches[1]:"", "num":matches[2]?matches[2]:"", "unit":matches[3]?matches[3]:"" }; } }
调用上边这个方法:
var matchesEnd=expr(key,value);
就得到一个分词结果对象:
{ "operator":"+", "num":300, "unit":"px" }
begin方法中,我们需要先获取动画前的样式,代码如下:
var curValue=_.getAppliedStyle(elem,key);//获取当前样式 if((curValue==="auto")&&(revise[key]===0)){////修正top right bottom left width height 默认值为auto的情况 value=0; } temp.start=curValue; var matchesStart=expr(key,curValue);//设置动画初始值
代码:
动画模块:
(function(window){ function expr(key,value){ var r=/^(?:(+|-)=)?(-?d+(?:.d+)?)(px|)$/; var matches=r.exec(value); if(!matches){ throw new Error("传入参数不能运行动画"); }else{ return { "operator":matches[1]?matches[1]:"", "num":matches[2]?matches[2]:"", "unit":matches[3]?matches[3]:"" }; } } //method of animation //@elem //@properties example:{"left":"1000px",top:"200px"} opacity请使用w3c标准:0-1 //@options example:{speed:200} function animation(elem,properties,options,callback){ var speed=options.speed||1000; var inter=20; if(navigator.userAgent.indexOf("MSIE 8.0")>0){ inter=80; } if(!elem.ani){ elem.ani={}; } _.each(properties,function(value,key){ if(!elem.ani[key]){ elem.ani[key]=[]; } var matchesEnd=expr(key,value); var temp={}; if(!matchesEnd.operator){ temp.end=value; } var st; if(matchesEnd.num){ var i=0; temp.callback=callback; temp.begin=function(){ var curValue=_.getAppliedStyle(elem,key); temp.start=curValue; var matchesStart=expr(key,curValue); if(matchesEnd.operator){ temp.end=matchesStart.num*1+(matchesEnd.operator==="+"?1*matchesEnd.num:(-1)*matchesEnd.num)+matchesEnd.unit; matchesEnd=expr(key,temp.end); } var gap=matchesEnd.num-matchesStart.num; var step=gap/speed*inter; var num=gap/step; st=setInterval(function(){ i++; var tempValue=matchesStart.num*1+step*i+matchesEnd.unit; _.setStyle(elem,key,tempValue); if(i>=num){ //修正可能的计算错误 _.setStyle(elem,key,temp.end); if(callback){ temp.callback.call(temp); } clearInterval(st); elem.ani[key].shift(); if(elem.ani[key].length){ elem.ani[key][0].begin(); } } },inter); }; temp.finish=function(){ if(st){ clearInterval(st); } } if(elem.ani[key].push(temp)===1){ temp.begin(); } } },this); } function stop(elem,callback){ for(var key in elem.ani){ var len=elem.ani[key].length; if(len){ elem.ani[key][0].finish(); if(elem.ani[key][0].callback){ elem.ani[key][0].callback.call(this); } _.setStyle(elem,key,elem.ani[key][len-1].end); elem.ani[key].length=0; if(callback){ callback(); } } } } window.AniModule={}; window.AniModule.animation=animation; window.AniModule.stop=stop; })(window);
工具函数:
(function(){ var root=this; root._={}; var breaker={}; var ArrayProto=Array.prototype, ObjProto=Object.prototype, FuncProto=Function.prototype; var hasOwnProperty=ObjProto.hasOwnProperty; //ECMAScript 5 native function var nativeForEach=ArrayProto.forEach, nativeKeys=Object.keys;
_.isIE=!-[1,];
_.keys=nativeKeys||function(obj){ if(obj!==Object(obj)) throw new TypeError('Invalid object'); var keys=[]; for(var key in obj){ if(hasOwnProperty.call(obj,key)){ keys.push(key); } } return keys; }; _.each=function(obj,iterator,context){ if(obj==null) return; if(nativeForEach&&obj.forEach===nativeForEach){ obj.forEach(iterator,context); }else if(obj.length===+obj.length){ for (var i = 0,length=obj.length; i < length; i++) { if(iterator.call(context,obj[i],i,obj)===breaker) return; } }else{ var keys=_.keys(obj); for(var i=0,length=keys.length;i<length;i++){ if(iterator.call(context,obj[keys[i]],keys[i],obj)===breaker) return; } } }; _.getAppliedStyle=function(elem,styleName){ var style=""; if(styleName== "opacity"&&_.isIE){ style=elem.filters('alpha').opacity/100; }else if(window.getComputedStyle){ style=elem.ownerDocument.defaultView.getComputedStyle(elem,null).getPropertyValue(toHyphens(styleName)); }else if(elem.currentStyle){ style=elem.currentStyle[toCamelCase(styleName)]; } function toHyphens(camelCaseValue){ var result=camelCaseValue.replace(/[A-Z]/g,function(c){ return ("-"+c.charAt(0).toLowerCase()); }); return result; } function toCamelCase(hyphenatedValue){ var result=hyphenatedValue.replace(/-D/g,function(c){ return c.charAt(1).toUpperCase(); }); return result; } return style; }; _.setStyle=function(elem,name,value){ if(name=="opacity"&&_.isIE){ elem.style["filter"]="alpha(opacity="+value*100+")"; }else{ elem["style"][name]!==undefined?elem["style"][name]=value:elem[name] = value; } }; }).call(this);
调用:animation方法:
window.AniModule.animation(elem,{"left":"+=300px"},{"speed":1000});
stop方法:
window.AniModule.stop(elem);