原理
- window.requestAnimationFrame可以保证动画以每秒60次的频率执行(有时也会出现降频的现象)
- 每一次数字的变动即算一次动画
- 再执行每次动画之前,利用window.cancelAnimationFrame取消之前的动画,从而保证浏览器性能
方式
- 指定时间完成动画
- 指定速度完成动画
注:目前两种方式是互斥的
核心代码
// count.js
/**
* 数字累加滚动,支持两种方式:
* 1. 指定速度,通过interval控制,即控制帧与帧的间隔
* 2. 指定时间,即:无论数字多大,指定时间内必须完成累加
* @param {Object} option 配置
* @param {Number} option.start 起始数字
* @param {Number} option.end 结束数字
* @param {Number} option.interval 帧与帧之间的间隔(ms)
* @param {Boolean} option.limitTime 指定时间
* @param {Function} option.callback 回调函数,参数为每次累加后的数字
*/
export default function Count({
start = 0,
end = 100,
interval = 0,
limitTime = 0,
callback
}) {
// 每帧所需的时间(ms)
// 按照MDN介绍,通常是每秒60帧
const frameTime = 1000 / 60;
// 帧数
let frameAmount;
// 步长
// 即每次的累加值,默认: 1
let frameStep = 1;
// 计数器
// 当指定interval时,计数器才起作用
// 作用:用于与interval比较,等于interval时,执行回调,然后清零重新计数,达到控制速度的效果
let counter = 0;
// 如果指定了limitTime,则重新计算步长
if (limitTime && !interval) {
let length = end - start;
// 指定时间内可以完成多少帧
frameAmount = limitTime / frameTime;
// 帧数与真实长度取两者之间最小值
if (frameAmount > length) {
frameAmount = length;
}
frameStep = Math.round((end - start) / frameAmount);
}
// 帧的回调函数
function step() {
let req;
// 方式1和2的公共逻辑部分
function commonLogic() {
start += frameStep;
// 防止最后一次累加时出现数字越界的情况
if (start >= end) {
callback(end);
} else {
callback(start);
window.cancelAnimationFrame(req);
req = window.requestAnimationFrame(step);
}
}
// 方式1:按时间间隔
if (interval !== 0) {
counter++;
if (counter === interval) {
commonLogic();
counter = 0;
} else {
window.cancelAnimationFrame(req);
req = window.requestAnimationFrame(step);
}
// 方式2:按指定时间
} else {
if (start < end) {
commonLogic();
}
}
}
window.requestAnimationFrame(step);
}
示例1:在指定时间内完成动效
注:结合Vue实现
<template>
<div id="app">
<div>{{num}}</div>
</div>
</template>
<script>
import Count from './count';
export default {
name: 'app',
data () {
return {
num: 0
}
},
mounted () {
// 调用api
Count({
end: 100, // 结束时间
limitTime: 2000, // 指定时间
callback: (num) => {
this.num = num;
}
})
}
}
</script>
运行结果
示例2:按指定速度完成动效
注:结合Vue实现
<template>
<div id="app">
<div>{{num}}</div>
</div>
</template>
<script>
import Count from './count';
export default {
name: 'app',
data () {
return {
num: 0
}
},
mounted () {
Count({
end: 100, // 结束数字
interval: 6, // 指定速度
callback: (num) => {
this.num = num;
}
})
}
}
运行结果
原文链接:https://www.guoyunfeng.com/2018/05/31/number-counter/