本文將按照如下步驟,封裝一個可以實現同時運動和鏈式運動的JavaScript動畫函數,即運動框架。javascript
運動框架實現思路:css
速度(改變值left、right、width、height、opacity)
html
緩衝運動(動畫的快慢變化)java
多物體運動node
任意值變化json
鏈式運動瀏覽器
同時運動框架
效果:ide
思路:函數
建立一個div元素,使其隱藏在瀏覽器左側(設置left值爲其寬度的負數值,如div的width爲200px,設置其left值爲-200px)
當鼠標移入div時,div勻速顯示出來(left值逐漸增長到0px,如:-200px -150px -100px -50px 0px)
當鼠標移出時,將div隱藏(left值逐漸減少爲初始值,如:0px 50px 100px 150px 200px)
知識點:
oDiv1.offsetLeft 獲取div1的left屬性值,返回數字
http://www.imooc.com/video/2879/0
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> * { padding:0; margin:0;} #div1 { position:relative; width:200px; height:200px; left:-200px; background-color: #f00; } #btn { position: absolute; background-color: #0f0; width:30px; height:50px; top:75px; right:-30px; } </style> <script type="text/javascript"> window.onload = function() { var oDiv1 = document.getElementById('div1'); oDiv1.onmouseover = function() { slide(0); } oDiv1.onmouseout = function() { slide(-200);} } var timer = null; /* *@param targetLeft 目標left值 */ function slide(targetLeft) { clearInterval(timer); //當鼠標重複移動到div1上時,會屢次加載定時器,因此在函數開始運行時清除全部定時器 var oDiv1 = document.getElementById('div1'); timer = setInterval(function() { var speed = 0; if(targetLeft > oDiv1.offsetLeft) { speed = 20; //left步長(速度),顯示 }else { speed = -20; //隱藏 } if(oDiv1.offsetLeft == targetLeft) { clearInterval(timer); }else { oDiv1.style.left = oDiv1.offsetLeft + speed + 'px'; } },200) } </script> </head> <body> <div id="div1"><span id="btn"></span></div> </body> </html>
知識點:
CSS設置透明度:
filter:alpha(opacity:30); opacity的範圍:0-100
JavaScript設置透明度:
node.style.filter = ‘alpha(opacity:30)’;
CSS設置透明度:
opacity:0.5; opacity的範圍:0-1
JavaScript設置透明度:
node.style.opacity = 0.5;
代碼:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> #div1 { width:200px; height:200px; background-color: #f00; filter:alpha(opacity:30); //IE opacity: 0.3; } </style> <script type="text/javascript"> window.onload = function() { var oDiv1 = document.getElementById('div1'); oDiv1.onmouseover = function() { moveAction(100); } oDiv1.onmouseout = function() { moveAction(30); } } var timer = null; var alpha = 30; function moveAction(iTarget) { clearInterval(timer); var oDiv1 = document.getElementById('div1'); var speed = 0; timer = setInterval(function() { if(alpha > iTarget) { speed = -10; } else { speed = 10; } if(alpha == iTarget) { clearInterval(timer); }else { alpha += speed; oDiv1.style.filter = 'alpha(opacity:' + alpha + ')'; //IE oDiv1.style.opacity = alpha/100; //主流瀏覽器 } },30); } </script> </head> <body> <div id="div1"></div> </body> </html>
效果:
使用第一個例子。將speed值改成當前left值減去目標left值,併除以一個係數以控制速度,使其動畫由快變慢。
不一樣的只是函數:
function slide(targetLeft) { clearInterval(timer); //當鼠標重複移動到div1上時,會屢次加載定時器,因此在函數開始運行時清除全部定時器 var oDiv1 = document.getElementById('div1'); timer = setInterval(function() { var speed = 0; //緩衝運動實現,控制speed值 speed = (targetLeft-oDiv1.offsetLeft)/20; //speed值愈來愈小,因爲是浮點型,最終會無限趨近於0,而不會到達0,因此要將speed值取整 speed = (speed>0)?Math.ceil(speed):Math.floor(speed); //console.log(speed); if(oDiv1.offsetLeft == targetLeft) { clearInterval(timer); }else { oDiv1.style.left = oDiv1.offsetLeft + speed + 'px'; } },30) }
效果:
注意:多物體運動,全部參數和變量、定時器等都不能共用。
使用參數this和使用「.」給對象添加屬性的方法使得變量和參數區分開來。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> * { padding:0; margin:0;} ul,li { list-style: none; } li { width:200px; height:100px; margin:10px 0; background-color: #ff0; } </style> <script type="text/javascript"> window.onload = function() { var oLi = document.getElementsByTagName('li'); for(var i=0;i<oLi.length;i++) { oLi[i].timer = null; oLi[i].onmouseover = function() { slide(this,400); } oLi[i].onmouseout = function() { slide(this,200); } } } /* *@param iTarget 動畫屬性要達到的目標值 */ function slide(obj,iTarget) { clearInterval(obj.timer); //當鼠標重複移動到div1上時,會屢次加載定時器,因此在函數開始運行時清除全部定時器 obj.timer = setInterval(function() { //緩衝運動實現,控制speed值 speed = (iTarget-obj.offsetWidth)/20; //speed值愈來愈小,因爲是浮點型,最終會無限趨近於0,而不會到達0,因此要將speed值取整 speed = (speed>0)?Math.ceil(speed):Math.floor(speed); //console.log(speed); if(obj.offsetWidth == iTarget) { clearInterval(obj.timer); }else { obj.style.width = obj.offsetWidth + speed + 'px'; } },30) } </script> </head> <body> <ul> <li></li> <li></li> <li></li> </ul> </body> </html>
效果:
透明度的多物體運動。注意IE定義透明度的方法在其餘瀏覽器是不能識別的,這會致使寫在諸如:filter:alpha('opacity:30')代碼以後的CSS樣式不被解析。故須要將IE的語法單獨聲明,防止出錯。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> div { width:150px; height:150px; background-color: #f00; margin:10px; opacity: 0.3; float:left; } div { filter:alpha(opacity:30); //IE } </style> <script type="text/javascript"> window.onload = function() { var oDiv = document.getElementsByTagName('div'); for(var i=0;i<oDiv.length;i++) { oDiv[i].timer = null; oDiv[i].alpha = 30; oDiv[i].onmouseover = function() { moveAction(this,100); } oDiv[i].onmouseout = function() { moveAction(this,30); } } } function moveAction(obj,iTarget) { clearInterval(obj.timer); obj.timer = setInterval(function() { var speed = 0; if(obj.alpha > iTarget) { speed = -10; }else { speed = 10; } if(obj.alpha == iTarget) { clearInterval(obj.timer); }else { obj.alpha += speed; obj.style.filter = 'alpha(opacity:' + obj.alpha + ')'; //IE obj.style.opacity = obj.alpha/100; //主流瀏覽器 } },30); } </script> </head> <body> <div id="div1"></div> <div id="div2"></div> <div id="div3"></div> <div id="div4"></div> </body> </html>
效果:
知識點:
node.style[attr]; 如: node.style['width']; node.style['fontSize'];
IE: node.currentStyle(attr); 如:node.currentStyle('width'); FireFox: getComputedStyle(obj,false)[attr]; 如:getComputedStyle(node,false)['fontSize']; 以上都返回結果都帶單位。
代碼:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> * { padding:0; margin:0;} div { width:100px; height:100px; background-color: #ff0; border: 2px solid #000; margin:10px; } #div1 { } #div2 { } </style> <script type="text/javascript"> window.onload = function() { var oDiv1 = document.getElementById('div1'); var oDiv2 = document.getElementById('div2'); oDiv1.onmouseover = function() { slide(this,'height',400); } oDiv1.onmouseout = function() { slide(this,'height',100); } oDiv2.onmouseover = function() { slide(this,'width',200); } oDiv2.onmouseout = function() { slide(this,'width',100); } } /* *@param iTarget 動畫屬性要達到的目標值 */ function slide(obj,attr,iTarget) { clearInterval(obj.timer); //當鼠標重複移動到div1上時,會屢次加載定時器,因此在函數開始運行時清除全部定時器 obj.timer = setInterval(function() { var iCur = parseInt(getStyle(obj,attr)); //緩衝運動實現,控制speed值 speed = (iTarget-iCur)/20; //speed值愈來愈小,因爲是浮點型,最終會無限趨近於0,而不會到達0,因此要將speed值取整 speed = (speed>0)?Math.ceil(speed):Math.floor(speed); //console.log(speed); if(iCur == iTarget) { clearInterval(obj.timer); }else { obj.style[attr] = iCur + speed + 'px'; } },30) } function getStyle(obj,attr) { //IE if(obj.currentStyle) { return obj.currentStyle[attr]; }else { return getComputedStyle(obj,false)[attr]; } } </script> </head> <body> <div id="div1"></div> <div id="div2"></div> </body> </html>
效果:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> * { padding:0; margin:0;} div { width:100px; height:100px; background-color: #ff0; border: 2px solid #000; margin:10px; opacity:0.3; } div { filter:alpha(opacity:30); } </style> <script type="text/javascript"> window.onload = function() { var oDiv1 = document.getElementById('div1'); oDiv1.onmouseover = function() { slide(this,'opacity',100); } oDiv1.onmouseout = function() { slide(this,'opacity',30); } } /* *@param targetLeft 目標left值 */ function slide(obj,attr,iTarget) { clearInterval(obj.timer); obj.timer = setInterval(function() { var iCur = 0; if(attr == 'opacity') { iCur = Math.round(parseFloat(getStyle(obj,attr))*100); }else { var iCur = parseInt(getStyle(obj,attr)); } //緩衝運動實現,控制speed值 speed = (iTarget-iCur)/20; //speed值愈來愈小,因爲是浮點型,最終會無限趨近於0,而不會到達0,因此要將speed值取整 speed = (speed>0)?Math.ceil(speed):Math.floor(speed); //console.log(speed); if(iCur == iTarget) { clearInterval(obj.timer); }else { if(attr == 'opacity') { obj.style.filter = iCur + speed; //IE obj.style[attr] = (iCur + speed)/100; //FireFox }else { obj.style[attr] = iCur + speed + 'px'; } } },30) } function getStyle(obj,attr) { //IE if(obj.currentStyle) { return obj.currentStyle[attr]; }else { return getComputedStyle(obj,false)[attr]; } } </script> </head> <body> <div id="div1"></div> </body> </html>
因爲例子三的侷限性,它能實現寬、高、偏移等的動畫(單位一致都爲px),但不能設置透明度值,透明度不帶單位,屬性值爲0-1的小數(FireFox,IE),或0-100的整數,再也不適用例子三中的動畫函數,故需對函數進行完善。
要實現透明度動畫,只需判斷attr參數是不是opacity,而後作相應處理便可。注意計算機不能準確存儲小數這一點。
效果:
鏈式運動就是一個動畫完成後緊接着進行下一個動畫,好比增長一個元素的高度後,立刻增長其寬度,而後變化其透明度,依此類推。
爲實現鏈式運動,將以上slide(obj,attr,iTarget)函數改成slide(obj,attr,iTarget,fun),增長一個fun參數,fun參數做爲回調函數,傳遞的一樣是slide函數。
在當前動畫完成後判斷是否有fun參數,若是有,就調用fun()執行下一個動畫。
將slide函數單獨取出封裝到外部js文件。
move.js:
/* *動畫函數 *@param obj 對象名 *@param attr 對象屬性名 *@param iTarget 屬性目標值 */ function move(obj,attr,iTarget,fun) { clearInterval(obj.timer); obj.timer = setInterval(function() { //1.獲取當前屬性值 var iCur = 0; if(attr == 'opacity') { iCur = Math.round(parseFloat(getStyle(obj,attr))*100); }else { var iCur = parseInt(getStyle(obj,attr)); } //2.計算動畫速度 speed = (iTarget-iCur)/20; speed = (speed>0)?Math.ceil(speed):Math.floor(speed); //console.log(speed); //for debug //3.檢測是否中止動畫 if(iCur == iTarget) { clearInterval(obj.timer); if(fun) { fun(); } }else { if(attr == 'opacity') { obj.style.filter = iCur + speed; //IE obj.style[attr] = (iCur + speed)/100; //FireFox }else { obj.style[attr] = iCur + speed + 'px'; } } },30) } /* *獲取屬性 *@param obj 對象名 *@param attr 屬性名 *@return 屬性值 */ function getStyle(obj,attr) { //IE if(obj.currentStyle) { return obj.currentStyle[attr]; }else { return getComputedStyle(obj,false)[attr]; } }
下面寫一個鏈式動畫的例子:
demo.html:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script src="./js/move.js"></script> <style type="text/css"> * { padding:0; margin:0;} #div1 { width:100px; height:100px; background-color: #ff0; border: 2px solid #000; margin:10px; opacity:0.3; } #div1 { filter:alpha(opacity:30); } </style> <script type="text/javascript"> window.onload = function() { var oDiv1 = document.getElementById('div1'); oDiv1.onmouseover = function() { move(oDiv1,'width',200,function() { move(oDiv1,'height',200,function(){ move(oDiv1,'opacity',100); }); }); } oDiv1.onmouseout = function() { move(oDiv1,'opacity',30,function(){ move(oDiv1,'height',100,function(){ move(oDiv1,'width',100); }); }); } } </script> </head> <body> <div id="div1"></div> </body> </html>
效果:
想想,若是要實現寬高同時變化,上面的方法能實現嗎?
答案是不能。由於每次調用函數的時候,都清除了定時器,也就是說,一次最多隻能有一個定時器在運行,每次只能完成一個屬性的動畫。
那麼,如何實現同時運動呢?
可使用字面量建立對象,將多組屬性和值聲明在一個字面量對象中。利用for...in...循環完成多組屬性動畫的遍歷。
具體就是將原來的move函數進行以下變換:
move(obj,attr,iTarget,fun) 將attr和iTarget組合爲字面量對象格式 變換後:move(obj,json,fun)
字面量建立含多組屬性和值的對象——json參數的格式:
var json = {width:200,height:100,opacity:100}
使用for...in...遍歷json:
for(var attr in json) attr就表示屬性名稱 json[attr]就表示屬性值
例如:
var json = {width:200,height:100,opacity:100} for(var attr in json) { console.log(attr); //輸出:width,height,opacity console.log(json[attr]); //輸出:200,100,100 }
同時要注意,當判斷是否完成動畫時,要全部動畫都完成才能夠,不然,當一個屬性的動畫完成後,會清除定時器。能夠建立一個flag標記是否全部動畫都完成。
完善後的move函數以下:
/* *動畫函數 *@param obj 對象名 *@json json格式:{attr:iTarget[,attr:iTarget,...]} *@param attr 對象屬性名 *@param iTarget 屬性目標值 */ function move(obj,json,fun) { clearInterval(obj.timer); obj.timer = setInterval(function() { var iCur = 0; var flag = true; for(var attr in json) { //1.獲取當前屬性值 if(attr == 'opacity') { iCur = Math.round(parseFloat(getStyle(obj,attr))*100); }else { var iCur = parseInt(getStyle(obj,attr)); } //2.計算動畫速度 speed = (json[attr]-iCur)/10; speed = (speed>0)?Math.ceil(speed):Math.floor(speed); console.log(iCur); //for debug //3.檢測是否中止動畫 if(iCur != json[attr]) { flag = false; } if(attr == 'opacity') { obj.style.filter = iCur + speed; //IE obj.style[attr] = (iCur + speed)/100; //FireFox }else { obj.style[attr] = iCur + speed + 'px'; } } if(flag) { clearInterval(obj.timer); if(fun) { fun(); } } },30) }
沿用第五節HTML:
<script type="text/javascript"> window.onload = function() { var oDiv1 = document.getElementById('div1'); oDiv1.onmouseover = function() { move(oDiv1,{width:150,height:200,opacity:100},function() {move(oDiv1,{borderWidth:10})}); } oDiv1.onmouseout = function() { move(oDiv1,{height:100,width:100,opacity:30},function() {move(oDiv1,{borderWidth:2})}); } } </script>
效果:
--to be continued--