JavaScript的动态效果最基本的是动态改变大小,移动位置,改变透明度,改变颜色等等。
而其他一些比较炫的效果无非是对这些最基本效果的组合和运用。
现在网上已经有很多很不错的优秀Javascript库或者效果库,我们是否有必要再造轮子呢?
放眼望去,YahooUI,基于Prototype的scriptaculous,Rico,JQuery,Dojo,还有很多很多。
这些库都带有很不错很优秀的动态效果。我们可以直接使用。
但是对于一些中小型项目来说,只是偶尔用到一两个特效,就没有必要引用整个框架,要知道
这些家伙体积都不小哦.prototype.js50K,scripttaculous的effects.js也有40-50kdojo,yui更大。
在大多数情况下我们需要一个小巧独立(300行代码以内),无侵入性的效果库。.即使有现有的轮子,
我们不但要学会怎么使用轮子,更要学会如何亲手造一个轮子。
基于以上原因,我们今天来重写一个灵活的,扩展性强的,小巧的,跨浏览器的动态效果库。
考虑到prototype.js用户群的广泛性,我的部分代码引用了prototype.js,当然,我说过,我们要做一个独立
的效果库,即使在没有prototype.js的情况下,也要让代码正常工作。
先做一些准备工作。下面这些代码是任何效果库中必不可少的,因为它负责一些类似取位置坐标,
设置,获取element的透明度等这些基础工作。
代码:
复制代码 代码如下:
/*
这个函数的代码来自Prototype.jshttp://prototype.conio.net/
如果页面引用了prototype.js,则可以删除下面这个函数,
当然,即使不删除也没关系,因为作了简单的兼容性判断
*/
(function(){
if(!("Prototype"inwindow)){
Prototype={emptyFunction:function(){}};
Class={
create:function(){returnfunction(){this.initialize.apply(this,arguments)}}
};
$=function(element){
returntypeof(element)=="string"?document.getElementById(element):element
};
$A=function(arrLike){
for(vari=0,ret=[];i<arrLike.length;i++)ret[i]=arrLike[i];
returnret
};
Number.prototype.toColorPart=function(){returnString("00"+this.toString(16)).slice(-2)};
Function.prototype.bind=function(){
var__method=this,args=$A(arguments),object=args.shift();
returnfunction(){return__method.apply(object,args.concat($A(arguments)))}
}
Position={
cumulativeOffset:function(element){
varvalueT=0,valueL=0;
do{
valueT+=element.offsetTop||0;
valueL+=element.offsetLeft||0;
element=element.offsetParent;
}while(element);
return[valueL,valueT];
}
}
}
})()
/*
1.读取/设置透明度,
2.如果只传了一个参数element,则返回element的透明度(0<value<1)
3.如果传了两个参数element和value则把element的透明度设置为valuevalue范围0-1
*/
functionOpacity(element,value){
//byGo_Rush(阿舜)fromhttp://ashun.cnblogs.com/
varret;
if(value===undefined){//读取
if(!/msie/i.test(navigator.userAgent))
if(ret=element.style.opacity)returnparseFloat(ret);
try{returnelement.filters.item('alpha').opacity/100}catch(x){return1.0}
}else{//设置
value=Math.min(Math.max(value,0.00001),0.999999)//这句修复一些非ie浏览器opacity不能设置为1的bug
if(/msie/i.test(navigator.userAgent))returnelement.style.filter="alpha(opacity="+value*100+")"
returnelement.style.opacity=value
}
}
那么怎么设计这个Effect效果库呢。
首先,它的入口应该简洁。
1.一个是要使用效果的元素element
2.另一个是将要使用什么效果options
options应该是扩展性强的,方便用户使用的。我们把它设计成哈稀结构。
比如options={x:100,y:100}表示将element移动到坐标100,100
options={w:200,h:200}表示将element的大小改变为width=200,height=200
他们可以重叠,也可以确省比如options={h:20,y:20}这表示将element移动到top=20的位置,而且在移动的过程中让他的大小改变为height=20,同时,原来的left坐标和宽度都不发生改变,这是不是在做QQ的滑动效果呢?
还有控制效果的几个关键因素duration(整个效果的时间),delay(延迟几秒才开始效果),fps(频率快慢)都通过options传进来
复制代码 代码如下:
Effect=Class.create();
Effect.Fn=newObject();
Effect.Init=newObject();
//ByGo_Rush(阿舜)fromhttp://ashun.cnblogs.com/
Effect.prototype={
initialize:function(element,options){
this.element=$(element);
this.options=options||{};
this.duration=(this.options.duration||2)*1000;//效果执行时间
this.fps=this.options.fps||40;//频率
//当前步长,注:这个变量是递减的,当它0的时候意味着整个效果结束
this.steps=Math.floor(this.duration/this.fps);
this.maxSteps=this.steps;//整个效果的步长
this.setting=newObject();
this.timer=null;
if(this.options.delay){//延时处理
var_this=this;
setTimeout(function(){
_this.setup(_this);
(_this.options.onStart||Prototype.emptyFunction)(_this);
_this.run();
},_this.options.delay*1000);
}else{
this.setup(this);
(this.options.onStart||Prototype.emptyFunction)(this);
this.run();
}
},
run:function(){
if(this.isFinished())return(this.options.onComplete||Prototype.emptyFunction)(this);
if(this.timer)clearTimeout(this.timer);
this.duration-=this.fps;
this.steps--;
varpos=1-this.steps/this.maxSteps;//总进度的百分比
this.loop(this,pos);
(this.options.onUpdate||Prototype.emptyFunction)(this,pos);
this.timer=setTimeout(this.run.bind(this),this.fps);
},
isFinished:function(){
returnthis.steps<=0;
},
setup:function(effect){//初始化设置所有效果单元
for(varkeyinEffect.Init){
if(typeof(Effect.Init[key])!="function")continue;
try{Effect.Init[key](this)}catch(x){}
}
},
loop:function(effect,pos){//执行所有效果单元
for(varkeyinEffect.Fn){
if(typeof(Effect.Fn[key])!="function")continue;
try{Effect.Fn[key](effect,pos)}catch(x){}
}
}
}
当动态效果改变的时候,比如淡出,我们让一个element慢慢的变淡变小,并消失。
在不用效果库的情况下只用element.style.display="none"就做到了。
用效果库后,element.style的透明度opacity,尺寸width,height甚至位置left,top都发生了改变。
直到element的大小改变为0或者opactiy为0的时候他才会消失display="none"
那么,当下次再让他出现的时候,怎么恢复他的原始信息呢。比如width.height,opacity等。
在上面的代码中我们用effect.setting保存效果发生前的所有element信息.
注意以上三个自定义函数onStart,onUpdate,onComplete他们都是通过options传进来的调用者自定义函数。
分别在效果发生以前,效果发生时,效果发生完毕后执行。传入的参数可以查阅effect的所有对象。
看到这里,细心的看官可能注意到,这个效果库实际上什么效果都没有做,他只是搭了一个空架子。
Effect.Init给我们留了一个空接口供setup方法调用,Effect.Fn也是一个空接口供loop方法调用。
下面我们要做的是扩展Effect.Init和Effect.Fn来充实效果库。
先来一个大家最熟悉的淡入淡出
Effect.Init里面的所有成员函数都会被effect.setup执行,这个执行动作在效果开始之前,因此这里
适合做一些初始化的动作。比如把一些初始信息保存到effect.setting里面供以后使用。
Effect.Fn里面的所有成员函数都会被effect.loop执行,这个执行动作在效果运行中,因此这里
就要放核心效果代码,比如计算,改变效果增量等等。
复制代码 代码如下:
if(effect.options.opacity===undefined)return;
effect.setting.opacity=Opacity(effect.element);
}
Effect.Fn.opacity=function(effect,pos){
if(effect.options.opacity===undefined)return;
Opacity(effect.element,effect.setting.opacity+(effect.options.opacity-effect.setting.opacity)*pos);
}
下面贴出可调试代码(空效果库和淡入浅出插件):(可以拷贝到一个html运行,测试)
/**//* 这个函数的代码来自 Prototype.js http://prototype.conio.net/ 如果页面引用了prototype.js ,则可以删除下面这个函数, 当然,即使不删除也没关系,因为作了简单的兼容性判断 */ (function(){ if (!("Prototype" in window)){ Prototype={emptyFunction:function(){}}; Class ={ create: function(){return function(){this.initialize.apply(this, arguments)}} }; $ = function(element){ return typeof(element)=="string"?document.getElementById(element):element }; $A= function(arrLike){ for(var i=0,ret=[];i