JavaScript实现动画效果

在一些动画设置中,我们可以用CSS中已有的动画属性方便的设置动画效果,比如说animation动画,transition过渡,它们结合一些2D,3D变换可以达到可观的动画效果,但是涉及到更多更加复杂的动画这个时候我们还要基于JavaScript实现。在这里我们定义一个JavaScript中一个实用的函数以便今后的设置。
在这里插入图片描述

动画基础

(1)定时器setTimeout

动画的设置是在一个连续间隔的时间内,变换关键帧,在人眼的视觉暂留下连续起来。这个时间间隔如何实现?时间间隔的实现依赖于setTimeout定时器API,今后的动画设置也将基于这个API。

setTimeout能够让某个函数在经过一段预定时间之后才开始执行,带有两个参数。第一个参数是要执行函数的名字,第二个参数则是一个数值,表示间隔的时间长短:

使用方法:setTimeout(“function()”,interval)
window.onload=setTimeout("changeNumber()",2000);//2s后调用此函数
function changeNumber()
{
	var test=document.getElementById("test");
	test.innerHTML="warning!";
}

定时器解除clearTimeout

我们可以像清除浮动那样清除定时器,用clearTimeout(variable).
使用方法:先将定时器赋值给一个变量,然后将这个变量作为clearTimeout的参数:

window.onload=function()
{
	var timer=setTimeout("changeNumber()",2000);
	clearTimeout(timer);
}
function changeNumber()
{
	var test=document.getElementById("test");
	test.innerHTML="warning!";
}

这样一来定时就被解除了.

(2)递归函数

既然有了定时器,我们就能基于定时器API来设置动画了。如何设置动画?我们需要在间隔时刻改变目标的位置,直到到达终点,只要这个间隔足够小,人眼就会将其视为连续的平滑动画。我决定定义一个moveElement函数,在间隔时刻改变目标的位置直到到达终点:

function moveElement(elementId,final_x,final_y,interval)
{
	if(!document.getElementById) return false;
	if(!document.getElementById(elementId)) return false;
	var elem=document.getElementById(elementId);
	var x=parseInt(elem.style.left);
	var y=parseInt(elem.style.top);
	if(x==final_x&&y==final_y) return true;//边界条件
	if(x<final_x){x++;}
	if(x>final_x){x--;}
	if(y<final_y){y++;}
	if(y>final_y){y--;}
	elem.style.left=x+"px";
	elem.style.top=y+"px";
	var repeat="moveElement('"+elementId+"',"+final_x+","+final_y+","+interval+")";
	timer=setTimeout(repeat,interval);//定时器设置,递归调用
}

这个函数用简单的递归调用实现。我们要注意一个问题,就是elem.style属性是elem标签的内联样式,而不是css中的id,class中的属性。一旦想使用style未初始化那么这个时候style中变量的值为NAN,所以想使用style中变量的值,有两个办法:
(1)行内初始化:

<p id="test" style="top: 0px;left:0px;"  >Hello World!</p>
<script src="test.js"></script>

(2)DOM初始化
我们定义一个初始化的函数,用这个函数给style属性初始化,或者直接在moveElement函数内部初始化:

function positionessage(elementId,first_x,first_y)
{
	if(!document.getElementById) return false;
	var elem=document.getElementById(elementId);
	elem.style.position="absoluate";
	elem.style.left=first_x+"px";
	elem.style.top=first_y+"px";
}

用动画增强网页效果

以上我们得到了一个动画函数,这个函数可以使我们的元素沿着任意方向移动,现在我们利用这个函数做一些更加是用的应用来增强我们的网页。
我们仍然看图片库这个例子:图片库
我们想当鼠标悬停在某个图片上时,下方的图片会更新,这样一来我们就能有一个预览效果。有一个简单的处理方法——将onclick改为onmouseover这样鼠标悬停就会得到响应。

function prepareGallery()
{
	if(!document.getElementsByTagName) return false;
	if(!document.getElementById) return false;
	if(!document.getElementById("photoGallery")) return false;
	var gallery=document.getElementById("photoGallery");
	var links=gallery.getElementsByTagName("a");
	for(var i=0;i<links.length;i++)
	{
		links[i].onmouseover=function()
		{
			return !showPic(this);//调用showPic函数
		}
		links[i].onkeypress=links[i].onmouseover;
	}                                                         
}

但是这样处理的缺点是响应不够顺畅,因为需要将新的图片加载上去难免会花费时间,我们想要的是更快更流畅的效果:

  • 设置一张长图,这张长图将所有的图片横向包含
  • 隐藏这张长图的绝大部分
  • 当鼠标悬浮时,显示这张图的相应子图
    在这里插入图片描述

(1)用CSS隐藏其他部分

现在整张图片都是可见的,我们想只展示一个400px宽,300px高的固定区域,而隐藏其他区域。用JavaScript无法做到这一点,但是我们可以用CSS的overflow属性来设置:
盒子模型溢出处理overflow

分量 描述
visible 溢出全部可见
hidden 隐藏,超出部分不可见
scroll 显示滚动条
auto 如果有超出,显示滚动条

(2) 设置偏移动画

现在我们可以将其余部分隐藏了,但是要达到浏览的效果,我们必须能够将其他部分展现出来。可以给图片设置一个偏移的效果,这样一来就能浏览到其他区域了,如何设置偏移呢?也许你会想到style.backgroundPositionX属性,但是这里我们用前面定义的动画函数moveElement.
HTML

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Photo Gallery</title>
	<link rel="stylesheet" type="text/css" href="photo gallery.css">
	<script src="photo gallery.js"></script>
</head>
<body>
	<h1>Photo Gallery</h1>
	<ul id="linkList">
		<li><a href="images/1.png" title="基普乔格1"><img src="images/1.png" width="55" height="45"></a></li>
		<li><a href="images/2.png" title="基普乔格2"><img src="images/2.png" width="55" height="45"></a></li>
		<li><a href="images/3.png" title="基普乔格3"><img src="images/3.png" width="55" height="45"></a></li>
		<li><a href="images/4.png" title="贝克勒1"><img src="images/4.png" width="55" height="45"></a></li>
		<li><a href="images/5.png" title="贝克勒2"><img src="images/5.png" width="55" height="45"></a></li>
		<li><a href="images/6.png" title="贝克勒3"><img src="images/6.png" width="55" height="45"></a></li>
	</ul>
	<div id="slidShow">
		<img src="images/test.jpg" alt="Choos a picture" id="preview" />		
	</div>
</body>
</html>

JavaScript:

function addLoadEvent(func)
{
	var oldonload=window.onload;
	if(typeof window.onload!='function')//未被绑定
	{
		window.onload=func;
	}
	else
	{
		window.onload=function()//匿名函数添加
		{
			oldonload();
			func();
		}
	}
}
addLoadEvent(prepareSlidshow);
function prepareSlidshow()
{
	if(!document.getElementById) return false;
	if(!document.getElementsByTagName) return false;
	if(!document.getElementById("linkList")) return false;
	if(!document.getElementsByTagName("preview")) return false;
	var preview=document.getElementById("preview");
	preview.style.position="absolute";
	preview.style.left="0px";
	preview.style.top="0px";
	var list=document.getElementById("linkList");
	var links=list.getElementsByTagName("a");
/*
	for(var i=0;i<links.length;i++)
	{
		links[i].οnmοuseοver=function()
		{
			var move=-(i+1)*400;
			moveElement("preview",move,0,10);
		}
	}
*/
		links[0].onmouseover=function()
		{
			var move=0;   
			moveElement("preview",move,0,0.01);
		}
		links[1].onmouseover=function()
		{
			var move=-400;
			moveElement("preview",move,0,0.01);
		}
		links[2].onmouseover=function()
		{
			var move=-800;
			moveElement("preview",move,0,0.01);
		}
		links[3].onmouseover=function()
		{
			var move=-1200;
			moveElement("preview",move,0,0.01);
		}
		links[4].onmouseover=function()
		{
			var move=-1600;
			moveElement("preview",move,0,0.01);
		}
		links[5].onmouseover=function()
		{
			var move=-2000;
			moveElement("preview",move,0,0.01);
		}
		links[6].onmouseover=function()
		{
			var move=-2400;
			moveElement("preview",move,0,0.01);
		}
}

function moveElement(elementId,final_x,final_y,interval)
{
	if(!document.getElementById) return false;
	if(!document.getElementById(elementId)) return false;
	var elem=document.getElementById(elementId);
	var x=parseInt(elem.style.left);
	var y=parseInt(elem.style.top);
	if(x==final_x&&y==final_y) return true;//边界条件
	if(x<final_x){x++;}
	if(x>final_x){x--;}
	if(y<final_y){y++;}
	if(y>final_y){y--;}
	elem.style.left=x+"px";
	elem.style.top=y+"px";
	var repeat="moveElement('"+elementId+"',"+final_x+","+final_y+","+interval+")";
	timer=setTimeout(repeat,interval);//定时器设置,递归调用
}

(3)累积事件处理

在上面的过程中我们历遍所有超链接,并且当鼠标悬浮在图片上方时,给长图设置偏移的动画moveElement,注意不能用循环处理,因为循环是一次性的,不能达到任意时刻悬浮都能移动的效果!乍一看移动效果是实现了,但是似乎有一些问题,当两个方向的图片都被悬浮的时候,图片没有移动而是来回振动,问题出在哪?
积累事件: 当图片被鼠标悬停时,moveElement函数被调用,movement计时器执行,而另一张图片被悬停时,第二个movement计时器也被执行,这个时候图片就无法确定执行谁,从而出现了错乱。
清除积累事件
我们想在moveElement函数内部添加一些东西,moveElement执行的时候,要将已有的timer清除:

方法1:定义全局变量

var timer;//定义全局变量
function moveElement(elementId,final_x,final_y,interval)
{
	if(!document.getElementById) return false;
	if(!document.getElementById(elementId)) return false;
	var elem=document.getElementById(elementId);
	var x=parseInt(elem.style.left);
	var y=parseInt(elem.style.top);
	if(timer) clearTimeout(timer);
	if(x==final_x&&y==final_y) return true;//边界条件
	if(x<final_x){x++;}
	if(x>final_x){x--;}
	if(y<final_y){y++;}
	if(y>final_y){y--;}
	elem.style.left=x+"px";
	elem.style.top=y+"px";
	var repeat="moveElement('"+elementId+"',"+final_x+","+final_y+","+interval+")";
	timer=setTimeout(repeat,interval);//定时器设置,递归调用
}

方法二:增加属性值

JavaScript允许我们创建新的属性:elem.property=value
我们可以给元素设置属性timer,如果存在属性那么就清除,否则直接执行:

function moveElement(elementId,final_x,final_y,interval)
{
	if(!document.getElementById) return false;
	if(!document.getElementById(elementId)) return false;
	var elem=document.getElementById(elementId);
	var x=parseInt(elem.style.left);
	var y=parseInt(elem.style.top);
	if(elem.timer) clearTimeout(elem.timer);
	if(x==final_x&&y==final_y) return true;//边界条件
	if(x<final_x){x++;}
	if(x>final_x){x--;}
	if(y<final_y){y++;}
	if(y>final_y){y--;}
	elem.style.left=x+"px";
	elem.style.top=y+"px";
	var repeat="moveElement('"+elementId+"',"+final_x+","+final_y+","+interval+")";
	elem.timer=setTimeout(repeat,interval);//定时器设置,递归调用
}

添加属性值的方法似乎更为安全,封装好的moveElement函数非常具有使用意义。
在这里插入图片描述

(4)改进变化趋势

我们的moveElement函数每次前进1px,你是否感觉太过于单调?现在我想给它设置一些变化,根据区域到目标的距离来变换,距离越远变化越快,越近变化越慢:

function moveElement(elementId,final_x,final_y,interval)
{
	if(!document.getElementById) return false;
	if(!document.getElementById(elementId)) return false;
	var elem=document.getElementById(elementId);
	var x=parseInt(elem.style.left);
	var y=parseInt(elem.style.top);
	if(elem.timer) clearTimeout(elem.timer);
	if(x==final_x&&y==final_y) return true;//边界条件
	var dis_x,dis_y;
	dis_x=Math.ceil((final_x-x)/10);//向上取整
	x+=dis_x;
	//round向下取整
	dis_y=Math.ceil((final_y-y)/10);//向上取整
	y+=dis_y;
	elem.style.left=x+"px";
	elem.style.top=y+"px";
	var repeat="moveElement('"+elementId+"',"+final_x+","+final_y+","+interval+")";
	elem.timer=setTimeout(repeat,interval);//定时器设置,递归调用
}

这将会是一个不错的选择
在这里插入图片描述

(5)生成HTML标记添加安全检查

前面的slidShow区域是专门为JavaScript服务的,考虑平稳退化时,JavaScript如果不被支持那么网页是否能够平稳退化?但暗示不会,当网页禁用JavaScript,我们的区域将会是一个不可更改的区域,这样的区域将毫无用处因为他的图片是固定的,这意味着我们未能平稳退化,所以我们要将JavaScript完全分离:
HTML:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Photo Gallery</title>
	<link rel="stylesheet" type="text/css" href="photo gallery.css">
	<script src="photo gallery.js"></script>
</head>
<body>
	<h1>Photo Gallery</h1>
	<ul id="linkList">
		<li><a href="images/1.png" title="基普乔格1"><img src="images/1.png" width="55" height="45"></a></li>
		<li><a href="images/2.png" title="基普乔格2"><img src="images/2.png" width="55" height="45"></a></li>
		<li><a href="images/3.png" title="基普乔格3"><img src="images/3.png" width="55" height="45"></a></li>
		<li><a href="images/4.png" title="贝克勒1"><img src="images/4.png" width="55" height="45"></a></li>
		<li><a href="images/5.png" title="贝克勒2"><img src="images/5.png" width="55" height="45"></a></li>
		<li><a href="images/6.png" title="贝克勒3"><img src="images/6.png" width="55" height="45"></a></li>
	</ul>

</body>
</html>

JavaScript:

function prepareSlidshow()
{
	if(!document.getElementById) return false;
	if(!document.getElementsByTagName) return false;
	if(!document.getElementById("linkList")) return false;
	if(!document.getElementsByTagName("preview")) return false;
	var slideShow=document.createElement("div");
	slideShow.setAttribute("id","slideShow");
	var preview=document.createElement("img");
	preview.setAttribute("src","images/test.jpg");
	preview.setAttribute("alt","Choose a picture");
	preview.setAttribute("id","preview");
	slideShow.appendChild(preview);
	var list=document.getElementById("linkList");
	insertAfter(slideShow,list);
	var preview=document.getElementById("preview");
	preview.style.position="absolute";
	preview.style.left="0px";
	preview.style.top="0px";
	
	var links=list.getElementsByTagName("a");
/*
	for(var i=0;i<links.length;i++)
	{
		links[i].οnmοuseοver=function()
		{
			var move=-(i+1)*400;
			moveElement("preview",move,0,10);
		}
	}
*/
		links[0].onmouseover=function()
		{
			var move=0;   
			moveElement("preview",move,0,15);
		}
		links[1].onmouseover=function()
		{
			var move=-400;
			moveElement("preview",move,0,15);
		}
		links[2].onmouseover=function()
		{
			var move=-800;
			moveElement("preview",move,0,15);
		}
		links[3].onmouseover=function()
		{
			var move=-1200;
			moveElement("preview",move,0,15);
		}
		links[4].onmouseover=function()
		{
			var move=-1600;
			moveElement("preview",move,0,15);
		}
		links[5].onmouseover=function()
		{
			var move=-2000;
			moveElement("preview",move,0,15);
		}

}

可以达到同样的效果:
在这里插入图片描述

发布了114 篇原创文章 · 获赞 128 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44307065/article/details/104077361
今日推荐