美文网首页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