JavaScript DOM笔记06

用JavaScript实现动画效果

系列文章

动画基础知识

要实现动画效果,就需要通过JavaScript控制元素的位置或形变,这点可以通过元素的 element.style.left 等样式的改变来实现。

动画效果应该需要在特定的时间触发。函数 setTimeout(function, interval) 能够让某个函数在经过一段预定的时间之后才开始执行,它的第一个参数是将要执行函数对象;第二个参数是一个以毫秒为单位的数值,设定需要经过多长时间后才开始执行第一个参数给出的函数。

该函数返回一个定时器编号。如果要取消某个正在排队等待执行的函数,可以将该编号传给 clearTimeout() 函数内。

以下代码在一秒后改变特点元素的位置,并且可以通过点击元素取消位置改变:

var elem = document.getElementById("entry");
function showMessage() {
    elem.style.left = "50px";
    elem.style.top = "100px";
}
var movement = setTimeout(showMessage, 1000);
elem.onclick = function() { clearTimeout(movement); }

以上形式的动画是一种突变的效果。不过可以通过多次微小的改变实现渐变效果。其思路为:

  1. 获取元素当前位置;
  2. 如果元素已经到达目的地,退出动画;
  3. 否则,把它向目的地移动一点;
  4. 经过短暂停顿,重复以上步骤。

函数 parseInt(string) 可以把字符串中的数值信息提取出来。如果把一个以数字开头的字符串传递给这个函数,它将返回对应数字。例如 parseInt("20px;") 将返回数值 20

该函数返回的是整数,如果想类似地提取小数,可以使用相应的 parseFloat()

经过以上分析,相应的渐变动画代码为:

var elem = document.getElementById("entry");
elem.style.left = "0";
elem.style.top = "0";
function showMessage() {
    var posx = parseInt(elem.style.left);
    var posy = parseInt(elem.style.top);
    if (posx == 200 && posy == 100) return true;
    if (posx < 200)      posx++;
    else if (posx > 200) posx--;
    if (posy < 100)      posy++;
    else if (posy > 100) posy--;
    elem.style.left = posx + "px";
    elem.style.top = posy + "px";
    movement = setTimeout(showMessage, 10);
}
showMessage();

注意单次动画是如何重复与结束的。其表现效果为:

animation

以下封装该函数,方便不同项目重复调用,完整代码为:

function moveElement(elem, finalx, finaly, interval) {
    if (elem.movement)
        clearTimeout(elem.movement);
    var posx = parseInt(elem.style.left);
    var posy = parseInt(elem.style.top);
    if (posx == finalx && posy == finaly)
        return true;
    if (posx < finalx)      posx++;
    else if (posx > finalx) posx--;
    if (posy < finaly)      posy++;
    else if (posy > finaly) posy--;
    elem.style.left = posx + "px";
    elem.style.top = posy + "px";
    elem.movement = setTimeout(function () {
        moveElement(elem, finalx, finaly, interval);
    }, interval);
}

改进动画效果

以上动画存在的问题是它只能水平竖直或斜45°移动,不够自然。可以对其进行改进,让它每一次移动还剩下距离的十分之一:

dist = (finalx - posx) / 10;
posx = posx + dist;

这样做的问题是当位置与目的相差不到10个像素时,不可能将元素移动不到一个像素的距离。这个问题可以使用 Math.ceil(number) 对象方法解决,它返回不小于 number 的下一个整数。

与此对应的 .floor() 方法可以把任意浮点数向更小的方向舍入为与之最接近的整数,.round() 方法把任意浮点数舍入为与之最为接近的整数。

以下是经过改进后的部分代码:

var dist = 0;
if (posx < finalx) {
    dist = Math.ceil((finalx - posx) / 10);
    posx += dist;
}
else if (posx > finalx) {
    dist = Math.ceil((posx - finalx) / 10);
    posx -= dist;
}

除此之外,还需要注意在运行前先要对元素进行检查,确保其有可以解析的 top 等属性。