目前專案團隊在做一個接受到的數字要做成實現數字滾動效果 在輪詢的基礎上做的 就是上一個數字到目前最新的數字的變化 我想到的就是用使用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來實現