JS動畫

本文將按照如下步驟,封裝一個可以實現同時運動和鏈式運動的JavaScript動畫函數,即運動框架。javascript

運動框架實現思路:css

  1. 速度(改變值left、right、width、height、opacity)
    html

  2. 緩衝運動(動畫的快慢變化)java

  3. 多物體運動node

  4. 任意值變化json

  5. 鏈式運動瀏覽器

  6. 同時運動框架

1、速度動畫

效果:ide

思路:函數

  1. 建立一個div元素,使其隱藏在瀏覽器左側(設置left值爲其寬度的負數值,如div的width爲200px,設置其left值爲-200px)

  2. 當鼠標移入div時,div勻速顯示出來(left值逐漸增長到0px,如:-200px  -150px -100px -50px 0px)

  3. 當鼠標移出時,將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>


2、透明度動畫

知識點:

透明度

IE設置透明度

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>

效果:

3、緩衝運動

使用第一個例子。將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)
}

效果:

4、多物體動畫

注意:多物體運動,全部參數和變量、定時器等都不能共用。

使用參數this和使用「.」給對象添加屬性的方法使得變量和參數區分開來。

例子1:寬度值變化

<!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>

效果:

例子2:透明度變化

透明度的多物體運動。注意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>

效果:


例子3:任意屬性的多物體動畫:

知識點:

設置樣式

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>

效果:

例子4:兼容opacity透明度動畫

<!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,而後作相應處理便可。注意計算機不能準確存儲小數這一點。

效果:

5、鏈式運動

鏈式運動就是一個動畫完成後緊接着進行下一個動畫,好比增長一個元素的高度後,立刻增長其寬度,而後變化其透明度,依此類推。

爲實現鏈式運動,將以上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>

效果:

6、同時運動

想想,若是要實現寬高同時變化,上面的方法能實現嗎?

答案是不能。由於每次調用函數的時候,都清除了定時器,也就是說,一次最多隻能有一個定時器在運行,每次只能完成一個屬性的動畫。

那麼,如何實現同時運動呢?

可使用字面量建立對象,將多組屬性和值聲明在一個字面量對象中。利用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--

相關文章
相關標籤/搜索