【JavaScript从入门到精通】第十九课

131 阅读4分钟

第十九课-JS运动中级


前面的学习已经让我们掌握了很多运动相关的知识,也构造出了自己的运动框架,当然,它离真正的完成版运动框架还有很远的距离。


链式运动框架


在我们讲解链式运动框架时,我们需要讲解一下什么是回调函数。在我们之前的一个运动函数startMove中,如果我们再添加一个参数,而且该参数是一个函数,我们希望在运动结束后调用这个函数——这就是所谓的回调函数。

function startMove(obj, attr, iTarget, fnEnd) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        var cur = 0;

        if (attr == 'opacity') {
            cur = Math.round(parseFloat(getStyle(obj, attr)) * 100);
        } else {
            cur = parseInt(getStyle(obj, attr));
        }

        var speed = (iTarget - cur) / 6;
        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

        if (cur == iTarget) {
            clearInterval(obj.timer);

            if (fnEnd) fnEnd();
        } else {
            if (attr == 'opacity') {
                obj.style.filter = 'alpha(opacity:' + (cur + speed) + ')';
                obj.style.opacity = (cur + speed) / 100;
            } else {
                obj.style[attr] = cur + speed + 'px';
            }
        }
    },
    30);
};

在clearInterval之后,我们调用这个函数(当然这里需要判断一下函数是否被传入)。

<html>
 <head> 
  <meta charset="utf-8" /> 
  <title>无标题文档</title> 
  <style>
#div1 {width:100px; height:100px; background:red; filter:alpha(opacity:30); opacity:0.3;}
</style> 
  <script src="move.js"></script> 
  <script>
window.onload=function ()
{
    var oDiv=document.getElementById('div1');
 
    oDiv.onmouseover=function ()
    {
        startMove(oDiv, 'width', 300, function (){
            startMove(oDiv, 'height', 300, function (){
                startMove(oDiv, 'opacity', 100);
            });
        });
    };
 
    oDiv.onmouseout=function ()
    {
        startMove(oDiv, 'opacity', 30, function (){
            startMove(oDiv, 'height', 100, function (){
                startMove(oDiv, 'width', 100);
            });
        });
    };
};
</script> 
 </head> 
 <body> 
  <div id="div1"></div>  
 </body>
</html>

效果如下:



可以看到,我们通过回调函数的嵌套实现了一个比较炫酷的伸缩展开效果——这就是一个简单的链式运动框架。


完美运动框架


到目前为止,我们学习的运动框架依然是有问题存在的:

<html>
 <head> 
  <meta charset="utf-8" /> 
  <title>无标题文档</title> 
  <style>
#div1 {width:100px; height:100px; background:red;}
</style> 
  <script src="move.js"></script> 
  <script>
window.onload=function ()
{
    var oBtn=document.getElementById('btn1');
    var oDiv=document.getElementById('div1');
 
    oBtn.onclick=function ()
    {
        startMove(oDiv, 'width', 300);
        startMove(oDiv, 'height', 300);
    };
};
</script> 
 </head> 
 <body> 
  <input id="btn1" type="button" value="运动" /> 
  <div id="div1"></div>  
 </body>
</html>

效果如下:



可以看到,我们试图让div的宽和高同时变化,但结果而言只有高度发生了变化,原因在于两次调用函数的定时器产生了干扰,因此只有后一个startmove函数生效了。那么,我们现在的运动框架无法使好几个属性同时发生变化,应该怎么解决它呢?答案是通过json,json有一个重要用法是循环——使用for in方法进行。我们之前传入属性和属性值是通过两个参数进行的,现在我们直接传入一个json,将属性和属性值分别作为键名和键值传入,这样我们就可以同时传入好几组值了。

function startMove(obj, json, fnEnd) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        var bStop = true; //假设:所有值都已经到了
        for (var attr in json) {
            var cur = 0;

            if (attr == 'opacity') {
                cur = Math.round(parseFloat(getStyle(obj, attr)) * 100);
            } else {
                cur = parseInt(getStyle(obj, attr));
            }

            var speed = (json[attr] - cur) / 6;
            speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

            if (cur != json[attr]) bStop = false;

            if (attr == 'opacity') {
                obj.style.filter = 'alpha(opacity:' + (cur + speed) + ')';
                obj.style.opacity = (cur + speed) / 100;
            } else {
                obj.style[attr] = cur + speed + 'px';
            }
        }

        if (bStop) {
            clearInterval(obj.timer);

            if (fnEnd) fnEnd();
        }
    },
    30);
}

通过json键值对和for in循环,我们就可以同时改变一个元素的好几个属性了。这里注意一点,原本的运动框架当属性值等于目标值时,运动就会停下来,但同时运动的时候几个运动结束的时间并不是一样的——我们应该等所有运动都结束之后再关闭定时器,因此我们建立了一个bStop变量来判断是否所有运动都到达了终点。

<html>
 <head> 
  <meta charset="utf-8" /> 
  <title>无标题文档</title> 
  <style>
#div1 {width:100px; height:100px; background:red; filter:alpha(opacity:30); opacity:0.3;}
</style> 
  <script src="move2.js"></script> 
  <script>
window.onload=function ()
{
    var oBtn=document.getElementById('btn1');
    var oDiv=document.getElementById('div1');
 
    oBtn.onclick=function ()
    {
        startMove(oDiv, {width:101, height: 300, opacity: 100}, function (){
            alert('a');
        });
    };
};
</script> 
 </head> 
 <body> 
  <input id="btn1" type="button" value="运动" /> 
  <div id="div1"></div>  
 </body>
</html>

效果如下:



这样,这个框架既可以同时改变元素的不同属性值(通过json实现),也可以分阶段进行属性值改变(通过回调函数实现),就形成了一个比较完美的运动框架,在css2范围内,这个运动框架已经足够使用了。

到这里我们的运动框架基本就已经讲解结束了,这里为大家总结一下我们编写过的运动框架:

  • startMove(iTarget) 运动框架
  • startMove(obj, iTarget) 多物体
  • startMove(obj, attr, iTarget) 任意值
  • startMove(obj, attr, iTarget, fn) 链式运动
  • startMove(obj, json) 多值运动
  • startMove(obj, json, fn) 完美运动框架