0


JavaScript之匀速与减速动画函数的封装原理与编码讲解


高质量前端博主,点个关注不迷路🌸🌸🌸

写在最前:

这篇文章是来填坑的填上一次给大家讲轮播图的一个坑:关于封装动画函数的详细介绍,以及匀速和减速动画各自的封装原理,废话不多说,大家请阅读文章!

I. 总述

首先我们要明白封装动画函数是要干什么?

考虑这样一件事,例如我想要一个div元素,从一个位置,通过运动到达另一个位置。并且是左右移动,应该怎么做?

这个时候大家第一个想到的应该是用css的动画样式,动画样式固然可以实现,但是我们今天采用另一种思路:用JavaScript实现这个移动的效果,那么它实现动画效果的原理是什么呢?我们仍然以左右移动为例:

通过提前判定目标位置的坐标,而后计算出距离目标坐标需要给div设置的left值,最后,通过函数setInterval()函数连续的执行移动(间隔时间非常短,通常设置30ms,越短越好),但是每一次移动,仅仅移动一小段,由于间隔的时间很短,于是我们人眼的错觉会认为是在缓缓移动

这就是实现的原理了,而常用的两类动画函数封装有匀速运动的封装和减速运动的封装,下面我们就一一给予原理的讲解与编码


II. 匀速动画的封装原理与编码

首先,我们进行匀速运动动画函数的封装:

我们一步一步来,首先,我们新建一个js文件,名字比如起一个animate.js,并把函数的框架写出来

function animate(obj, target, callback) {

}

从框架中,我们看到有三个参数:obj、target 和 callback,我们逐一解释它们的作用:

1️⃣ obj:obj是需要执行动画的元素,例如我们前面举的例子,那么div就是我们的obj传入div对应的dom对象即可。(jquery对象也可以)

2️⃣ target:这是目标位置的left值,当然了如果后面大家需要向别的方向做动画,例如上下方向,那这个target就是目标的top值或者bottom值

3️⃣ callback:这是完成了本次动画后执行的回调函数,我们要理解"完成"二字:完成指的是已经完成了从一个地方到目的地而不是我刚才说的setInterval()函数的每一次执行,简单的说,这里的完成就是指的是已经到达目的地了,然后会执行的函数

于是,接下来就像我刚才提到的那样,我们先在里面写一个setInterval()函数,并设置一个很短的调用间隔时间,比如30ms调用一次

function animate(obj, target, callback) {
    obj.timer = setInterval(function () {

   },30);
}

这里obj.timer指的就是obj的setInterval()函数,我们知道每一个dom元素或者jQuery元素对象都有一个timer属性,这个timer属性就是指它绑定的定时器,这个定时器其实就是setInterval(),通俗的讲就是这一步就给obj加了一个定时器timer,加的方式是setInterval()函数。

那看到这里,大家应该会有一个想法:那就是会不会出现多个定时器的混叠,比如每次调用了这个函数,传入一个obj,这个obj就会被安装一个定时器,这样就会引起混叠和混乱,于是我们需要在安装定时器前加一句代码:

function animate(obj, target, callback) {
    clearInterval(obj);
    obj.timer = setInterval(function () {

   },30);
}

没错,就是用经典的clearInterval()(不了解的朋友可以回看我之前关于setInterval()函数的博客)。

这之后,我们开始分析div的移动过程,请看下图

首先,我们拿到了div这个元素的dom对象obj后,我们理所应当的可以拿到它当前的left样式值,拿到样式值的代码大家都会写,应该是这样的:

var current_left = obj.css("left")

之后,我们每一次调用setInterval()要走的距离,由于时间只有30ms,因此不能走的太远,否则就没有动画的感觉了,而是有很突兀的瞬移感,因此我推荐每次移动5像素,但由于传入的目标值可以大于当前的left值,也可以小于当前的left值,因此我们加一个判断:

var step;
if(parseInt(current_left) > target){
    step = -5;
}
else{
    step = 5
}

有了每一次移动的距离step(也叫步长,后面我们都叫它步长),我们也有目标left值target,于是很自然的,我们可以写出这样的逻辑判断进行匀速移动,于是完整的匀速代码也就完成了

function animate(obj, target, callback) {
    clearInterval(obj);
    obj.timer = setInterval(function () {
        var current_left = obj.css("left");
        var step;
        if(parseInt(current_left) > target){
            step = -5;
        }
        else{
            step = 5
        }
        if (parseInt(current_left) == target) {
            clearInterval(obj.timer);
            if (callback) {
                callback();
            }
        }
        obj.css("left", parseInt(current_left) + step + "px");
    }, 30)
}

可以看到,当完成了运动,也即满足:

parseInt(current_left) == target

则会**先把定时器解除,防止再运动;与此同时,执行回调函数callback()**。

那说到这里,匀速运动的封装我们就完成了为了使讲解更加生动有趣,我们用一个小例子测试一下动画函数好不好用

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>animation</title>
    <style>
        #father {
            position: relative;
            height: 200px;
        }

        #son {
            position: absolute;
            width: 200px;
            height: 200px;
            border: 1px solid black;
            background-color: aqua;
        }

        #btn {
            width: 120px;
            height: 30px;
            line-height: 30px;
            margin: 0 auto;
            margin-top: 100px;
            border: 1px solid black;
            text-align: center;
        }
    </style>
</head>

<body>
    <div id="father">
        <div id="son"></div>
    </div>
    <div id="btn">点击运动</div>
    <script type="text/javascript" src="../匀速动画/animate.js"></script>
    <script type="text/javascript" src="../匀速动画/jquery.min.js"></script>
    <script type="text/javascript">
        var btn = $("#btn");
        var div_obj = $("#son");
        btn.bind("click", function () {
            animate(div_obj, 400, function () {
                alert("完成运动!")
            })
        })
    </script>

</body>

</html>

其中,我们用这句话引入写好的动画函数的js文件

<script type="text/javascript" src="../匀速动画/animate.js"></script>

下面我还引入了jQuery,目的是简化代码,大家也可以用原生的DOM,都可以。

运行后,效果如下图所示


III. 减速动画的封装原理与编码

最后,我们谈一谈减速动画要怎么封装,原理是什么。

所谓的减速,其实和我们中学物理学的减速运动很类似,也就是在运动的过程中,速度越走越慢,那么我们看一个式子:

v = s / t

这个公式不知道的,可以面壁思过了哈哈,中学物理的速度定义式子,从这个式子中,我们知道想要让速度越走越慢,有两个可行角度:

增加 s 或者减小 t

但是事实上,减小t并不现实,原因其实刚才已经说过了,30ms的每次执行移动时间间隔已经是一个极限的时间了小于这个时间的移动,会变得很假,就不像是动画了,更像瞬移。于是结论就有了:

要实现减速运动 = = = 减小每一次移动的距离 s

那么我们要怎么减小每一次移动的距离s呢,一个很简单的想法就是每一次让移动的步长减小一个固定的常数但如果每一次减小的都是一个固定的常数,那等于没有实现减速,而是速度变小的匀速,于是我们应该是每一次的s都能比前一次的小,于是我们可以用下面的这个式子:

var step = (target - parseInt(obj.css("left"))) / 10;

其中,step是当次移动的步长,target仍然是目标的left值,parseInt(obj.css)是当前的left值,那么每一次的step有没有减小呢?我们来计算一下

例如当前的left值是100,目标是0,那么我们的步长应该是这样的:

第一次:step = (100 - 0) / 10 = 10 px

第二次:step = (100 - 10) / 10 = 9 px

第三次:step = (100 - 19) / 10 = 8.1 px

...

好了,于是计算了一下发现的确能使得步长step不断减小,也即我们上面式子中的s不断减小,因此这个式子貌似可行。

但是善于思考的朋友可能要质疑我了:如果按照这样的计算方式,那么理论上应该是永远也到不了目的地,因为每一次总会与目的地有一个细微的差值,其实这样的理解是对的,于是我们需要加这样一行代码:

step = step > 0 ? Math.ceil(step) : Math.floor(step);

这句代码**涉及了两个函数Math.ceil()和Math.floor()**,避免大家之前没见过,我们做一个简述:

Math.ceil()方法执行的是向上取整计算,它返回的是大于或等于函数参数,并且与之最接近的整数。

Math.floor() 方法执行的是向下取整计算,它返回的是小于或等于函数参数,并且与之最接近的整数。

于是加了这句话,会出现什么神奇的事情呢?我们看一下:

当前的left是91,目标是100,那么有:

step = (100 - 99) / 10 = 0.1

按理说,它应该走0.1,然后再留下0.9,但有了这个函数,它直接就走了1,也就是走到了目的地。这是对ceil()函数的解释,那对于floor()则逻辑是完全一样的,不做赘述了。

于是我们拿出刚才封装好的匀速函数,把step的公式换成上面的公式,就得到了减速运动的封装函数:

function animate(obj, target, callback) {
    clearInterval(obj);
    obj.timer = setInterval(function () {
        var current_left = parseInt(obj.css("left"));
        var step;
        step = (target - current_left) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (current_left == target) {
            clearInterval(obj.timer);
            if (callback) {
                callback();
            }
        }
        obj.css("left", current_left + step + "px");
    }, 30)
}

继续用前面那个小例子,我们测试一下减速函数好不好用

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>animation</title>
    <style>
        #father1,
        #father2 {
            position: relative;
            height: 150px;
            margin-top: 50px;
        }

        #son1,
        #son2 {
            text-align: center;
            font-size: 30px;
            line-height: 150px;
            position: absolute;
            width: 150px;
            height: 150px;
            border: 1px solid black;
            background-color: aqua;
        }

        #btn {
            width: 120px;
            height: 30px;
            line-height: 30px;
            margin: 0 auto;
            margin-top: 100px;
            border: 1px solid black;
            text-align: center;
        }
    </style>
</head>

<body>
    <div id="father1">
        <div id="son1">匀速</div>
    </div>
    <div id="father2">
        <div id="son2">减速</div>
    </div>
    <div id="btn">点击运动</div>
    <script type="text/javascript" src="../减速动画/animate2.js"></script>
    <script type="text/javascript" src="../减速动画/animate.js"></script>
    <script type="text/javascript" src="../减速动画/jquery.min.js"></script>
    <script type="text/javascript">
        var btn = $("#btn");
        var div_obj1 = $("#son1");
        var div_obj2 = $("#son2");
        btn.bind("click", function () {
            animate(div_obj1, 400, function () {
            })
            animate2(div_obj2, 400, function () {
            })
        })
    </script>

</body>

</html>

为了方便大家对比,我这次把两个动画函数都引了进来,并且把匀速动画函数命名为animate,把减速动画函数命名为animate2,并复制了一个二号盒子下面是效果展示

好了,那到这里减速动画的封装原理和编码也讲解完毕感谢大家的阅读!(评论区制定有本次讲解的两个动画函数的资源,大家可以自行下载js资源文件)

以上是本期的全部内容,喜欢的小伙伴们可以三连支持一下!💓💓💓!

另外本期博客参与了新星计划】,还请大家支持一下🌟🌟🌟

标签: javascript 动画 js

本文转载自: https://blog.csdn.net/qq_52736131/article/details/124034168
版权归原作者 跳探戈的小龙虾 所有, 如有侵权,请联系我们删除。

“JavaScript之匀速与减速动画函数的封装原理与编码讲解”的评论:

还没有评论