美文网首页H5学习笔记Web 前端开发
如何用js优雅地对ta说“自己动”——匀速运动

如何用js优雅地对ta说“自己动”——匀速运动

作者: 熊郅峰 | 来源:发表于2016-07-14 22:43 被阅读169次
    Happiness is a way station between too much and too little

    js中匀速运动的基本原理:

    用定时器不断的改变元素的某一属性,从而达到动态效果

    由于动图实在是太麻烦(好吧,是我懒 '_>`),建议大家下载源码,方便同步演示

    ☛<a href="http://pan.baidu.com/s/1jI2MD0q">戳此下载源码</a>

    提纲

    • 简单的从左至右运动
    • 终止运动
    • 运动的速度控制
    • 处理bug:由速度引起的无法终止运动
    • 处理bug:达到目标后点击按钮仍向前运动
    • 处理bug:多次点击按钮会使速度变快
    • demo:div的鼠标移入滑出运动
    • demo:透明度运动

    简单的从左至右运动:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style type="text/css">
            #div1{
                width: 150px;
                height: 80px;
                background-color: #2470B4;
                position: absolute;
                top: 50px;
                left: 100px;
            }
        </style>
        <script type="text/javascript">
            function startMove() {
    
                var oDiv1 = document.getElementById("div1");
    
                setInterval(function () {
    
                    //每30毫秒div1的left属性增加7
                    oDiv1.style.left = oDiv1.offsetLeft + 7 + "px";
                }, 30);
    
            }
        </script>
    </head>
    <body>
        <input type="button" value="开始运动" onclick="startMove();">
        <div id="div1"></div>
    </body>
    </html>
    

    这是最简单的运动了,点击按钮,蓝色div会一直向右走...

    ...一直走到天荒地老

    由于我们没有设置终止,div会一直向右移动,现在我们来想办法让它停下来

    终止运动

    我们是通过定时器让div动起来的,想让它停下来,就得关掉定时器:

    <script type="text/javascript">
    
        var timer = null;
    
        function startMove() {
            var oDiv1 = document.getElementById("div1");
    
            timer = setInterval(function () {
                
                //让div1移动到500px停下来
                if(oDiv1.offsetLeft == 500) clearInterval(timer);
                
                //每30毫秒div1的left属性增加7
                oDiv1.style.left = oDiv1.offsetLeft + 10 + "px";
            }, 30);
        }
    </script>
    

    用一个变量 timer 来存储定时器,方便关闭,在每次定时器启动的时候先判断是否达到终止条件

    如果达到了就用 clearInterval(timer);关闭定时器,停止运动

    运动的速度控制

    由之前的代码可以看出,真正控制运动的是

            `oDiv1.style.left = oDiv1.offsetLeft + 10 + "px";`
    

    而这个运动速度是由增量10来决定的,增量越大,运动越快,反之亦然

    为了方便变更速度,我们可以用一个变量 speed 来保存增量:

    timer = setInterval(function () {
        
        var speed = 7;
    
        if(oDiv1.offsetLeft == 500) clearInterval(timer);
    
        oDiv1.style.left = oDiv1.offsetLeft + speed + "px";
    }, 30);
    

    这样改变speed的值就可以改变速度了,这里我们改成了7

    bug:由速度引起的无法终止运动

    速度改为7之后出现了一个bug:div达到了终止条件依然还在移动

    原因很明显,speed为7即增量为7,而我们设置的初始left为100

    也就是说,div1offsetLeft永远也不会等于500,也就无法停止定时器停下来了

    所以我们应该修改下终止条件:

    改为oDiv1.offsetLeft >= 500,这样就能保证不论speed能否整除500,div都能停下来了

    到目前为止,我们的div已经能动,能停了,但是依然漏洞百出

    bug:达到目标后点击按钮仍向前运动

    大家可以试下,第一次点击按钮可以让div动起来,停止后,再次点击按钮,div会蹿一下,点一下蹿一下,点一下蹿一下...

    这是因为setInterval()这个函数跟do{...}while()有点像,do while是先执行一遍再判断,而前者是先运行一遍,再延时,运行时虽然达到了终止边界要停止定时器,但是这一遍是要运行完的,所以会向前走一个距离增量

    解决办法非常简单,既然达到了终止条件,就应该不运行后面的代码了,把后面的代码用else包起来就行了:

    timer = setInterval(function () {
        var speed = 7;
        if(oDiv1.offsetLeft >= 500){
            clearInterval(timer);
        } else {
            oDiv1.style.left = oDiv1.offsetLeft + speed + "px";
            oP.innerHTML = oDiv1.offsetLeft + "px";
        }
    }, 30);
    

    这样就只有当oDiv1.offsetLeft < 500时才会执行后面的代码

    bug:多次点击按钮会使速度变快

    最后这个小bug是非常常见的,先说说问题的原因吧,这是因为每次点击按钮都会开启一个定时器,假如要给定时器30毫秒的增量是10,点三次开启三个定时器的话30毫秒的增量就成了30,所以就越来越快

    解决办法也十分有代表性,在每次点击按钮时先关闭定时器,再开启,这样就能保证只有一个定时器在运行:

    clearInterval(timer);
    timer = setInterval(function () {
        var speed = 7;
        if(oDiv1.offsetLeft >= 500){
            clearInterval(timer);
        } else {
            oDiv1.style.left = oDiv1.offsetLeft + speed + "px";
            oP.innerHTML = oDiv1.offsetLeft + "px";
        }
    }, 30);
    
    demo:div的鼠标移入滑出运动

    大家一定见过这种效果:一个小div悬浮在屏幕的一侧,鼠标移入时,小div连着一个大div一起滑出,鼠标移出时又收回去,只剩小div继续悬浮在屏幕一侧。

    有了之前的经验,思路变得非常清晰:

    • 把大div定位到屏幕外边隐藏起来
    • 鼠标移入时:两个div移出,露出大div
    • 鼠标移出时:两个div收回,只剩小div

    html代码:

    <body>
        <div id="div1">
            <div id="div2"></div>
        </div>
    </body>
    

    CSS代码:

    #div1{
        width: 200px;
        height: 400px;
        border: 1px solid #2470B4;
        position: absolute;
        top: 100px;
        left: -200px;
    }
    #div2{
        width: 30px;
        height: 100px;
        background-color: #2470B4;
        position: absolute;
        top: 150px;
        right: -30px;
    }
    

    js代码:

    window.onload = function () {
        var oDiv1 = document.getElementById("div1");
        var timer = null;
    
        oDiv1.onmouseover = function () {
            clearInterval(timer);//先清除定时器,确保只有一个定时器在运行
    
            //鼠标移入offsetLeft要增加,speed为正
            var speed = 10;                     
            timer = setInterval(function () {
                if(oDiv1.offsetLeft == 0){      //鼠标移入offsetLeft由-200变为0
                    clearInterval(timer);
                } else {
                    oDiv1.style.left = oDiv1.offsetLeft + speed +"px";
                }
            }, 30)
        }
    
        oDiv1.onmouseout = function () {
            clearInterval(timer);//先清除定时器,确保只有一个定时器在运行
    
            //鼠标移入offsetLeft要减小,speed为负
            var speed = -10;
            timer = setInterval(function () {
                if(oDiv1.offsetLeft == -200){   //鼠标移入offsetLeft由0变为-200
                    clearInterval(timer);
                } else {
                    oDiv1.style.left = oDiv1.offsetLeft + speed +"px";
                }
            }, 30)
        }
    }
    

    实现效果虽然十分简单,但是写出来的代码重复的地方太多了,臃肿难看,不够优雅,我们想办法来改进一下

    大家可以看出来,这里onmouseoveronmouseout调用的两个匿名函数几乎一模一样,只有两个关键的值不同——移动终点和速度的正负,而速度的正负可以有移动终点来判断,所以我们不妨重新定义一个函数toggleShow(target),把这两个值提取出来,统一成一个参数传进去,这样就可以把两个函数合二为一了:

    window.onload = function () {
        var oDiv1 = document.getElementById("div1");
        var timer = null;
    
        oDiv1.onmouseover = function () {
            toggleShow(0);
        };
    
        oDiv1.onmouseout = function () {
            toggleShow(-200);
        };
    
        function toggleShow(target) {
            clearInterval(timer);
            var speed = 0;
    
            //根据target的位置来判断速度的正负
            speed = target - oDiv1.offsetLeft > 0 ? 10 : -10;
    
            timer = setInterval(function () {
                if(oDiv1.offsetLeft == target){
                    clearInterval(timer);
                } else {
                    oDiv1.style.left = oDiv1.offsetLeft + speed +"px";
                }
            }, 30)
        }
    }
    

    比较一下,是不是精简了许多:)

    demo:透明度运动

    这个效果也挺常见的,鼠标移入div变得不透明,移出再变的半透明,又叫淡入淡出。

    原理跟上个demo一样,只是这次改变的是透明度:

    • 设置透明度的初始值为30(0.3)
    • 鼠标移入时:透明度增加到100(1)
    • 鼠标移出时:透明度减小到30(0.3)

    html代码:

    <body>
        <div id="div1"></div>
    </body>
    

    CSS代码:

    #div1{
        width: 300px;
        height: 200px;
        background-color: #2470B4;
        margin: 150px auto;
        opacity: 0.5;
        filter: alpha(opacity: 30);
    }
    

    css中不同浏览器的设置透明度的属性不同:

    opacity:0.5 //for IE9, Firefox, Chrome, Opera, Safari

    filter:alpha(opacity = 50) //for IE6, IE7, IE8

    后者也可以写成:

    filter:alpha(opacity:50) //for IE6, IE7, IE8

    js代码:

    window.onload = function () {
        var oDiv1 = document.getElementById("div1");
        var timer = null;
    
        var alpha = 30;
    
        function toggleShow(target) {
            clearInterval(timer);
    
            var speed = target - alpha > 0 ? 10 : -10;
    
            timer = setInterval(function () {
                if(alpha == target){
                    clearInterval(timer);
                } else {
                    alpha += speed;
                        oDiv1.style.opacity = alpha/100;
                        oDiv1.style.filter =  "alpha(opacity: " + alpha + ")";
                }
            }, 30)
        }
    
        oDiv1.onmouseover = function () {
            toggleShow(100);
        }
        oDiv1.onmouseout = function () {
            toggleShow(30);
        }
    }
    

    这里用了个小技巧:

    由于透明度的设置方法有两种,不方便直接获取当前透明度的值,这里用了一个变量alpha来保存透明度的值,alpha的初值设为30,跟css中设置的相同,而且增量是直接作用于alpha上的,最后才让透明度等于alpha的值

    其他值得注意的是,参数target是0-100的数值,所以最后oDiv1.style.opacity = alpha/100;要除以100以恢复0-1的取值范围

    以上。

    转载请注明出处

    相关文章

      网友评论

        本文标题:如何用js优雅地对ta说“自己动”——匀速运动

        本文链接:https://www.haomeiwen.com/subject/gxkejttx.html