透明特效是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>