透明特效是script.aculo.us提到的特效中最简单的特效之一。既然是特效,必须涉及时间与空间的概念。时间我们可以用setTimeout与setInterval,个人比较喜欢setTimeout,虽然它每次调用都重复注册,但可控性比较好。空间就全凭CSS的绝对定位实现位移了。在开始之前,我们练习一下setTimeout的递归用法(用来模拟setInterval)。
function text(el){ var node = (typeof el == "string")? document.getElementById(el) : el; var i = 0; var repeat = function(){ setTimeout(function(){ node.innerHTML = "<h1>"+i+"</h1>"; i++; if(i <= 100){ setTimeout(arguments.callee, 100); } },100) } repeat(); }
我们来试一下最简单的淡入特效,就是把node.innerHTML那一行改成透明度的设置。
function fadeIn(el){ var node = (typeof el == "string")? document.getElementById(el) : el; var i = 0; var fade = function(){ setTimeout(function(){ !+"\v1"? (node.style.filter="alpha(opacity="+i+")"): (node.style.opacity = i / 100); i++; if(i <= 100){ setTimeout(arguments.callee, 100); } },100) } fade(); }
但是这样并不完美,因为IE的滤镜可能会在IE7中失效,我们必须要用zoom=1来激活hasLayout。我们再添加一些可制定参数扩充它。注释已经非常详细,不明白在留言里再问我吧。
function opacity(el){ //必选参数 var node = (typeof el == "string")? document.getElementById(el) : el, //可选参数 options = arguments[1] || {}, //变化的持续时间 duration = options.duration || 1.0, //开始时透明度 from = options.from || 0.0 , //结束时透明度 to = options.to || 0.5, operation = 1, init = 0; if(to - from < 0){ operation = -1, init = 1; } //内部参数 //setTimeout执行的间隔时间,单位毫秒 var frequency = 100, //设算重复调用的次数 count = duration * 1000 / frequency, // 设算每次透明度的递增量 detal = Math.abs(to - from) /count, // 正在进行的次数 i = 0; var main = function(){ setTimeout(function(){ if(!+"\v1"){ if(node.currentStyle.hasLayout) node.style.zoom = 1;//防止滤镜失效 node.style.filter="alpha(opacity="+ (init * 100 + operation * detal * i * 100).toFixed(1) +")" }else{ node.style.opacity = (init + operation * detal * i).toFixed(3) } node.innerHTML = (init + operation * detal * i).toFixed(3) i++; if(i <= count){ setTimeout(arguments.callee, frequency); } },frequency) } main(); }
<div class="text" onclick="opacity(this,{duration:4.0,from:0.0,to:1})"></div> <div class="text" onclick="opacity(this,{duration:4.0,from:1.0,to:0})"></div>
但上面并不尽善尽美,有一个Bug。我们是通过短路运算符来决定是否使用默认参数还是我们传入的参数,但在javascript中,数字0甚至0.0都会自动转换为false。因此在第个例子,如果我们在to中传入0,它永远不会用到这个0,而是默认的0.5。解决方法让它变成字符串“0”。另,参数i也不是必须的,我们可以省去它,用count负责所有的循环,但这样一来,我们的思维就要逆过来想了。原来是加的,我们要变成减的。
function opacity(el){ //必选参数 var node = (typeof el == "string")? document.getElementById(el) : el, //可选参数 options = arguments[1] || {}, //变化的持续时间 duration = options.duration || 1.0, //开始时透明度 from = options.from || 0.0 , //结束时透明度 to = (options.to && options.to + "") || 0.5, operation = -1, init = 1; if(to - from < 0){ operation = 1, init = 0; } //内部参数 //setTimeout执行的时间,单位 var frequency = 100, //设算重复调用的次数 count = duration * 1000 / frequency, // 设算每次透明度的递增量 detal = operation * Math.abs(to - from) /count; var main = function(){ setTimeout(function(){ if(!+"\v1"){ if(node.currentStyle.hasLayout) node.style.zoom = 1;//防止滤镜失效 node.style.filter="alpha(opacity="+ (init * 100 + detal * count * 100).toFixed(1) +")" }else{ node.style.opacity = (init + detal * count).toFixed(3) } count--; if(count + 1){ setTimeout(arguments.callee, frequency); } },frequency) } main(); }
进一步优化,利用原型共享方法。
function Opacity(el){ var node = (typeof el == "string")? document.getElementById(el) : el, options = arguments[1] || {}, duration = options.duration || 1.0, from = options.from || 0.0 , to = (options.to && options.to + "") || 0.5, operation = -1, init = 1; if(to - from < 0){ operation = 1, init = 0; } var frequency = 100, count = duration * 1000 / frequency, detal = operation * Math.abs(to - from) /count; this.main(node,init,detal,count,frequency); } Opacity.prototype = { main : function(node,init,detal,count,frequency){ setTimeout(function(){ if(!+"\v1"){ if(node.currentStyle.hasLayout) node.style.zoom = 1;//防止滤镜失效 node.style.filter="alpha(opacity="+ (init * 100 + detal * count * 100).toFixed(1) +")" }else{ node.style.opacity = (init + detal * count).toFixed(3) } node.innerHTML = (init + detal * count).toFixed(3) count--; if(count + 1){ setTimeout(arguments.callee, frequency); } },frequency) } }
<div class="text" onclick="new Opacity(this,{duration:4.0,from:0.0,to:1})"></div> <div class="text" onclick="new Opacity(this,{duration:4.0,from:1.0,to:0})"></div>