JS动画

2015/12/29 01:27
阅读数 210

本文将按照以下步骤,封装一个能够实现同时运动和链式运动的JavaScript动画函数,即运动框架。

运动框架实现思路:

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

  2. 缓冲运动(动画的快慢变化)

  3. 多物体运动

  4. 任意值变化

  5. 链式运动

  6. 同时运动

一、速度动画

效果:

思路:

  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>


二、透明度动画

知识点:

透明度

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>

效果:

三、缓冲运动

使用第一个例子。将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和使用“.”给对象添加属性的方法使得变量和参数区分开来。

例子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,然后做相应处理即可。注意计算机不能准确存储小数这一点。

效果:

五、链式运动

链式运动就是一个动画完成后紧接着进行下一个动画,比如增加一个元素的高度后,马上增加其宽度,然后变化其透明度,依此类推。

为实现链式运动,将以上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--


展开阅读全文
打赏
0
17 收藏
分享
加载中
很好 学习一下
2015/12/29 14:30
回复
举报
上古遗露博主

引用来自“CorsAirs”的评论

不错

谢谢13
2015/12/29 10:20
回复
举报
不错
2015/12/29 08:53
回复
举报
更多评论
打赏
3 评论
17 收藏
0
分享
返回顶部
顶部