目前项目团队在做一个接受到的数字要做成实现数字滚动效果 在轮询的基础上做的 就是上一个数字到目前最新的数字的变化 我想到的就是用使用setTimeout定时器,敬请读到文章结尾再提出自己的意见,前面都是不完善的思路,最后尚可用
/**
* 定时器方法
* @param num {number} 当前值
* @param digitalBeating {number} 上一个值
* @param diff {number} num - digitalBeating的差值
*/
transNum(num,digitalBeating,diff) {
let i = digitalBeating,timeout = null;
const animate = () => {
if (i<num) {
i++
timeout = setTimeout(animate,1000/diff)
} else {
digitalBeating = num
}
let numList = i.toString().split('') // 这里是为了以数组的形式单独展示每一个数字
}
animate()
}
那么写到这里是不是就可以简单实现了呢 其实还有一段路要走,比如你考虑了性能问题了吗
transNum(num,digitalBeating,diff) {
let i = digitalBeating,timeout = null;
const animate = () => {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
if (i<num) {
i++
timeout = setTimeout(animate,1000/diff)
} else {
digitalBeating = num
}
let numList = i.toString().split('') // 这里是为了以数组的形式单独展示每一个数字
}
animate()
}
上一步我们增加了防抖就是在递归调用当上一个setTimeout还存在的时候 我们要清除掉setTimeout 以免造成干扰和卡顿现象 那么是不是就可以了呢 你考虑过1秒之内setTimeout也有极限的吗 根据各个浏览器的性能差异,应该控制在20ms~30ms之间,那么我们进行下一步的改造
transNum(num,digitalBeating,diff) {
let i = digitalBeating,timeout = null,steps = 1;
const animate = () => {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
if (i<num) {
if(diff<=50) {
steps = 1
} else if (diff=<100&&diff>50) {
steps = 2
} else if (diff>100&&diff<150) {
steps = 3
} else if ...
i += steps
timeout = setTimeout(animate,1000*steps/diff)
} else {
digitalBeating = num
}
let numList = i.toString().split('') // 这里是为了以数组的形式单独展示每一个数字
}
animate()
}
我们定义了一个变量steps作为步长来保证1秒之内调用的次数,可是以上的行为会不会很蠢 因为你没办法知道这个diff差值到底是多少 没有封顶的 所以我们继续改造
transNum(num,digitalBeating,diff) {
let i = digitalBeating,
timeout = null,
len = diff.toString().length,
steps = Math.pow(10,len-2); // 这里步长设置最为关键,这样取值可保证频率(<100)
const animate = () => {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
if (i<num) {
if(diff<10) {
steps = 1
}
if (diff/steps>50) {
steps *= 2
}
i += steps
timeout = setTimeout(animate,1000*steps/diff)
} else {
digitalBeating = num
}
let numList = i.toString().split('') // 这里是为了以数组的形式单独展示每一个数字
}
animate()
}
目前来看大概就可以保证在1秒之内调用次数50次了,不过呢,你可能会发现最后一次的数字变动可能是i>num了 那么肯定就不是我们想要的结果了,就在最后一次再加个判断吧
transNum(num,digitalBeating,diff) {
let i = digitalBeating,
timeout = null,
len = diff.toString().length,
steps = Math.pow(10,len-2); // 这里步长设置最为关键,这样取值可保证频率(<100)
const animate = () => {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
if (i<num) {
if(diff<10) {
steps = 1
}
if (diff/steps>50) {
steps *= 2
}
i += steps
timeout = setTimeout(animate,1000*steps/diff)
// 这里作为最后一次判断 如果最后一次的i和num的差距小于steps时 肯定会跳过了
if (num-i<steps) {
i = num
}
} else {
digitalBeating = num
}
let numList = i.toString().split('') // 这里是为了以数组的形式单独展示每一个数字
}
animate()
}
再来看看 是不是基本上能满足需求了呢 那么你能保证不会延迟吗 所以应该规定在1秒之内必须跑完了
transNum(num,digitalBeating,diff) {
let i = digitalBeating,
timeout = null,
len = diff.toString().length,
steps = Math.pow(10,len-2), // 这里步长设置最为关键,这样取值可保证频率(<100)
n1 = 10, // 设置两个限制条件n1,n2 可以进一步规范步长(>=1)和频率(<50次)
n2 = 50,
_lastTime = new Date(); // 设置初始时间
const animate = () => {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
let _nowTime = new Date() // 这一轮调用时的时间
// 在这里统一判断
if (i<num && num-i>=steps && _nowTime - _lastTime<1000) {
if(diff<n1) {
steps = 1
}
if (diff/steps>n2) {
steps *= 2
}
i += steps
timeout = setTimeout(animate,1000*steps/diff)
} else {
i = num
clearTimeout(timeout)
timeout = null
digitalBeating = num
}
let numList = i.toString().split('') // 这里是为了以数组的形式单独展示每一个数字
}
animate()
}
唔,现在也许可行了吧 然而做了这么多 其实还是不够完美的 一个是数字滚动就不是一个数字一个数字的滚了 而且用了定时器了 这个本身就是耗费性能的事 有没有更优的办法呢 当然有 请期待用css transition 3d来实现