【JavaScript从入门到精通】第十六课 JS运动基础-02

此内容来自开课吧石川微信公众号

缓冲运动

在现实生活中,很多运动并不是完全匀速的,在一个物体停止之前,必然会有一段速度的缓冲。为了尽可能贴近真实,我们希望能够通过JS制作出缓冲运动的效果。缓冲运动的要求是让元素逐渐变慢,最后停止。我们可以理解为,当元素距离终点位置远的时候,其速度较快,而距离近的时候,速度较慢。也就是说,速度与距离成正比,我们可以通过距离来控制速度。

<html>
<head>
    <meta charset="utf-8"/>
    <title>无标题文档</title>
</head>
<body>
    <input type="button" value="开始运动" onclick="startMove()"/>
    <div id="div1"></div>
</body>
</html>
#div1 {
    width: 100px;
    height: 100px;
    background: red;
    position: absolute;
    left: 0;
    top: 50px;
}
function startMove() {
    var oDiv = document.getElementById('div1');
    setInterval(function () {
        var speed = (300 - oDiv.offsetLeft) / 10;
        oDiv.style.left = oDiv.offsetLeft + speed + 'px';
    }, 30);
}

效果如下:

【JavaScript从入门到精通】第十六课 JS运动基础-02

通过更改var speed=(300-oDiv.offsetLeft)/10中的分子可以改变缓冲运动的速度。但实际上,如果我们将div块运动后的left值打印出来,会发现并不是300而是291,也就是说div块并没有运动到我们想让它运动到的位置,这是为什么呢?我们来分析一下,当div块运动到291px时,其速度还剩下0.9px,但px(像素)是计算机能接收的最小单位,因此速度小于1(0.9)之后,div块从291px变为了291.9px,这时候计算机会直接将291.9px变为291px,相当于没有发生运动。为了避免这种情况发生,我们需要用到Math类。Math类是JS里一个很常用的类,具体的我们以后再讲。Math类里面有一个ceil方法,其作用为在向上取整,如下所示:

alert(Math.ceil(3.5));

输出结果为4。和ceil相对的方法是floor,作用为向下取整,用法和ceil一样,这里不再多说。

回到我们的缓冲运动,这里只要我们对speed进行向上取整,那么当其速度小于1时就会变为1,这样就可以到达终点了。

function startMove() {
    var oDiv = document.getElementById('div1');
    setInterval(function () {
        var speed = (300 - oDiv.offsetLeft) / 10;
        speed = Math.ceil(speed);
        oDiv.style.left = oDiv.offsetLeft + speed + 'px';
        document.title = oDiv.offsetLeft + ',' + speed;
    }, 30);
}

这里依然存在一个小问题,现在我们将div1的left值改为600px,那么它也不会到达终点。原因是,当速度达到-1-0之间的时候,向上取整会导致速度直接变为0。因此,我们还需要对速度的正负性进行判断,如果是正数则使用ceil方法,负数则使用floor方法。

<html>
<head>
    <meta charset="utf-8">
    <title>无标题文档</title>
</head>
<body>
    <input type="button" value="开始运动" onclick="startMove()"/>
    <div id="div1"></div>
    <div id="div2"></div>
</body>
</html>
#div1 {
    width: 100px;
    height: 100px;
    background: red;
    position: absolute;
    left: 0;
    top: 50px;
}
function startMove() {
    var oDiv = document.getElementById('div1');
    setInterval(function () {
        var speed = (300 - oDiv.offsetLeft) / 10;
        //speed=Math.floor(speed);
        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
        oDiv.style.left = oDiv.offsetLeft + speed + 'px';
        document.title = oDiv.offsetLeft + ',' + speed;
    }, 30);
}

效果如下:

【JavaScript从入门到精通】第十六课 JS运动基础-02

记得我们在学DOM的时候,学过制作过一个悬浮在右边的框,但实际上那个框会存在一定的问题:拖动页面时框会产生抖动。现在我们要想方法来避免这个抖动,所以我们来看看缓冲菜单是怎么做的。

<html>
<head>
    <meta charset="utf-8"/>
    <title>无标题文档</title>
</head>
<body style="height:2000px;">
    <div id="div1"></div>
</body>
</html>
#div1 {
    width: 100px;
    height: 150px;
    background: red;
    position: absolute;
    right: 0;
    bottom: 0;
}
window.onscroll = function () {
    var oDiv = document.getElementById('div1');
    var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    //oDiv.style.top=document.documentElement.clientHeight-oDiv.offsetHeight+scrollTop+'px';
    startMove(document.documentElement.clientHeight - oDiv.offsetHeight + scrollTop);
};
var timer = null;

function startMove(iTarget) {
    var oDiv = document.getElementById('div1');
    clearInterval(timer);
    timer = setInterval(function () {
        var speed = (iTarget - oDiv.offsetTop) / 4;
        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
        if (oDiv.offsetTop == iTarget) {
            clearInterval(timer);
        }
        else {
            oDiv.style.top = oDiv.offsetTop + speed + 'px';
        }
    }, 30);
}

效果如下:

【JavaScript从入门到精通】第十六课 JS运动基础-02

通过将悬浮框导入我们的运动框架就可以打到这样的效果。如果我们将document.documentElement.clientHeight-oDiv.offsetHeight的值除以2,可以看到,div块的悬浮位置从页面底部变为了页面中间。但大家如果运行这个程序,会发现,div块会进行轻微的抖动。这是为什么呢?我们来做一个小小的测试,用一个textbox来显示div块的的top值,会发现div块的top值在858和859之间不断跳动,这是为什么呢?实际上,目标点的实际值是858.5px(因为除以2的原因有可能出现这样的情况),但计时器每次都只能前进1px,因此div块会跑到859px,然后因为超过了要求值又向回跳到858px,如此往复,形成了这样的抖动效果。解决方法也很简单,在将document.documentElement.clientHeight-oDiv.offsetHeight的值除以2之后取整即可。

匀速运动的停止条件

和缓冲运动一样,匀速运动也存在始终到不了精确的终点(相比于最后速度小于1的缓冲运动,匀速运动很容易发生越界的情况)而发生反复抖动的情况。实际上对于匀速运动来说,只要元素和目的位置足够近就算达成了目标。在JS的Math类里有一个abs()方法用于计算数字的绝对值,使用这种方法可以避免匀速运动在终点时的抖动。

<html>
<head>
    <meta charset="utf-8"/>
    <title>无标题文档</title>
</head>
<body>
    <input type="button" value="到100" onclick="startMove(100)"/>
    <input type="button" value="到300" onclick="startMove(300)"/>
    <div id="div1"></div>
    <div id="div2"></div>
    <div id="div3"></div>
</body>
</html>
#div1 {
    width: 100px;
    height: 100px;
    background: red;
    position: absolute;
    left: 600px;
    top: 50px;
}

#div2 {
    width: 1px;
    height: 300px;
    position: absolute;
    left: 300px;
    top: 0;
    background: black;
}

#div3 {
    width: 1px;
    height: 300px;
    position: absolute;
    left: 100px;
    top: 0;
    background: black;
}
var timer = null;

function startMove(iTarget) {
    var oDiv = document.getElementById('div1');
    clearInterval(timer);
    timer = setInterval(function () {
        var speed = 0;
        if (oDiv.offsetLeft < iTarget) {
            speed = 7;
        }
        else {
            speed = -7;
        }
        if (Math.abs(iTarget - oDiv.offsetLeft) <= 7) {
            clearInterval(timer);
            oDiv.style.left = iTarget + 'px';
        }
        else {
            oDiv.style.left = oDiv.offsetLeft + speed + 'px';
        }
    }, 30);
}

效果如下:

【JavaScript从入门到精通】第十六课 JS运动基础-02

当我们的元素和目标位置的距离小于等于速度的时候,我们就认为元素已经到达了终点,于是将定时器关闭,同时将元素直接设置在目标位置。这样既防止了元素抖动,也能让元素精确地到达终点。

如需转载,烦请注明出处:https://www.qdskill.com/javascript/542.html

发表评论

登录后才能评论