添加样式信息,CSS仍是最佳工具,但是如果想随着时间的变化而改变某个元素的样式,则只能使用JS。JS能够按照预定的时间间隔重复调用一个函数,这就意味着我们可以随着时间的推移而不断改变某个元素的样式。
时间
JS函数setTimeout能够让某个函数在经过一段预定的时间之后才开始执行。
setTimeout("function",interval)
// 第一个参数,是要执行的函数的名字
//第二个参数,它以毫秒为单位设定了需要经过多长时间后菜开始执行第一个参数所给出的函数
clearTimeout("function")
//取消等待执行队列里的某个函数
如:movement=setTimeout("moveMessage()",5000);
clearTimeout(movement);
//movement在声明它时未使用关键字var,它是一个全局变量。
时间递增量
上面的动画实现是跳跃式的,那我们实际想实现的效果是渐进式的,那该怎么办呢?
正确的处理逻辑是:
- 获得元素的当前位置
- 如果元素已经达到它的目的地,则退出这个函数
- 如果元素尚未达到它的目的地,则把它向目的地移近一点儿。
- 经过一段段时间间隔之后从步骤1开始重复上述步骤
JS函数parseInt可以把字符串里的数值信息提取出来。
如parseInt("39 steps")返回的数值是39。parseInt函数的返回值通常是整数。如果需要提取的是带小数点的数值,就应该使用相应的parseFloat函数。parseFloat(string)
抽象
抽象的JS代码如下所示。对网页中的message元素进行缓慢移动。
function positionMessage(){
if (!document.getElementById) {return false;}
if (!document.getElementById("message")) {return false;}
var elem=document.getElementById("message");
elem.style.position="absolute";
elem.style.left="50px";
elem.style.top="100px";
moveElement("message",200,100,10);
}
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 xpos=parseInt(elem.style.left);
var ypos=parseInt(elem.style.top);
if(xpos==final_x&&ypos==final_y){
return true;
}
if(xpos<final_x){
xpos++;
}
if(xpos>final_x){
xpos--;
}
if(ypos<final_y){
ypos++;
}
if(ypos>final_y){
ypos--;
}
elem.style.left=xpos+"px";
elem.style.top=ypos+"px";
var repeat="moveElement('"+elementID+"',"+final_x+","+final_y+","+interval+")";
movement=setTimeout(repeat,interval);
}
addLoadEvent(positionMessage);
实用动画
网页上的动画元素不仅容易引起访问者的反感,还容易导致各种各样的可访问性的问题。下面来看一个实用性的例子。
提出问题
当用户把鼠标指针悬停在其中的某个链接时,我们想用一种先睹为快的方式告诉用户这个链接将他们带往何方。
点击链接时,希望立刻显示一张图片。如果只是通过网络加载的话,肯定需要时间,就不能做到及时响应。为了解决这个问题的方案如下:
- 为所有的预览图片生成为一张“集体照”形式的照片
- 隐藏这张“集体照”图片的绝大部分
- 当用户把鼠标指针悬停在某个链接上方时,只显示这张“集体照片”的相应部分。
每次只让图片的某个100*100像素出现,我们无法用JS做到这一点,但是可以用CSS来做。
CSS
CSS的overflow属性用来处理一个元素的尺寸超出其容,器尺寸的情况。当一个元素包含的内容超出自身大小时,就会发生内容溢出,这种情况,你可以对内容进行“裁剪”只让一部分内容可见。你还可以通过overflow属性告诉浏览器是否需要显示滚动条,以便让用户能够看到内容的其余部分。
overflow属性可取值有4种:visible、hidden、scroll和auto。
- visible:不裁剪溢出的内容。浏览器将把溢出的内容显示在其容器元素的显示区域以外,全部内容都可见。
- hidden:隐藏溢出的内容。内容只显示在其容器元素的显示区域里,这意味着只有一部分内容可见。
- scroll:类似于hidden,浏览器将对溢出的内容进行隐藏,但显示一个滚动条以便让用户能够滚动看到内容的其它部分。
- auto:类似于scroll,但浏览器只在确实发生溢出时才显示滚动条,如果内容没有溢出,就不显示滚动条。
上面的四种可取值种,最能满足我们要求的是hidden。
首先将图片放到容器中。
设置容器的显示样式。
//容器的id为sildeshow
#slideshow{
width: 100px;
height: 100px;
position: relative;
overflow: hidden;
}
JS
上面的代码现在可以只显示一部分图片。如果根据用户正把鼠标指针悬停在哪个链接上,我们需要将这个图片向左或者向右移动。我们需要把moveElement函数的行为与链接清单里每个链接的onmouseover事件关联起来。
function prepareSlideshow() {
// 确定浏览器支持DOM方法
if(!document.getElementsByTagName)return false;
if(!document.getElementById)return false;
//确保元素存在
if(!document.getElementById("linklist"))return false;
if(!document.getElementById("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");
//为mouseover事件添加动画效果
links[0].onmouseover=function(){
moveElement("preview",-100,0,10);
}
links[1].onmouseover=function(){
moveElement("preview",-200,0,10);
}
links[2].onmouseover=function(){
moveElement("preview",-300,0,10);
}
}
addLoadEvent(prepareSlideshow);
上面的实现根据鼠标指针悬停在哪个链接上,placeholder.gif图片的不同部分就会进入我们的视线。不过,如果把鼠标指针在链接之间快速来回移动,动画效果将变得混乱起来。
变量作用域问题
动画效果不正确的问题是由一个全局变量引起的,把moveMessage函数抽象化为moveElement函数过程中,我们未对变量movement做任何修改。
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 xpos=parseInt(elem.style.left);
var ypos=parseInt(elem.style.top);
if(xpos==final_x&&ypos==final_y){
return true;
}
if(xpos<final_x){
xpos++;
}
if(xpos>final_x){
xpos--;
}
if(ypos<final_y){
ypos++;
}
if(ypos>final_y){
ypos--;
}
elem.style.left=xpos+"px";
elem.style.top=ypos+"px";
var repeat="moveElement('"+elementID+"',"+final_x+","+final_y+","+interval+")";
movement=setTimeout(repeat,interval);
这留下了一个隐患。每当用户把鼠标指针悬停在某个链接上,不管上一次调用是否已经把图片移动到位,moveElement函数都会被再次调用并试图把这个图片移动到另一个地方去。如果用户移动鼠标速度够快,积累在setTimeout队列里的事件就会导致动画效果产生滞后。为了消除动画滞后的现象,可以用clearTimeout函数清除积累在setTimeout队列里的事件。
clearTimeout(movement);
PS:这个clearTimeout应该放在setTimeout函数之前调用,但是有个问题,movement还没有声明的话(没有声明就是全局),执行clearTimeout函数会出错。如果在这之后声明,明显又不行。
JS允许我们为元素创建属性。element.property=value。它很像在创建一个变量,但区别是这个变量专属于某个特定的元素。
修改代码如下:
function moveElement(elementID,final_x,final_y,interval) {
if(!document.getElementById)return false;
if(!document.getElementById(elementID))return false;
var elem=document.getElementById(elementID);
if(elem.movement){
clearTimeout(elem.movement);
}
var xpos=parseInt(elem.style.left);
var ypos=parseInt(elem.style.top);
if(xpos==final_x&&ypos==final_y){
return true;
}
if(xpos<final_x){
xpos++;
}
if(xpos>final_x){
xpos--;
}
if(ypos<final_y){
ypos++;
}
if(ypos>final_y){
ypos--;
}
elem.style.left=xpos+"px";
elem.style.top=ypos+"px";
var repeat="moveElement('"+elementID+"',"+final_x+","+final_y+","+interval+")";
elem.movement=setTimeout(repeat,interval);
}
改进动画效果
之前动画的移动是每次移动1px,移动速度有点慢,改进效果是:如果那个元素与它的目的地距离较远,就让它每次前进一大步;如果那个与目的地juli较近,就让它每次前进一小步。
具体步骤如下:
- 首先计算出元素与它的目的地之间的距离。
- 然后让每个元素每次前进这个距离的1/10。
需要注意的问题是:当元素离目的地不到10的时候,除以10将小于1,而我们不可能把一个元素移动不到一个像素的距离。这个问题可以用Math对象的ceil方法来解决。它可以返回一个不小于 number值的整数。语法是Math.ceil(number)
改进后的代码如下:
function moveElement(elementID,final_x,final_y,interval) {
if(!document.getElementById)return false;
if(!document.getElementById(elementID))return false;
var elem=document.getElementById(elementID);
if(elem.movement){
clearTimeout(elem.movement);
}
var xpos=parseInt(elem.style.left);
var ypos=parseInt(elem.style.top);
var dist=0;
if(xpos==final_x&&ypos==final_y){
return true;
}
if(xpos<final_x){
dist=Math.ceil((final_x-xpos)/10);
xpos=xpos+dist;
}
if(xpos>final_x){
dist=Math.ceil((xpos-final_x)/10);
xpos=xpos-dist;
}
if(ypos<final_y){
dist=Math.ceil((final_y-ypos)/10);
ypos=ypos+dist;
}
if(ypos>final_y){
dist=Math.ceil((ypos-final_y)/10);
ypos=ypos-dist;
}
elem.style.left=xpos+"px";
elem.style.top=ypos+"px";
var repeat="moveElement('"+elementID+"',"+final_x+","+final_y+","+interval+")";
elem.movement=setTimeout(repeat,interval);
}
addLoadEvent(positionMessage);
添加安全检查
moveElement函数表现很好,但是函数开头部分需要一个假设,
var xpos=parseInt(elem.style.left);
var ypos=parseInt(elem.style.top);
所以我们可以在函数开头添加这样一个判断。
if(!elem.style.left||!elem.style.top){
return false;
}
或者没有值的话,我进行一个赋值。
if(!elem.style.left){
elem.style.left="0px";
}
if(!elem.style.top){
elem.style.top="0px";
}
生成HTML标记
list.html文档里包含一些只是为了能够用JS代码实现动画效果而存在的标记。
<div id="slideshow">
![](images/placeholder.gif)
</div>
如果用户没有启动JavaScript,以上的内容未免太多余了。与其将这些元素硬编码在文档里,不如用JS代码来生成它们。
最终代码如下:
function prepareSlideshow() {
// 确定浏览器支持DOM方法
if(!document.getElementsByTagName)return false;
if(!document.getElementById)return false;
//确保元素存在
if(!document.getElementById("linklist"))return false;
if(!document.getElementById("preview"))return false;
var slideshow=document.createElement("div");
slideshow.setAttribute("id","slideshow");
var preview=document.createElement("img");
preview.setAttribute("src","images/placeholder.gif");
preview.setAttribute("alt","building blocks of web design");
preview.setAttribute("id","preview");
slideshow.appendChild(preview);
//取得列表中的所有链接
var list=document.getElementById("linklist");
insertAfter(slideshow,list);
var links=list.getElementsByTagName("a");
//为mouseover事件添加动画效果
links[0].onmouseover=function(){
moveElement("preview",-100,0,10);
}
links[1].onmouseover=function(){
moveElement("preview",-200,0,10);
}
links[2].onmouseover=function(){
moveElement("preview",-300,0,10);
}
}
addLoadEvent(prepareSlideshow);
效果图如下所示: