一步一步教你写淡入淡出带注释的图片轮播插件(二)
接上一篇,现在进行第二部分。
开始之前,还是说说前文提到的关于把所有函数都写在一个闭包内的优化问题。前文也提到了,因为我们在初始化的时候要调用的只是init,所以可以只把init写入闭包,其他功能函数作为init的原型继承方法来调用。所以前文的代码其实可以这样改写:
var Hongru={}; function H$(id){return document.getElementById(id)} function H$$(c,p){return p.getElementsByTagName(c)} Hongru.fader = function(){ function init(options){ //options参数:id(必选):图片列表父标签id;auto(可选):自动运行时间;index(可选):开始的运行的图片序号 var wp = H$(options.id), // 获取图片列表父元素 ul = H$$('ul',wp)[0], // 获取 li = this.li = H$$('li',ul); this.a = options.auto?options.auto:2; //自动运行间隔 this.index = options.position?options.position:0; //开始运行的图片序号(从0开始) this.l = li.length; this.cur = this.z = 0; //当前显示的图片序号&&z-index变量 this.pos(this.index); //变换函数 } init.prototype = { auto:function(){ this.li.a = setInterval(new Function('Hongru.fader.move(1)'),this.a*1000); }, move:function(i){//参数i有两种选择,1和-1,1代表运行到下一张,-1代表运行到上一张 var n = this.cur+i; var m = i==1?n==this.l?0:n:n<0?this.l-1:n; //下一张或上一张的序号(注意三元选择符的运用) this.pos(m); //变换到上一张或下一张 }, pos:function(i){ clearInterval(this.li.a); this.z++; this.li[i].style.zIndex = this.z; //每次让下一张图片z-index加一 this.cur = i; //绑定当前显示图片的正确序号 this.auto(); //自动运行 } } return {init:init} }();
但是这样其实是有问题的,不知道大家发现没有,如果这样改写的话,在auto函数里就不能再调用‘Hongru.fader.move()’了,这是没定义的,那有人会说,既然是init的原型继承,那就调用'Hongru.fader.init.move()'不就对了吗?其实也不对,我在以前的文章中就讨论过这个问题http://www.cnblogs.com/hongru/archive/2010/10/09/1846636.html;init在没有实例化之前是访问不到它的prototype的,所以我们在这样做的时候要注意两个问题,
一个是初始化的时候要用new关键字对init实例化。
另外一个是在代码内部调用它的原型方法时也要通过我们实例化后的对象来调用,举个例子,比如我们对上面的代码初始化的时候应该是这样的
var newFader = new Hongru.fader.init({ //这个new很重要 id:'fader' });
如果我们在代码里要调用init的方法要通过我们新建的实例化对象newFader来调用,比如在上面的auto函数里要调用init的move方法的话,就直接调用'newFader.move()',这样才行。
可是这样还有个小问题,就是必须要保证实例化的变量名和代码中调用的一致,那么如果我改了我的初始化对象的名字,比如用newFader1,那么我还得去改源码,这样肯定是不行的,所以有个小技巧,就是在init里面多传一个参数,自己在做初始化的时候让变量名和参数一致,然后在源码里我们通过参数来调用。这样问题就圆满的解决了。
(ps:代码里之所以使用new Function的原因也是因为这样能冲破作用域链,这也是保证我们能这样架构我们代码的条件之一。)
综上:之前的代码应该这样优化:
var Hongru={}; function H$(id){return document.getElementById(id)} function H$$(c,p){return p.getElementsByTagName(c)} Hongru.fader = function(){ function init(anthor,options){this.anthor=anthor; this.init(options);} init.prototype = { init:function(options){ //options参数:id(必选):图片列表父标签id;auto(可选):自动运行时间;index(可选):开始的运行的图片序号 var wp = H$(options.id), // 获取图片列表父元素 ul = H$$('ul',wp)[0], // 获取 li = this.li = H$$('li',ul); this.a = options.auto?options.auto:2; //自动运行间隔 this.index = options.position?options.position:0; //开始运行的图片序号(从0开始) this.l = li.length; this.cur = this.z = 0; //当前显示的图片序号&&z-index变量 this.pos(this.index); //变换函数 }, auto:function(){ this.li.a = setInterval(new Function(this.anthor+'.move(1)'),this.a*1000); }, move:function(i){//参数i有两种选择,1和-1,1代表运行到下一张,-1代表运行到上一张 var n = this.cur+i; var m = i==1?n==this.l?0:n:n<0?this.l-1:n; //下一张或上一张的序号(注意三元选择符的运用) this.pos(m); //变换到上一张或下一张 }, pos:function(i){ clearInterval(this.li.a); this.z++; this.li[i].style.zIndex = this.z; //每次让下一张图片z-index加一 this.cur = i; //绑定当前显示图片的正确序号 this.auto(); //自动运行 } } return {init:init} }();
初始化时应该这样:
var fader = new Hongru.fader.init('fader',{ //保证第一个参数和变量名一致 id:'fader' });
好了,代码的优化方案到此结束。下面是第二部分效果的实现:淡入淡出的效果
其实有了上面良好的代码结构和逻辑的话,加入淡入淡出效果是比较容易的,思路很简单,在变化之前让图片透明,然后通过计时器让透明度渐渐增加。只不过这里面有几个边界的判断是比较重要的。同时透明度改变在ie和非ie下要注意用不同的css属性。
核心代码的改动就下面两段,一个是增加了透明度渐变函数fade(),另一个是在pos()里面事先要先把图片透明-->然后开始执行fade()
pos()里增加一个代码段:
if(this.li[i].o>=100){ //在图片淡入之前先把图片透明度置为透明 this.li[i].o = 0; this.li[i].style.opacity = 0; this.li[i].style.filter = 'alpha(opacity=0)'; } this.li[i].f = setInterval(new Function(this.anthor+'.fade('+i+')'),20);
然后加一个功能函数fade()
fade:function(i){ if(this.li[i].o>=100){ clearInterval(this.li[i].f); //如果透明度变化完毕,清除计时器 if(!this.li.a){ //确保所有计时器都清除掉之后再开始自动运行。要不然会导致有控制器时点击过快的话,计时器没来得及清除就开始下一次变化,功能就乱了 this.auto(); } } else{ //透明度变化 this.li[i].o+=5; this.li[i].style.opacity = this.li[i].o/100; this.li[i].style.filter = 'alpha(opacity='+this.li[i].o+')'; } }
好了,就这么简单。不过还有一点要记住就是在pos()调用的最初一定要记得清除上次的计时器!!
下面再把整个的源码都贴一遍吧:
var Hongru={}; function H$(id){return document.getElementById(id)} function H$$(c,p){return p.getElementsByTagName(c)} Hongru.fader = function(){ function init(anthor,options){this.anthor=anthor; this.init(options);} init.prototype = { init:function(options){ //options参数:id(必选):图片列表父标签id;auto(可选):自动运行时间;index(可选):开始的运行的图片序号 var wp = H$(options.id), // 获取图片列表父元素 ul = H$$('ul',wp)[0], // 获取 li = this.li = H$$('li',ul); this.a = options.auto?options.auto:2; //自动运行间隔 this.index = options.position?options.position:0; //开始运行的图片序号(从0开始) this.l = li.length; this.cur = this.z = 0; //当前显示的图片序号&&z-index变量 /* ==加入淡入淡出功能 ==*/ for(var i=0;i<this.l;i++){ this.li[i].o = 100; //为每一个图片都设置一个透明度变化量 this.li[i].style.opacity = this.li[i].o/100; //非IE用opacity即可 this.li[i].style.filter = 'alpha(opacity='+this.li[i].o+')'; //IE用滤镜 } this.pos(this.index); //变换函数 }, auto:function(){ this.li.a = setInterval(new Function(this.anthor+'.move(1)'),this.a*1000); }, move:function(i){//参数i有两种选择,1和-1,1代表运行到下一张,-1代表运行到上一张 var n = this.cur+i; var m = i==1?n==this.l?0:n:n<0?this.l-1:n; //下一张或上一张的序号(注意三元选择符的运用) this.pos(m); //变换到上一张或下一张 }, pos:function(i){ clearInterval(this.li.a); //清除自动变换计时器 clearInterval(this.li[i].f); //清除淡入淡出效果计时器 this.z++; this.li[i].style.zIndex = this.z; //每次让下一张图片z-index加一 this.cur = i; //绑定当前显示图片的正确序号 this.li.a = false; //做一个标记,下面要用到,表示清除计时器已经完成 //this.auto(); //自动运行 if(this.li[i].o>=100){ //在图片淡入之前先把图片透明度置为透明 this.li[i].o = 0; this.li[i].style.opacity = 0; this.li[i].style.filter = 'alpha(opacity=0)'; } this.li[i].f = setInterval(new Function(this.anthor+'.fade('+i+')'),20); }, fade:function(i){ if(this.li[i].o>=100){ clearInterval(this.li[i].f); //如果透明度变化完毕,清除计时器 if(!this.li.a){ //确保所有计时器都清除掉之后再开始自动运行。要不然会导致有控制器时点击过快的话,计时器没来得及清除就开始下一次变化,功能就乱了 this.auto(); } } else{ this.li[i].o+=5; this.li[i].style.opacity = this.li[i].o/100; this.li[i].style.filter = 'alpha(opacity='+this.li[i].o+')'; } } } return {init:init} }();
大家要注意我写的注释,有些地方是比较关键的。
再看看运行效果吧:
可能有人注意到了,这里的淡入淡出只是个头衔,其实只有淡入效果,不过不碍事,效果其实和有淡出基本一样,而且就算要淡出功能也仅仅需要改两句话。呵呵
这一部分到此结束,下一部分会加入控制器。