【渲染優化】動態調整虛擬列表刷新率:讓代碼學會"偷懶"
引擎版本:Cocos Creator 3.x
閱讀時間:5分鐘(能幫你省8小時調試)
?? 翻車現場
那天是周五下午 4 點半,我正打算提交代碼下班。
測試小姐姐突然跑過來一句:
“你這個商城列表,在我手機上滑動的時候卡得我想摔手機!”
我一臉懵逼:
“啊?我在 iPhone 14 Pro 上測試很絲滑啊!”
拿過她的紅米 Note 9 一滑——
臥槽,真的卡。
再試試快速滑動——
臥槽,更卡了。
我打開 Chrome DevTools 看性能面板,幀率從 60 狂掉到 25,一大片紅色警告條刺眼地躺在那里:
OnScrolling() - 35ms ??
UpdateVisibleItems() - 28ms ??
CalculateVisibleRange() - 15ms ??
心里默默地嘆了口氣:
“完蛋鳥,又是一個不眠的周末。”
?? 問題的根源
我翻開了以前寫的虛擬列表代碼:
// 我之前的寫法(估計很多人也這么寫)
private onScrolling(): void {
this.updateVisibleItems(); // 每次滾動都刷新
}
看起來挺合理的對吧?
滾動更新可見項,天經地義。
但我忽略了一個殘酷事實:
Cocos 的 scrolling 事件,在快速滑動時,1 秒能觸發 60 次!
也就是說,我的 updateVisibleItems() 每秒執行 60 次,干的事還挺重:
- 計算可見區間(遍歷位置數組)
- 回收舊節點(遍歷 Map)
- 創建新節點(對象池、設置位置、更新數據)
60 次 × 每次 30ms = 1800ms 的計算量。
一秒鐘才 1000ms,非要干 1800ms 的活兒——
不卡才怪。
?? 靈魂拷問:真的需要刷新那么快嗎?
我冷靜想了想。
當用戶快速甩動列表時,他真的能看清里面的內容嗎?
答案是:根本看不清。
你可以自己試試,打開淘寶、微信朋友圈,快速滑動時會發現:
- 內容略模糊(其實沒完整渲染)
- 停下來才變清晰
- 但完全不會覺得“卡”
原來大廠早就在玩這個騷操作:快速滾動時,故意降低刷新率。
就像拍電影——打斗戲 24 幀就夠,定格鏡頭才用 60 幀。
?? 我的方案:讓列表自己決定要不要刷新
核心思路特別簡單,就四句話:
1?? 慢速滑動 → 用戶能看清 → 必須刷新 → 60fps
2?? 快速滑動 → 用戶看不清 → 少刷幾次 → 20fps
3?? 卡頓明顯 → 設備吃不消 → 降級模式
4?? 滑動停止 → 強制刷新 → 確保顯示完整
聽起來挺復雜,其實不到 100 行代碼就能搞定。
?? 第一步:給列表裝個“速度計”
export class PerformanceManager {
private lastPos = 0;
private lastTime = 0;
private speed = 0;
shouldUpdate(currentPos: number): boolean {
const now = Date.now();
const timePassed = now - this.lastTime;
if (timePassed < 16) return false; // 每幀間隔16ms
const moved = Math.abs(currentPos - this.lastPos);
this.speed = moved / (timePassed / 1000);
if (this.speed > 2000) {
if (timePassed < 50) return false; // 飛速滑動,20fps
} else if (this.speed > 1000) {
if (timePassed < 33) return false; // 快速滑動,30fps
}
this.lastPos = currentPos;
this.lastTime = now;
return true;
}
}
簡單說:
速度越快 → 刷新越少 → 性能越穩。
?? 第二步:改造滾動邏輯
private onScrolling(): void {
const offset = this.scrollView.getScrollOffset();
const pos = this.scrollView.vertical ? offset.y : offset.x;
if (this.perfManager.shouldUpdate(pos)) {
this.updateVisibleItems(); // 該刷才刷
}
this.onScrollProgress();
}
private onScrollEnded(): void {
this.updateVisibleItems(); // 停下來強制刷新
}
只加了一個判斷,改動極小,零侵入。
??? 第三步:加個“低性能模式”
recordFrameTime(time: number): void {
this.frameTimes.push(time);
if (this.frameTimes.length > 5) this.frameTimes.shift();
const slowFrames = this.frameTimes.filter(t => t > 16).length;
this.isLowPerformanceMode = slowFrames >= 3;
if (this.isLowPerformanceMode) {
console.warn('?? 進入低性能模式,降低刷新率保證流暢');
}
}
當連續幾幀掉幀,就自動降級刷新頻率。
你說是不是很“智能”???
?? 實測結果
| 手機型號 | 優化前 | 優化后 | 提升幅度 | 刷新次數/秒 |
|---|---|---|---|---|
| iPhone 14 Pro | 54 fps | 60 fps | ↑ 11% | 60 → 35 |
| 小米 12 | 38 fps | 55 fps | ↑ 45% | 60 → 28 |
| 紅米 Note 9 | 23 fps | 42 fps | ↑ 83% | 60 → 18 |
沒看錯,低端機性能提升了 83%!
而代碼改動量,不到 100 行。
?? 進階優化:動態緩沖區
再來一個錦上添花的小優化——
private getBufferSize(): number {
const speed = this.perfManager.getSpeed();
if (speed > 2000) return 3; // 飛速滑動:3屏緩沖
if (speed > 1000) return 2; // 快速滑動:2屏緩沖
return 1; // 慢速滑動:1屏緩沖
}
滑得越快,就多加載幾屏內容,防止白屏。
?? 核心總結
1?? 不要盲目追求高刷新率。
2?? 用數據說話,不憑感覺。
3?? 性能優化要有取舍,不是一味地“更快”。
?? 寫在最后
虛擬列表的性能優化,說到底是一場“感知”與“取舍”的平衡。
一句話總結:
好的性能優化,不是讓程序跑得更快,而是讓程序知道什么時候該偷懶。
就像武俠小說里的高手,不是每一招都用盡全力,而是知道——
何時發力,何時卸力。
查看源碼
?? 《高性能實用虛擬列表》
核心特性:
- 支持垂直/水平/網格布局
- 動態高度 / 寬度
- 自適應性能優化
- 對象池自動管理
- 插入 / 刪除動畫
?? 常見問題
Q:會看到空白嗎?
A:不會。快速滑動時自動擴展緩沖區。
Q:兼容性如何?
A:Cocos Creator 2.x / 3.x 通吃,原生、小游戲、H5 全支持。
Q:老項目能用嗎?
A:能,只需在 onScrolling 里加一句判斷。
?? 最后
如果你也在為性能掉幀頭疼,希望這篇文章能幫你少熬幾個夜。
也歡迎留言分享你的思路,讓我們一起把 Cocos 的生態做得更好。
祝大家的列表都絲滑如德芙!??


浙公網安備 33010602011771號