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()

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

animation
点击运动

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

下面我还引入了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)

}

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

animation
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值