JS实现缓动动画效果
原理如下:
假设要从数值A变化到数值B,如果是线性运动,则每次移动距离是一样;如果是缓动,每次移动距离不一样。那如何才能不一样呢?很简单,按比例移动就可以。
例如:每次移动剩余距离的一半。
对吧,超容易理解的。
比方说:你和初恋之间距离是64,每秒移动一半,则,你们之间的距离下一秒就是32, 再下一秒就是16,然后8,然后4,然后2,然后1,然后……你们就在一起了。你们在一起的这个过程就是一个典型的先快后慢的缓动运动过程,如下示意图:
用一个简单的公式表示就是:
A = A + (B - A) / 2
点击按钮执行的是下面的backToTop()
方法:
// 滚动到顶部缓动实现 // rate表示缓动速率,默认是2 var backToTop = function (rate) { var doc = document.body.scrollTop? document.body : document.documentElement; var scrollTop = doc.scrollTop; var top = function () { scrollTop = scrollTop + (0 - scrollTop) / (rate || 2); // 临界判断,终止动画 if (scrollTop < 1) { doc.scrollTop = 0; return; } doc.scrollTop = scrollTop; // 动画gogogo! requestAnimationFrame(top); }; top(); };
其中,代码的核心是:
scrollTop = scrollTop + (0 - scrollTop) / (rate || 2);
scrollTop
表示公式的A, 滚动到顶部滚动高度是0
,因此,上面的0
,实际上就是公式的B, 而公式中的2
表示缓动速率,实际开发的时候是可以灵活调整的,缓动速率范围是1
到无穷大,速率值越小,运动越快。比如说上面的返回顶部效果,我们把缓动速率改成4
,点击下面的按钮感受效果:
等比例靠近理论上最终只会无穷靠近,并不会真正的相等,也就是动画永远没有结束的时候,所以说需要做一个临界判断,也就是距离小到一定数目的时候,直接等于目标值,并终止动画。例如,上面的返回顶部,就是当距离顶部滚动高度小于1
的时候,直接返回顶部,并终止动画。
if (scrollTop < 1) { doc.scrollTop = 0; return; }
如果项目很多地方使用该算法,每次都写一遍requestAnimationFrame
和边界判断是很啰嗦的,于是,我们可以把算法变个身,例如下面这样:
var _easeout = function(start, end, rate, callback) {
var _end = end;
if (start == end || typeof start != 'number') {
return;
}
end = end || 0;
rate = rate || 2;
var step = function() {
start = start + (end - start) / rate;
if (Math.abs(start - _end) < 1) {
console.log('end');
callback(end, true);
return;
}
callback(start, false);
requestAnimationFrame(step);
};
step();
};
其中:
A
是起始位置;B
是目标位置;rate
是缓动速率;callback
是变化的位置回调,支持两个参数,value
和isEnding
,表示当前的位置值(数值)以及是否动画结束了(布尔值);
于是,我们的返回顶部效果可以这么使用:
var doc = document.body.scrollTop !== undefined ? document.body : document.documentElement;
console.log('start', doc.scrollTop);
$("#back").click(function(){
console.log('start', doc.scrollTop);
_easeout(doc.scrollTop, 500, 2, function(value) {
doc.scrollTop = value;
});
})
});