Javascript动画效果(三)

前面我们已经介绍了速度动画、透明度动画、多物体运动和任意值变化,并且我们在Javascript动画效果(二)中介绍到我们封装了一个简单的插件雏形,接下来我们对前面的动画效果进行进一步扩充,尽量将我们的框架做到更实用。在这里我们还需要了解两个运动,一个是链式运动,一个是同时运动。它们间的区别分别是:链式运动是指运动一个接着一个(一个运动完成马上进行下一个运动);而同时运动是指所有的运动同时进行。在这里,我们该如何实现呢?

1、链式运动

  前面的效果中,我们已经能对任意值进行相应的变化,我们该如何在一个动画后添加一个动画呢?

  思路:我们能不能在参数中传入一个函数,当一个效果完成后马上执行后面的函数(效果),该函数可以是想要的动画效果

  实现:在function startMove(obj,attr,iTarget)中在传入一个参数fn,代表一个函数。这时我们还需要修改的有在定时器的后面增加一个判断if(fn){fn(); },当存在fn函数时执行fn函数,当不存在fn函数时清除定时器。这样我们的window.onload函数也应该发生相应变化,代码如下:



window.onload = function() {
	var Li = document.getElementById('li1');
	Li.onmouseover = function() {
		startMove(Li, 'width', 400, function() {
			startMove(Li, 'height', 200, function() {
				startMove(Li, 'opacity', 100);
			});
		});
	};
	Li.onmouseout = function() {
		startMove(Li, 'opacity', 30, function() {
			startMove(Li, 'height', 100, function() {
				startMove(Li, 'width', 200);
			});
		});
	};
};



所以我们得到链式运动的如下代码:



<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>链式运动</title>
		<style type="text/css">
			body,ul,li{ margin: 0px; padding: 0px; }
			ul,li{ list-style: none; }
			ul li{ width: 200px; height: 100px; background: yellow; margin-bottom: 20px; border: 4px solid #000; filter:alpha(opacity:30); opacity:0.3; }
		</style>
		
	</head>
	<body>
		<ul>
			<li id="li1"></li>
		</ul>
		<script type="text/javascript">
			window.onload = function(){
				var Li = document.getElementById('li1');
				Li.onmouseover = function(){
					startMove(Li,'width',400,function(){
						startMove(Li,'height',200,function(){
							startMove(Li,'opacity',100);
						});
					});
				};
				Li.onmouseout = function(){
					startMove(Li,'opacity',30,function(){
						startMove(Li,'height',100,function(){
							startMove(Li,'width',200);
						});
					});
				};
			};
		
			function startMove(obj,attr,iTarget,fn){
				clearInterval(obj.timer);
				obj.timer = setInterval(function(){
					var icur = 0;
					if(attr == 'opacity'){
						icur = Math.round(parseFloat(getStyle(obj,attr))*100);
					}else{
						icur = parseInt(getStyle(obj,attr));
					}
					var speed = (iTarget - icur)/10;
					speed = speed>0?Math.ceil(speed):Math.floor(speed);
					if(iTarget == icur){
						clearInterval(obj.timer);
						if(fn){
							fn();
						}
					}
					else{
						if(attr == 'opacity'){
							obj.style.filter = 'alpha(opacity:'+(icur+speed)+')';
							obj.style.opacity = (icur+speed)/100;
						}
						else{
							obj.style[attr] = icur+speed+'px';
						}
					}
				},30)
			}		
			
			function getStyle(obj,attr){
				if(obj.currentStyle){
					return obj.currentStyle[attr];
				}
				else{
					return getComputedStyle(obj,false)[attr];
				}
			}
		</script>
	</body>
</html>



 

2、同时运动

说到同时运动,你可能会觉得很简单,直接在onmouseover事件后面同时添加两个不同的starMove()函数就可以解决了(错误思路!),实际上并不是这样的,当有多个效果时,我们看到的是最后添加的那个效果。也就是说我们想同时改变宽度和高度(高度在后),我们得到的效果为只改变高度,宽度并没有变化。这里我们应该继续传参数么?从链式运动来看,传参数的效果只能在后面添加函数得到连续的动画效果。

  思路:我们可不可以用JSON的方法来同时改变多个动画效果?

  实现:将function startMove(obj,attr,iTarget,fn)中的attr和iTarget合并为一个参数:json,我们用for/in的方法来遍历json中的数据,例如:



var json = {"a":12,"b":21};
for(var attr in json){
	alert(json);//结果分别是:a,b
	alert(json[attr]);//结果分别是:12,21
}



 按照此方法,我们依次改变前面的代码:在定时器函数的后面加上代码:for(var attr in json) {...},并且修改里面的iTarget为json[attr],再将window.onload函数修改为:



window.onload = function() {
	var Li = document.getElementById('li1');
	Li.onmouseover = function() {
		startMove(Li, {
			width: 400,
			height: 200,
			opacity: 100
		});
	};
	Li.onmouseout = function() {
		startMove(Li, {
			width: 200,
			height: 100,
			opacity: 30
		});
	};
};



 这时我们差不多就可以得到我们想要的效果了,但是这样离我们最终的效果还是查了那么一点点,比如说我们将onmouseover事件中的starMove中的width修改为202,再执行我们的代码,我们会发现最终的height不是200px,opacity也不是100,如图:

这样就很尴尬了,之前我们的效果不是还蛮好的么?我们再回到我们的JS代码中分析结构,觉得最有可能出错的就是starMove函数中,我们发现这段代码的意思有点难理解:

if(json[attr] == icur) {
    clearInterval(obj.timer);
    if(fn) {
        fn();
    }
}

我们并不知道是不是所有的运动都到达终点时停止所有运动还是当只有一个运动达到终点时立马停止所有运动。但是通过我们前面的操作,可以知道的实际结果为,只有一个运动到达终点值时,所有的运动都停止了(此时还有运动没执行完),我们该如何进行操作呢?

  思路:我们可以假定一个参数flag,并且赋值为true,在执行clearInterval(obj.timer);操作前我们先进行判断if(json[attr] != icur) { flag = false;},后面执行之前else里面的语句,再执行如下语句if(flag = true) {clearInterval(obj.timer); if(fn) {fn();}},这样我们就可以得到同时运动的完整代码如下:



<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>链式运动</title>
		<style type="text/css">
			body,ul,li{ margin: 0px; padding: 0px; }
			ul,li{ list-style: none; }
			ul li{ width: 200px; height: 100px; background: yellow; margin-bottom: 20px; border: 4px solid #000; filter:alpha(opacity:30); opacity:0.3; }
		</style>
		
	</head>
	<body>
		<ul>
			<li id="li1"></li>
		</ul>

		<script type="text/javascript">
			window.onload = function(){
				var Li = document.getElementById('li1');
				Li.onmouseover = function(){
					startMove(Li,{width:202,height:200,opacity:100});
				};
				Li.onmouseout = function(){
					startMove(Li,{width:200,height:100,opacity:30});
				};
			};
			
			function getStyle(obj, attr) {
				if(obj.currentStyle) {
					return obj.currentStyle[attr];
				} else {
					return getComputedStyle(obj, false)[attr];
				}
			}
			
			function startMove(obj, json, fn) {
				//定义标杆
				var flag = true; //假设的
			
				clearInterval(obj.timer);
				obj.timer = setInterval(function() {
					for(var attr in json) {
						var icur = 0;
						if(attr == 'opacity') {
							icur = Math.round(parseFloat(getStyle(obj, attr)) * 100);
						} else {
							icur = parseInt(getStyle(obj, attr));
						}
						var speed = (json[attr] - icur) / 10;
						speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
						if(json[attr] != icur) {
							flag = false;
						}
						if(attr == 'opacity') {//判断是否为opacity
							obj.style.filter = 'alpha(opacity:' + (icur + speed) + ')';
							obj.style.opacity = (icur + speed) / 100;
						} else {
							obj.style[attr] = icur + speed + 'px';
						}
						if(flag){
							clearInterval(obj.timer);
							if(fn){
								fn();
							}
						}	
					}
				}, 30)
			}
		</script>
	</body>
</html>



 这样我们的同时运动的动画效果就实现了。在这里,你有没有觉得很神奇?

在这里我们已经将一个简单的运动插件封装完成了,我们将里面的代码做一些解释,并且将它保存为一个foodoir.animate.js文件,代码如下:



/*
 * 简单的运动框架
 * 作者:foodoir
 * 此框架仅作参考!!!
 * 
 * 使用方法见博文
 */

function getStyle(obj, attr) {
	if(obj.currentStyle) {
		return obj.currentStyle[attr];
	} else {
		return getComputedStyle(obj, false)[attr];
	}
}

function startMove(obj, json, fn) {

	clearInterval(obj.timer); //清除定时器,避免重复生成多个定时器
	obj.timer = setInterval(function() {
		var flag = true; //假设刚开始时所有运动都已完成
		for(var attr in json) { //遍历json

			var icur = null;
			//1.判断类型
			if(attr == 'opacity') {
				icur = Math.round(parseFloat(getStyle(obj, attr)) * 100);
			} else {
				icur = parseInt(getStyle(obj, attr));
			}
			//2.算速度
			var speed = (json[attr] - icur) / 5;
			speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
			//3.检测停止
			if(icur != json[attr]) {
				flag = false;
			}
			if(attr == 'opacity') {
				obj.style.filter = 'alpha(opacity:' + (icur + speed) + ')';
				obj.style.opacity = (icur + speed) / 100;
			} else {
				obj.style[attr] = icur + speed + 'px';
			}
		}
		if(flag) { //当所有运动都完成时,清除定时器
			clearInterval(obj.timer);
			if(fn) {
				fn();
			}
		}
	}, 30);
}



 后面,我们将介绍用自己的框架来实现多种动画效果,并且和jquery中的动画效果进行比较。