zoukankan      html  css  js  c++  java
  • js运动框架逐渐递进版

    运动,其实就是在一段时间内改变left、right、width、height、opactiy的值,到达目的地之后停止。

    现在按照以下步骤来进行我们的运动框架的封装:

    1. 匀速运动。
    2. 缓冲运动。
    3. 多物体运动。
    4. 任意值变化。
    5. 链式运动。
    6. 同时运动

    (第一部分):匀速运动

    运动基础

    思考:如何让div动起来?
    如下:

    1. 设置元素为绝对定位,只有绝对定位后,left,top等值才生效。
    2. 定时器的使用(动态改变值),这里使用setInterval()每隔指定的时间执行代码。
    • 计时器setInterval(函数,交互时间(毫秒)):在执行时,从载入页面后每隔指定的时间执行代码。
    • 取消计时器clearInterval(函数) 方法可取消由 setInterval() 设置的交互时间。
    1. 获取当前的位置,大小等等。offsetLeft(当前元素相对父元素位置)获取左边距。
    2. 速度–控制物体运动的快慢
    • 定时器间隔时间
    • 改变值的大小

    根据上面的信息我们就可以开始封装运动框架创建一个变化的div了。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    /**

     * 运动框架-1-动起来

     * @param {HTMLElement} element 进行运动的节点

     */

    var timer = null;

    function startMove(element) {

        timer = setInterval(function () {//定时器

            element.style.left = element.offsetLeft + 5 + "px";

        }, 30);

    }

     

     

    例如:window.onload = function()

    {  

    var oBtn = document.getElementById('btn');  //元素的获取

        oBtn.onclick = function()

    {

     var oDiv = document.getElementById('div1');  //设置定时器  

            setInterval(function()

        {  //改变物体位置  

                oDiv.style.left = oDiv.offsetLeft + 10 + 'px';  

            },30)  

        }  

    }  

    你没看错,就是那么简单。但是等等, what? 怎么不会停?

    那是因为我们没有运动终止条件。好再还是比较简单。直接在定时器内部,判断到达目标值,清除定时器就行啦!

    判断很重要

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    /**

     * 运动框架-2-运动终止

     * @param {HTMLElement} element 进行运动的节点

     * @param {number}      iTarget 运动终止条件。

     */

     var timer = null;

    function startMove(element, iTarget) {

        timer = setInterval(function () {

            element.style.left = element.offsetLeft + 5 + "px";

            if (element.offsetLeft === iTarget) {//停止条件

                clearInterval(timer);

            }

        }, 30);

    }

    例如:window.onload = function()

    {  

    var timer=null;

    var oBtn = document.getElementById('btn');  

        oBtn.onclick = function()

    {

     var oDiv = document.getElementById('div1');  //设置定时器

     var timer = setInterval(function()

    {  //判断停止条件

          if(oDiv.offsetLeft > 300)

        {  

                    clearInterval(timer);  

                }

        else{  //改变物体位置  

                    oDiv.style.left = oDiv.offsetLeft + 10 + 'px';  

                    document.title = oDiv.offsetLeft;  //显示当前的边界值

                }  

           },30);                                                                                                                                                                                                                  }  

    }  

    就这样是不是就完成了呢?已经ok了呢?
    no。还有一些Bug需要处理。

    运动中的Bug

    1. 速度取到某些值会无法停止
    2. 到达位置后再点击还会运动
    3. 重复点击速度加快
    4. 速度无法更改

    解决BUG

        1. 速度取到某些值会无法停止(这个Bug稍后解决,在进化过程中自然解决)
        2. 把运动和停止隔开(if/else)
        3. 在开始运动时,关闭已有定时器
        4. 把速度用变量保存
        5.  运动框架-3-解决Bug
        6. var timer = null;

          function startMove(element, iTarget) {

              clearInterval(timer);//在开始运动时,关闭已有定时器

              timer = setInterval(function () {

                  var iSpeed = 5;//把速度用变量保存

                  //把运动和停止隔开(if/else)

                  if (element.offsetLeft === iTarget) {//结束运动

                      clearInterval(timer);

                  } else {

                      element.style.left = element.offsetLeft + iSpeed + "px";

                  }

              }, 30);

        7. 例如:
     
      
    1. var timer = null;  
    2. function startMove(){  
    3.     var oDiv = document.getElementById('div1');  
    4.     clearInterval(timer);  
    5.         //计算速度  
    6.     var iSpeed = 10;  
    7.     //设置定时器  
    8.     timer = setInterval(function(){  
    9.         //判断停止条件  
    10.         if(oDiv.offsetLeft > 300){  
    11.             clearInterval(timer);  
    12.         }else{  
    13.             //改变物体位置  
    14.             oDiv.style.left = oDiv.offsetLeft + iSpeed + 'px';  
    15.             document.title = oDiv.offsetLeft;  
    16.         }  
    17.     },30);  
    18. }
           

    这样一个简单的运动框架就完成了。但是,再等等。只能向右走?别急,我们不是定义了把速度变成为了变量吗?只需要对它进行一些处理就行啦!
    var iSpeed = 5;–>

    1

    2

    3

    4

    5

    6

    7

    //判断距离目标位置,达到自动变化速度正负

    var iSpeed = 0;

    if (element.offsetLeft < iTarget) {

        iSpeed = 5;

    } else {

        iSpeed = -5;

    }

    例如:

    1. window.onload = function(){  
    2.     var oDiv = document.getElementById('div1');  
    3.     oDiv.onmouseover = function(){  
    4.         startMove(0);  
    5.     }  
    6.     oDiv.onmouseout = function(){  
    7.         startMove(-100);  
    8.     }  
    9. }  
    10. var timer = null;  
    11. var iSpeed;  
    12. function startMove(iTatget){  
    13.     var oDiv = document.getElementById('div1');  
    14.     clearInterval(timer);  
    15.     timer = setInterval(function(){  
    16.         //计算速度  
    17.         if(iTatget -oDiv.offsetLeft > 0){  
    18.             iSpeed = 10;  
    19.         }else{  
    20.             iSpeed = -10;  
    21.         }  
    22.                                                                                                                                                                                                                                
    23.         if(oDiv.offsetLeft == iTatget){  
    24.             clearInterval(timer);  
    25.         }else{  
    26.             oDiv.style.left = oDiv.offsetLeft + iSpeed + 'px';  
    27.         }  
    28.         document.title = oDiv.offsetLeft;  
    29.     },30)  
    30. }

    透明度动画

    1. 用变量alpha储存当前透明度。
    2. 把上面的element.offsetLeft改成变量alpha。
    3. 运动和停止条件部分进行更改。如下:

    1

    2

    3

    4

    5

    6

    7

    8

    //透明度浏览器兼容实现

    if (alpha === iTarget) {

        clearInterval(time);

    } else {

        alpha += speed;

        element.style.filter = 'alpha(opacity:' + alpha + ')'; //兼容IE

        element.style.opacity = alpha / 100;//标准

    }

    例如:

    1. window.onload = function(){  
    2.     var oImg = document.getElementById('img1');  
    3.     oImg.onmouseover = function(){  
    4.         startMove(100);  
    5.     }  
    6.     oImg.onmouseout = function(){  
    7.         startMove(30);  
    8.     }  
    9. }  
    10. var timer = null;  
    11. //保存透明度的数字值  
    12. var alpha = 30;  
    13. function startMove(iTarget){  
    14.     var oDiv = document.getElementById('img1');  
    15.     clearInterval(timer);  
    16.     timer = setInterval(function(){  
    17.         var iSpeed = 0;  
    18.         if(alpha > iTarget){  
    19.             iSpeed = -1;  
    20.         }else{  
    21.             iSpeed = 1;  
    22.         }  
    23.         if(alpha == iTarget){  
    24.             clearInterval(timer);  
    25.         }else{  
    26.                         //改变透明度速度值  
    27.             alpha += iSpeed;  
    28.             oDiv.style.filter = 'alpha(opacity:'+ alpha+')';  
    29.             oDiv.style.opacity = alpha/100;  
    30.             document.title = alpha;  
    31.         }  
    32.                                                                                                                                                                                                          
    33.     },30)  
    34. }  

    (二)缓冲动画

    思考:怎么样才是缓冲动画?

    应该有以下几点:

    • 逐渐变慢,最后停止
    • 距离越远速度越大
      • 速度由距离决定
      • 速度=(目标值-当前值)/缩放系数
    • Bug :速度取整(使用Math方法),不然会闪
      • 向上取整。Math.ceil(iSpeed)
      • 向下取整。Math.floor(iSpeed)

    还是对速度作文章:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    /**

     * 运动框架-4-缓冲动画

     */

    function startMove(element, iTarget) {

        clearInterval(timer);

        timer = setInterval(function () {

        //因为速度要动态改变,所以必须放在定时器中

            var iSpeed = (iTarget - element.offsetLeft) / 10; //(目标值-当前值)/缩放系数=速度

            iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整

            if (element.offsetLeft === iTarget) {//结束运动

                clearInterval(timer);

            } else {

                element.style.left = element.offsetLeft + iSpeed + "px";

            }

        }, 30);

    }

    • 做到这里,(速度取到某些值会无法停止)这个Bug就自动解决啦!
    • 例子:缓冲菜单
    • 跟随页面滚动的缓冲侧边栏

    潜在问题目标值不是整数时

    例如:

    1. window.onload = function(){  
    2.     var btn = document.getElementsByTagName('input')[0];  
    3.     btn.onclick = function(){  
    4.         startMove(300);  
    5.     }  
    6. }  
    7. var timer = null;  
    8. function startMove(iTarget){  
    9.     var oDiv = document.getElementById('div1');  
    10.     clearInterval(timer);  
    11.     timer = setInterval(function(){  
    12.         //求出带有变化的速度   
    13.         var iSpeed = (iTarget - oDiv.offsetLeft) / 8;  
    14.         if(oDiv.offsetLeft == iTarget){  
    15.             clearInterval(timer);  
    16.         }else{  
    17.             oDiv.style.left = oDiv.offsetLeft + iSpeed + 'px';  
    18.         }  
    19.         document.title = oDiv.offsetLeft + '...' + iSpeed;  
    20.     },30);  

    上述方法可以得到缓冲运动,但是实际运行效果,物体并没有在 300的位置停掉,而是在 293的位置就停掉了。究其原因。因为当物体的速度小于1 的时候。物体位置为293。此时计算的速度是 0.875.通过 oDiv.style.left = oDiv.offsetLeft + iSpeed + 'px'; 物体的位置为 293.875.可是计算机不能识别小数,将小数省略了。此时的 位置offsetLeft仍然是 293.再计算一次,还是同样的结果。定时器没有关掉,但是物体的位置却再也无法改变,故停留在了 293的位置。解决方案,就是将速度进行向上取整。但是,像上述运动,速度是正的,可是,当速度是负的时候,就同样会有相同的结果,因此需要在速度为负的时候,向下取整。

    1. function startMove(iTarget){  
    2.     var oDiv = document.getElementById('div1');  
    3.     clearInterval(timer);  
    4.     timer = setInterval(function(){  
    5.         var iSpeed = (iTarget - oDiv.offsetLeft) / 8;  
    6.         //对正的速度向上取整,负的速度向下取整  
    7.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    8.         if(oDiv.offsetLeft == iTarget){  
    9.             clearInterval(timer);  
    10.         }else{  
    11.             oDiv.style.left = oDiv.offsetLeft + iSpeed + 'px';  
    12.         }  
    13.         document.title = oDiv.offsetLeft + '...' + iSpeed;  
    14.     },30);  
    15. }  

    (三)多物体运动

    思考:如何实现多物体运动?

    • 单定时器,存在问题。每个div一个定时器
    • 定时器作为对象的属性
      • 直接使用element.timer把定时器变成对象上的一个属性。
    • 参数的传递:物体/目标值
      比较简单把上面框架的进行如下更改:timer–>element.timer

    处理多物体运动,运动函数里面每次都要选取一个元素加事件。如果需要对多个物体进行同样的运动, 需要将运动对象作为参数传进来。

    1. window.onload = function(){  
    2.     var aDiv = document.getElementsByTagName('div');  
    3.     for(var i=0;i<aDiv.length;i++){  
    4.         aDiv[i].onmouseover = function(){  
    5.             startMove(this,300);  
    6.         }  
    7.         aDiv[i].onmouseout = function(){  
    8.             startMove(this,100);  
    9.         }  
    10.     }  
    11. }  
    12. var timer = null;  
    13. function startMove(obj,iTarget){  
    14.     clearInterval(timer);  
    15.     timer = setInterval(function(){  
    16.         var iSpeed = (iTarget - obj.offsetWidth) / 8;  
    17.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    18.         if(obj.offsetWidth == iTarget){  
    19.             clearInterval(timer);  
    20.         }else{  
    21.             obj.style.width = obj.offsetWidth + iSpeed + 'px';  
    22.         }  
    23.                                                                                                                                                          
    24.     },30)  
    25. }  
    26. 通过循环物体,将物体的 this传给运动函数,使得多物体可以运动。但是这样有一个弊端,即当滑入第一个运动的时候,开启了定时器。如果此时,滑入另外一个物体,将会清理上一个定时器。这就造成了,上一次运动,很有可能还没完成结束,定时器就没关闭了。解决的方法,每个运动的物体,都能开了一个属于自己的定时器。因此,把定时器当成物体的属性。清理的时候也就是清理自己的定时器
    1. window.onload = function(){  
    2.     var aDiv = document.getElementsByTagName('div');  
    3.     for(var i=0;i<aDiv.length;i++){  
    4.         aDiv[i].onmouseover = function(){  
    5.             startMove(this,300);  
    6.         }  
    7.         aDiv[i].onmouseout = function(){  
    8.             startMove(this,100);  
    9.         }  
    10.     }  
    11. }  
    12. function startMove(obj,iTarget){  
    13.     //将定时器,变成物体的属性,类似给物体添加索引  
    14.     clearInterval(obj.timer);  
    15.     obj.timer = setInterval(function(){  
    16.         var iSpeed = (iTarget - obj.offsetWidth) / 8;  
    17.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    18.         if(obj.offsetWidth == iTarget){  
    19.             clearInterval(obj.timer);  
    20.         }else{  
    21.             obj.style.width = obj.offsetWidth + iSpeed + 'px';  
    22.         }  
    23.                                                                                                                                              
    24.     },30)  
    25. }  

    (四)任意值变化

    咳咳。我们来给div加个1px的边框。boder :1px solid #000

    然后来试试下面的代码

    1

    2

    3

    setInterval(function () {

        oDiv.style.width = oDiv.offsetWidth - 1 + "px";

    }, 30)

    嗯,神奇的事情发生了!what?我设置的不是宽度在减吗?怎么尼玛增加了! 不对啊,大兄弟。

    究竟哪里出了问题呢?

    一起找找资料,看看文档,原来offset这一系列的属性都会存在,被其他属性干扰的问题。

    好吧,既然不能用,那么我们就顺便把任意值变化给做了吧。

    第一步:获取实际样式

    使用offsetLeft..等获取样式时, 若设置了边框, padding, 等可以改变元素宽度高度的属性时会出现BUG..

    • 通过查找发现element.currentStyle(attr)可以获取计算过之后的属性。
    • 但是因为兼容性的问题,需封装getStyle函数。(万恶的IE)
    • 当然配合CSS的box-sizing属性设为border-box可以达到一样的效果 ? (自认为,未验证)。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    /**

     * 获取实际样式函数

     * @param   {HTMLElement}   element  需要寻找的样式的html节点

     * @param   {String]} attr 在对象中寻找的样式属性

     * @returns {String} 获取到的属性

     */

    function getStyle(element, attr) {

        //IE写法

        if (element.currentStyle) {

            return element.currentStyle[attr];

        //标准

        } else {

            return getComputedStyle(element, false)[attr];

        }

    }

    第二步:改造原函数

    1. 添加参数,attr表示需要改变的属性值。
    2. 更改element.offsetLeft为getStyle(element, attr)。
    • 需要注意的是:getStyle(element, attr)不能直接使用,因为它获取到的字符串,例:10px。
    • 变量iCurrent使用parseInt(),将样式转成数字。
    1. element.style.left为element.style[attr]。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    /**

     * 运动框架-4-任意值变化

     * @param {HTMLElement} element 运动对象

     * @param {string}      attr    需要改变的属性。

     * @param {number}      iTarget 目标值

     */

    function startMove(element, attr, iTarget) {

        clearInterval(element.timer);

        element.timer = setInterval(function () {

            //因为速度要动态改变,所以必须放在定时器中

        var iCurrent=0;

        iCurrent = parseInt(getStyle(element, attr));//实际样式大小

            var iSpeed = (iTarget - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度

            iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整

            if (iCurrent === iTarget) {//结束运动

                clearInterval(element.timer);

            } else {

                element.style[attr] = iCurrent + iSpeed + "px";

            }

        }, 30);

    }

    试一试,这样是不是就可以了呢?

    还记得上面我们写的透明度变化吗? 再试试

    果然还是不行, (废话,你见过透明度有”px”单位的么? - -白眼 )

    第三步:透明度兼容处理

    思考:需要对那些属性进行修改?

    1. 判断attr是不是透明度属性opacity 。
    2. 对于速度进行处理。
    • 为透明度时,由于获取到的透明度会是小数,所以需要 * 100
    • 并且由于计算机储存浮点数的问题,还需要将小数,进行四舍五入为整数。使用: Math.round(parseFloat(getStyle(element, attr)) * 100)。
    • 否则,继续使用默认的速度。
    1. 对结果输出部分进行更改。
    • 判断是透明度属性,使用透明度方法
    • 否则,使用使用默认的输出格式。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    /**

     * 运动框架-5-兼容透明度

     * @param {HTMLElement} element 运动对象

     * @param {string}      attr    需要改变的属性。

     * @param {number}      iTarget 目标值

     */

    function startMove(element, attr, iTarget) {

        clearInterval(element.timer);

        element.timer = setInterval(function () {

            //因为速度要动态改变,所以必须放在定时器中

            var iCurrent = 0;

            if (attr === "opacity") { //为透明度时执行。

                iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100);

            } else { //默认情况

                iCurrent = parseInt(getStyle(element, attr)); //实际样式大小

            }

            var iSpeed = (iTarget - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度

            iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整

            if (iCurrent === iTarget) {//结束运动

                clearInterval(element.timer);

            } else {

                if (attr === "opacity") { //为透明度时,执行

                    element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE

                    element.style.opacity = (iCurrent + iSpeed) / 100; //标准

                } else { //默认

                    element.style[attr] = iCurrent + iSpeed + "px";

                }

            }

        }, 30);

    }

    到这里,这个运动框架就基本上完成了。但是,我们是追求完美的不是吗?

    继续进化!

    多物体的淡入淡出的时候,也有类似的问题。因为修改透明度的时候,是先用一个变量保存透明度,必须针对每个物体设立透明度值属性。

    1. window.onload = function(){  
    2.     var aDiv = document.getElementsByTagName('div');  
    3.     for(var i=0;i<aDiv.length;i++){  
    4.         //将透明度值当初属性  
    5.         aDiv[i].alpha = 30;  
    6.         aDiv[i].onmouseover = function(){  
    7.             startMove(this,100);  
    8.         }  
    9.         aDiv[i].onmouseout = function(){  
    10.             startMove(this,30);  
    11.         }  
    12.     }  
    13. }  
    14. function startMove(obj,iTarget){      
    15.     clearInterval(obj.timer);  
    16.     obj.timer = setInterval(function(){  
    17.         var iSpeed = (iTarget - obj.alpha) / 8;  
    18.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    19.         if(obj.alpha == iTarget){  
    20.             clearInterval(obj.timer);  
    21.         }else{  
    22.             obj.alpha += iSpeed;  
    23.             obj.style.filter = 'alpha(opacity:'+obj.alpha+')';  
    24.             obj.style.opacity = obj.alpha / 100;  
    25.         }  
    26.         document.title = obj.alpha;  
    27.     },30);  
    28. }  


    4.1  位置属性的bug

    offsetWidth 或者 offsetHeight 等位置属性,一旦给他们加上 border。则会有诡异的现象出现。

    1. window.onload = function(){  
    2.         var oDiv = document.getElementById('div1');  
    3.         setInterval(function(){  
    4.             oDiv.style.width = oDiv.offsetWidth - 1 + "px";  
    5.         },30)  
    6.     }  


    例如 oDiv.style.width = oDiv.offsetWidth - 1 + 'px'; 如果给 oDiv 的width 为一百,border 为 1.则这个物体的 width是100px;offsetWidth 为102px;带入公式之后,即减一之后。100 = 102 - 1 ,反而等于101;即 物体本来要减小,事实却增大了。解决的方案就是,加减的时候,必须使用物体的内联样式。但是 火狐 和 IE 又有兼容模式。解决方案如下:

    1. window.onload = function(){  
    2.         var oDiv = document.getElementById('div1');  
    3.         setInterval(function(){  
    4.                                                                                                                    
    5.             oDiv.style.width = parseInt(getStyle(oDiv,'width')) - 1 + 'px';  
    6.                                                                                                         
    7.         },30)  
    8.     }  
    9.     function getStyle(obj,attr){  
    10.         if(obj.currentStyle){  
    11.             return obj.currentStyle[attr];  
    12.         }else{  
    13.             return getComputedStyle(obj,false)[attr];  
    14.         }  
    15.     }  


    其中,getStyle函数,传入一个元素对象,和其 css 属性,获取的是元素的样式,即 witdh 100px;因此需要parseInt转换

    5.任意值运动

    通过 getStyle 函数,可以获取元素的样式,还可也通过 attr 制定需要修改的 css属性。这样就能是物体有不同的运动形式。

    1. window.onload = function(){  
    2.     var aDiv = document.getElementsByTagName('div');  
    3.     aDiv[0].onmouseover = function(){  
    4.         startMove(this,'width',300);  
    5.     }  
    6.     aDiv[0].onmouseout = function(){  
    7.         startMove(this,'width',100);  
    8.     }  
    9.     aDiv[1].onmouseover = function(){  
    10.         startMove(this,'height',100);  
    11.     }  
    12.     aDiv[1].onmouseout = function(){  
    13.         startMove(this,'height',50);  
    14.     }  
    15. }  
    16. function getStyle(obj,attr){  
    17.     if(obj.currentStyle){  
    18.         return obj.currentStyle(attr);  
    19.     }else{  
    20.         return getComputedStyle(obj,false)[attr];  
    21.     }  
    22. }  
    23. function startMove(obj,attr,iTarget){  
    24.     clearInterval(obj.timer);  
    25.     obj.timer = setInterval(function(){  
    26.         var iCur = parseInt(getStyle(obj,attr));  
    27.         var iSpeed = (iTarget - iCur) / 8;  
    28.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    29.         if(iCur == iTarget){  
    30.             clearInterval(obj.timer);  
    31.         }else{  
    32.             obj.style[attr] = iCur + iSpeed + 'px';  
    33.         }  
    34.                                                                                                     
    35.     },30)  
    36. }  


    例如:

    多物体的淡入淡出的时候,也有类似的问题。因为修改透明度的时候,是先用一个变量保存透明度,必须针对每个物体设立透明度值属性

    1. window.onload = function(){  
    2.     var aDiv = document.getElementsByTagName('div');  
    3.     for(var i=0;i<aDiv.length;i++){  
    4.         //将透明度值当初属性  
    5.         aDiv[i].alpha = 30;  
    6.         aDiv[i].onmouseover = function(){  
    7.             startMove(this,100);  
    8.         }  
    9.         aDiv[i].onmouseout = function(){  
    10.             startMove(this,30);  
    11.         }  
    12.     }  
    13. }  
    14. function startMove(obj,iTarget){      
    15.     clearInterval(obj.timer);  
    16.     obj.timer = setInterval(function(){  
    17.         var iSpeed = (iTarget - obj.alpha) / 8;  
    18.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    19.         if(obj.alpha == iTarget){  
    20.             clearInterval(obj.timer);  
    21.         }else{  
    22.             obj.alpha += iSpeed;  
    23.             obj.style.filter = 'alpha(opacity:'+obj.alpha+')';  
    24.             obj.style.opacity = obj.alpha / 100;  
    25.         }  
    26.         document.title = obj.alpha;  
    27.     },30);  
    28. }  
    29. 位置属性的bug

    offsetWidth 或者 offsetHeight 等位置属性,一旦给他们加上 border。则会有诡异的现象出现。

    1. window.onload = function(){  
    2.         var oDiv = document.getElementById('div1');  
    3.         setInterval(function(){  
    4.             oDiv.style.width = oDiv.offsetWidth - 1 + "px";  
    5.         },30)  
    6.     }  
    7. 例如 oDiv.style.width = oDiv.offsetWidth - 1 + 'px'; 如果给 oDiv 的width 为一百,border 为 1.则这个物体的 width是100px;offsetWidth 为102px;带入公式之后,即减一之后。100 = 102 - 1 ,反而等于101;即 物体本来要减小,事实却增大了。解决的方案就是,加减的时候,必须使用物体的内联样式。但是 火狐 和 IE 又有兼容模式。解决方案如下:
    1. window.onload = function(){  
    2.         var oDiv = document.getElementById('div1');  
    3.         setInterval(function(){  
    4.                                                                                                                    
    5.             oDiv.style.width = parseInt(getStyle(oDiv,'width')) - 1 + 'px';  
    6.                                                                                                         
    7.         },30)  
    8.     }  
    9.     function getStyle(obj,attr){  
    10.         if(obj.currentStyle){  
    11.             return obj.currentStyle[attr];  
    12.         }else{  
    13.             return getComputedStyle(obj,false)[attr];  
    14.         }  
    15.     }  

    其中,getStyle函数,传入一个元素对象,和其 css 属性,获取的是元素的样式,即 witdh 100px;因此需要parseInt转换

    任意值运动

    通过 getStyle 函数,可以获取元素的样式,还可也通过 attr 制定需要修改的 css属性。这样就能是物体有不同的运动形式。

    1. window.onload = function(){  
    2.     var aDiv = document.getElementsByTagName('div');  
    3.     aDiv[0].onmouseover = function(){  
    4.         startMove(this,'width',300);  
    5.     }  
    6.     aDiv[0].onmouseout = function(){  
    7.         startMove(this,'width',100);  
    8.     }  
    9.     aDiv[1].onmouseover = function(){  
    10.         startMove(this,'height',100);  
    11.     }  
    12.     aDiv[1].onmouseout = function(){  
    13.         startMove(this,'height',50);  
    14.     }  
    15. }  
    16. function getStyle(obj,attr){  
    17.     if(obj.currentStyle){  
    18.         return obj.currentStyle(attr);  
    19.     }else{  
    20.         return getComputedStyle(obj,false)[attr];  
    21.     }  
    22. }  
    23. function startMove(obj,attr,iTarget){  
    24.     clearInterval(obj.timer);  
    25.     obj.timer = setInterval(function(){  
    26.         var iCur = parseInt(getStyle(obj,attr));  
    27.         var iSpeed = (iTarget - iCur) / 8;  
    28.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    29.         if(iCur == iTarget){  
    30.             clearInterval(obj.timer);  
    31.         }else{  
    32.             obj.style[attr] = iCur + iSpeed + 'px';  
    33.         }  
    34.                                                                                                     
    35.     },30)  
    36. }  

    任意值完美版

    上述版本,还不能处理透明度的任意值,因此需要增加额外的兼容hack。

    1. window.onload = function(){  
    2.         var aDiv = document.getElementsByTagName('div');  
    3.         aDiv[0].onmouseover = function(){  
    4.             startMove(this,'opacity',100);  
    5.         }  
    6.         aDiv[0].onmouseout = function(){  
    7.             startMove(this,'opacity',30);  
    8.         }  
    9.     }  
    10.     function getStyle(obj,attr){  
    11.         if(obj.currentStyle){  
    12.             return obj.currentStyleattr[attr];  
    13.         }else{  
    14.             return getComputedStyle(obj, false)[attr];  
    15.         }  
    16.     }  
    17.     function getStyle(obj, attr){  
    18.         if(obj.currentStyle)    {  
    19.             return obj.currentStyle[attr];  
    20.         }else{  
    21.             return getComputedStyle(obj, false)[attr];  
    22.         }  
    23.     }  
    24.     function startMove(obj,attr,iTarget){  
    25.         clearInterval(obj.timer);  
    26.         obj.timer = setInterval(function(){  
    27.             var iCur = 0;  
    28.             if(attr == 'opacity'){  
    29.                 iCur = parseInt(parseFloat(getStyle(obj, attr))*100);  
    30.             }else{  
    31.                 iCur = parseInt(getStyle(obj,attr));  
    32.             }  
    33.             var iSpeed = (iTarget - iCur) / 8;  
    34.             iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    35.             if(iCur == iTarget){  
    36.                 clearInterval(obj.timer);  
    37.             }else{  
    38.                 if(attr=='opacity'){  
    39.                     iCur += iSpeed  
    40.                     obj.style.filter='alpha(opacity:' + iCur + ')';  
    41.                     obj.style.opacity=iCur / 100;  
    42.                 }  
    43.                 else{  
    44.                     obj.style[attr]=iCur+iSpeed+'px';  
    45.                 }  
    46.                 document.title = obj.style[attr];  
    47.             }  
    48.                                                                                       
    49.         },30)  
    50.     }  

    链式动画(运动)

    链式动画:顾名思义,就是在该次运动停止时,开始下一次运动。(我们的运动框架到目前为止,基本功能都能实现了。现在拓展。所谓链式运动,即运动接着运动。当运动停止的时候,如果回调一个函数。回调一个运动函数,就能出现这样的效果。因此传入一个函数作为回调函数。)

       

    如何实现呢?

    • 使用回调函数:运动停止时,执行函数
    • 添加func形参(回调函数)。

    在当前属性到达目的地时iCurrent === iTarget,判断是否有回调函数存在,有则执行。

    1

    2

    3

    4

    5

    6

    if (iCurrent === iTarget) {//结束运动

        clearInterval(element.timer);

        if (func) {

            func();//回调函数

        }

    }

    链式动画完成!距离完美还差一步!

    例如:

    1. window.onload = function(){  
    2.     var oDiv = document.getElementById('div1');  
    3.     oDiv.onclick = function(){  
    4.         startMove(this,'width',300,function(){  
    5.             startMove(oDiv,'height',300,function(){  
    6.                 startMove(oDiv,'opacity',100)  
    7.             })  
    8.         })  
    9.     }  
    10. }  
    11. function getStyle(obj,attr){  
    12.     if(obj.currentStyle){  
    13.         return obj.currentStyleattr[attr];  
    14.     }else{  
    15.         return getComputedStyle(obj, false)[attr];  
    16.     }  
    17. }  
    18.                                                                       
    19. function startMove(obj,attr,iTarget,fn){  
    20.     clearInterval(obj.timer);  
    21.     obj.timer = setInterval(function(){  
    22.         var iCur = 0;  
    23.         if(attr == 'opacity'){  
    24.             iCur = parseInt(parseFloat(getStyle(obj, attr))*100);  
    25.         }else{  
    26.             iCur = parseInt(getStyle(obj,attr));  
    27.         }  
    28.         var iSpeed = (iTarget - iCur) / 8;  
    29.         iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    30.         if(iCur == iTarget){  
    31.             clearInterval(obj.timer);  
    32.             //回调函数  
    33.             if(fn) fn();  
    34.         }else{  
    35.             if(attr=='opacity'){  
    36.                 iCur += iSpeed  
    37.                 obj.style.filter='alpha(opacity:' + iCur + ')';  
    38.                 obj.style.opacity=iCur / 100;  
    39.             }  
    40.             else{  
    41.                 obj.style[attr]=iCur+iSpeed+'px';  
    42.             }  
    43.             document.title = obj.style[attr];  
    44.         }  
    45.                                                                         
    46.     },30)  
    47. }  

    同时运动

    思考:如何实现同时运动?

    1. 使用JSON传递多个值
    2. 使用for in循环,遍历属性,与值。
    3. 定时器问题!(运动提前停止)
    • 在循环外设置变量,假设所有的值都到达了目的值为true
    • 在循环中检测是否到达目标值,若没有值未到则为false
    • 在循环结束后,检测是否全部达到目标值.是则清除定时器

    实现:

    1. 删除attr与iTarget两个形参,改为json
    2. 在函数开始时,设置一个标记var flag = true; //假设所有运动到达终点.
    3. 在定时器内使用for in,遍历属性与目标,改写原来的attr与iTarget,为json的属性与值
    4. 修改运动终止条件,只有每一项的实际属性值iCurrent,等于目标值json[attr]时,flag才为true。清除定时器,判断是否回调。

    否则,继续执行代码,直到所有属性值等于目标值。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    /**

     * 获取实际样式函数

     * @param   {HTMLElement}   element  需要寻找的样式的html节点

     * @param   {String]} attr 在对象中寻找的样式属性

     * @returns {String} 获取到的属性

     */

    function getStyle(element, attr) {

        //IE写法

        if (element.currentStyle) {

            return element.currentStyle[attr];

            //标准

        } else {

            return getComputedStyle(element, false)[attr];

        }

    }

    /**

     * 完美运动框架

     * @param {HTMLElement} element 运动对象

     * @param {JSON}        json    属性:目标值      

     *   @property {String} attr    属性值

     *   @config   {Number} target  目标值

     * @param {function}    func    可选,回调函数,链式动画。

     */

    function startMove(element, json, func) {

        var flag = true; //假设所有运动到达终点.

        clearInterval(element.timer);

        element.timer = setInterval(function () {

            for (var attr in json) {

                //1.取当前的属性值。

                var iCurrent = 0;

                if (attr === "opacity") { //为透明度时执行。

                    iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100);

                } else { //默认情况

                    iCurrent = parseInt(getStyle(element, attr)); //实际样式大小

                }

                //2.算运动速度,动画缓冲效果

                var iSpeed = (json[attr] - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度

                iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整

                //3.未到达目标值时,执行代码

                if (iCurrent != json[attr]) {

                    flag = false; //终止条件

                    if (attr === "opacity") { //为透明度时,执行

                        element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE

                        element.style.opacity = (iCurrent + iSpeed) / 100; //标准

                    } else { //默认

                        element.style[attr] = iCurrent + iSpeed + "px";

                    }

                } else {

                    flag = true;

                }

                //4. 运动终止,是否回调

                if (flag) {

                    clearInterval(element.timer);

                    if (func) {

                        func();

                    }

                }

            }

        }, 30);

    }

    (目前为止,我们的运动框架还有个小缺点,就是不能同时该两个属性进行运动,比如同时更改宽和高。这个要求传入的属性是不同的几个值。则考虑传入一个 json用来保存需要更改的属性。)

    例如:

    1. window.onload = function(){  
    2.     var oDiv = document.getElementById('div1');  
    3.     oDiv.onclick = function(){  
    4.         startMove(this,{'width':300,'height':400});  
    5.     }  
    6. }  
    7. function getStyle(obj, attr){  
    8.     if(obj.currentStyle)    {  
    9.         return obj.currentStyle[attr];  
    10.     }else{  
    11.         return getComputedStyle(obj, false)[attr];  
    12.     }  
    13. }  
    14. function startMove(obj,json,fn){  
    15.     clearInterval(obj.timer);  
    16.     obj.timer = setInterval(function(){  
    17.      // 循环json   
    18.      for(var attr in json){        
    19.             var iCur = 0;  
    20.             if(attr == 'opacity'){  
    21.                 iCur = parseInt(parseFloat(getStyle(obj, attr))*100);  
    22.             }else{  
    23.                 iCur = parseInt(getStyle(obj,attr));  
    24.             }  
    25.             var iSpeed = (json[attr] - iCur) / 8;  
    26.             iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    27.             if(iCur == json[attr]){  
    28.                 clearInterval(obj.timer);  
    29.                 if(fn) fn();  
    30.             }else{  
    31.                 if(attr=='opacity'){  
    32.                     iCur += iSpeed  
    33.                     obj.style.filter='alpha(opacity:' + iCur + ')';  
    34.                     obj.style.opacity=iCur / 100;  
    35.                 }  
    36.                 else{  
    37.                     obj.style[attr]=iCur+iSpeed+'px';  
    38.                 }  
    39.                 document.title = obj.style[attr];  
    40.             }  
    41.         }  
    42.     },30)  

    上述代码,可以解决了同时运动的问题。但是还是有一个bug。比如,同时运动的某个属性,如果变化很小,马上就停止了,即关掉了定时器。那么会造成其他属性的变化也停止。因为这些属性都共用了一个定时器。因此需要判断,假设有三个人要来,然后一起去爬山。三个人有的先来,有的后来,只要三个人都到齐了,才出发。也就是只有三个属性都到了目标值,才关定时器。一开始,设立一个检查量,为真。假设所有人都到了,然后循环,只有有一个人没有到,检查就为假。直到所有的都到了,检测为真。则停止定时器。

    1. window.onload = function(){  
    2.     var oDiv = document.getElementById('div1');  
    3.     oDiv.onclick = function(){  
    4.         startMove(this,{'width':102,'height':400,'opacity':100});  
    5.     }  
    6. }  
    7. function getStyle(obj, attr){  
    8.     if(obj.currentStyle)    {  
    9.         return obj.currentStyle[attr];  
    10.     }else{  
    11.         return getComputedStyle(obj, false)[attr];  
    12.     }  
    13. }  
    14. function startMove(obj,json,fn){  
    15.     clearInterval(obj.timer);  
    16.     obj.timer = setInterval(function(){  
    17.         var bStop = true;  
    18.         for(var attr in json){    
    19.             //取当前值    
    20.             var iCur = 0;  
    21.             if(attr == 'opacity'){  
    22.                 iCur = parseInt(parseFloat(getStyle(obj, attr))*100);  
    23.             }else{  
    24.                 iCur = parseInt(getStyle(obj,attr));  
    25.             }  
    26.             //计算速度  
    27.             var iSpeed = (json[attr] - iCur) / 8;  
    28.             iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    29.             //检测停止  
    30.             if(iCur != json[attr]){  
    31.                 bStop = false;  
    32.             }     
    33.             if(attr=='opacity'){  
    34.                 iCur += iSpeed  
    35.                 obj.style.filter='alpha(opacity:' + iCur + ')';  
    36.                 obj.style.opacity=iCur / 100;  
    37.             }  
    38.             else{  
    39.                 obj.style[attr]=iCur+iSpeed+'px';  
    40.             }  
    41.         }  
    42.         if(bStop){  
    43.             clearInterval(obj.timer);  
    44.             if(fn) fn();  
    45.         }  
    46.     },30)  
    47. }  

    再循环外定义一个 标志变量 bStop = true。用来表示所有属性到达目标值。等循环结束了,如果这个值是真的,则停止定时器。因为,每次运行定时器,都会初始化这个值。循环的过程中,只要有一个没有到,bStop就被设定为 false。如果某个到了,此时 iCur != json[attr],表示速度为0 后面执行的结果,也不会有变化。只有所有的都达到目标值。循环则不再改变 bStop的值。此时,只要下一次运行定时器。就是初始化 bStop为真。而循环因为都到了,所以速度为0 也就再也没有变化。循环结束,sBstop还是真,表示所有都到了。因此此时结束定时器。

    最后附上完美运动框架,封装成 move.js 就可以调用了。

    1. /** 
    2.  * @author rsj217 
    3.  * getStyle 获取样式 
    4.  * startMove 运动主程序 
    5.  */  
    6.                  
    7. function getStyle(obj, attr){  
    8.     if(obj.currentStyle)    {  
    9.         return obj.currentStyle[attr];  
    10.     }else{  
    11.         return getComputedStyle(obj, false)[attr];  
    12.     }  
    13. }  
    14. function Move(obj,json,fn){  
    15.     //停止上一次定时器  
    16.     clearInterval(obj.timer);  
    17.     //保存每一个物体运动的定时器  
    18.     obj.timer = setInterval(function(){  
    19.         //判断同时运动标志  
    20.         var bStop = true;  
    21.         for(var attr in json){    
    22.             //取当前值    
    23.             var iCur = 0;  
    24.             if(attr == 'opacity'){  
    25.                 iCur = parseInt(parseFloat(getStyle(obj, attr))*100);  
    26.             }else{  
    27.                 iCur = parseInt(getStyle(obj,attr));  
    28.             }  
    29.             //计算速度  
    30.             var iSpeed = (json[attr] - iCur) / 8;  
    31.             iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);  
    32.             //检测同时到达标志  
    33.             if(iCur != json[attr]){  
    34.                 bStop = false;  
    35.             }     
    36.             //更改属性,获取动画效果  
    37.             if(attr=='opacity'){  
    38.                 iCur += iSpeed  
    39.                 obj.style.filter='alpha(opacity:' + iCur + ')';  
    40.                 obj.style.opacity=iCur / 100;  
    41.             }  
    42.             else{  
    43.                 obj.style[attr]=iCur+iSpeed+'px';  
    44.             }  
    45.         }  
    46.         //检测停止  
    47.         if(bStop){  
    48.             clearInterval(obj.timer);  
    49.             if(fn) fn();  
    50.         }  
    51.     },30)  
    52. }  

    运动框架总结

    • 运动框架演变过程

    框架

    变化

    startMove(element)

    运动

    startMove(element,iTarget)

    匀速–>缓冲–>多物体

    startMove(element,attr,iTargrt)

    任意值

    startMove(element,attr,iTargrt,func)

    链式运动

    startMove(element,json,func)

    多值(同时)–>完美运动框架

  • 相关阅读:
    利用合成数据进行时间序列分类的数据扩充方法
    基于 KubeSphere CI/CD 将 Spring Boot 项目发布至 Kubernetes
    矩阵白化
    delphi 过滤开头 结尾 全部 空格的函数
    delphi CopyFileProgressBar 拷贝文件显示进度条
    delphi 分享三个随机字符串
    delphi IsIPAdress 非正则表达式验证IP的方法
    delphi PosAnsi
    delphi公用函数
    Delphi中break,exit,abort跳出循环的比较
  • 原文地址:https://www.cnblogs.com/wfaceboss/p/5950189.html
Copyright © 2011-2022 走看看