看了pink老师的缓动动画制作,需要完成一个盒子从起点到800px,再回到500px,且离终点越近速度减慢,即做到 缓动效果。
图1 是要完成的效果图,盒子到达固定位置就停下,且定时器也停下,还能实现倒退,有明显的速度减慢。
我一开始的问题是,盒子不仅停不下来,还飞到无穷远的地方去了!
这是第一次写的问题多多代码,😓
<style>
.box {
position: absolute;
margin-top: 40px;
width: 50px;
height: 50px;
background-color: palegreen;
}
</style>
</head>
<body>
<div class="box"></div>
<button class="btn5">到五百</button>
<button class="btn8">到八百</button>
<script>
var box = document.querySelector('.box');
function animate(obj, target) {
clearInterval(obj.timer);
var step = (target - obj.offsetLeft) / 10;
obj.timer = setInterval(function() {
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + step + 'px';
}, 20)
}
var btn5 = document.querySelector('.btn5');
var btn8 = document.querySelector('.btn8');
btn5.addEventListener('click', function() {
animate(box, 500);
})
btn8.addEventListener('click', function() {
animate(box, 800);
})
</script>
</body>
</html>
后来我发现给盒子的left属性赋值为0,可以完成一次动画效果,但是等第二次按下按钮还是不能到达指定位置,而且还没有缓动效果。
加入调试代码,分别打印了,盒子的offsetLeft值、定时器是否停止(target 目标值是否达到)、步长是多少。以下为运行结果图
可以看到几个问题:
一、步长没有发生变化,一直都是一个数字,猜测很有可能是 obj.offsetLeft没有动态获取到盒子的偏移值。
二、定时器只停下了两次,也就是在第一次按下500、800的按钮时,再次点击无效,猜测target目标位置永远没有到达,所以定时器一直停不下来
解决问题:
这里的步长没有发生变化,我觉得可能是,offsetLeft只获取了一次偏移值,就只使用了一次这个公式计算(目标值-现在位置) / 10 = 每次移动的步长 ,因此算得的步长值自然没有变换,把计算步长的代码放到定时器里面,每完成一次动画,就再计算一次,这样就可以获得动态的offsetLeft值了
再者,到达不了目标值,定时器不会停止,是因为我们使用的计算步长的公式有除法,商数极有可能是有小数的,小数一直除下去就没完没了了,因此加入取整的代码。
这里想到过直接用 parseInt()这个方法, var step = parseInt((target - obj.offsetLeft) / 10); 还是会出现无法到达目标位置的情况,除到商小于1的时候,parseInt()取整的结果是0,可目标位置远没到达,此方法不可行。
最合适的还是用向上取整公式Math.ceil(),但是若只向上取整,那么盒子在倒退的时候有会有问题,这时候的target - offsetLeft < 0,负数向上取整,-9.9向上取整得到-9,盒子本应该前进9.9,向上取整盒子后退的距离就会小。
所以当盒子后退的时候,也就是step < 0时,步长应该向下取整,再使用三目运算加入判断就可以啦! step = step > 0 ? Math.ceil(step) : Math.floor(step);
加入步长判断后,可以看到没有到达目标位置前,步长都是正整数、负整数。
小结一下:
使用offsetLeft记得给盒子加上position属性,left也要赋值为0;
每执行一次定时器就要重新获取偏移值,这样才能动态计算步长;
对于位移问题,盒子移动的距离取的是绝对值,负数向下取整绝对值才大,才不会出现没有到达目标位置,步长就为0 的情况。
function animate(obj, target) {
clearInterval(obj.timer);
obj.timer = setInterval(function() {
console.log('偏移:' + obj.offsetLeft);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
console.log('stop');
}
obj.style.left = obj.offsetLeft + step + 'px';
console.log('步长: ' + step);
}, 20)
}