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

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的取值范围

以上。

转载请注明出处

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容