簡體   English   中英

Canvas 繪制速度不一致(requestAnimationFrame)

[英]Canvas is drawing with inconsistent speed (requestAnimationFrame)

我有最簡單直接的 animation 和 canvas:

 const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ctx.canvas.width = 700; ctx.canvas.height = 300; var x = 0; var update = function() { x = x + 6; } var draw = function() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillRect(x, 10, 30, 30); } let lastRenderTime = 0 const frameRate = 60; function main(currentTime) { window.requestAnimationFrame(main) const secondsSinceLastRender = (currentTime - lastRenderTime) / 1000 if (secondsSinceLastRender < 1 / frameRate) return lastRenderTime = currentTime update() draw() } window.requestAnimationFrame(main)
 <canvas id="canvas"></canvas>

它只是一個從左到右移動的矩形。

但是,即使在我功能強大的 PC 上,它的運行也不一致(您可以看到它對於 60 fps 不夠流暢,而且速度也在變化)。

是我做錯了什么還是這就是 canvas 的工作原理?

是的,你做錯了幾件事。

作為一般規則,您不應該將距離增加一個固定量,而是使用增量時間來確定您的 object 自上一幀以來應該移動了多少。

這是因為requestAnimationFrame (rAF) 可能不會定期觸發,例如,如果瀏覽器有很多事情要並行執行,則下一個 rAF 循環可能會延遲。 無論如何,您無法確定 rAF 回調將以何種速率觸發。 這將取決於用戶顯示器的刷新率。


在這里,您嘗試設置 60FPS 的最大幀速率,我假設您認為這將允許您使用固定的增量值,因為該代碼應該控制幀速率。

但此代碼僅適用於幀速率是目標 FPS 的倍數(例如 120Hz、240Hz)的情況。 其他所有幀速率都會受到此代碼的影響,並且正如我們之前所說,幀速率不應被認為是穩定的,即使是 120Hz 和 240Hz 的顯示器也會受到影響。
(請注意,在刷新率低於 60Hz 的顯示器上,此代碼也無法幫助他們趕上延遲。)

讓我們以 75Hz 的顯示器為例(因為它實際上很常見,因為它是一個很好的例子),沒有任何東西干擾頁面,因此沒有任何“穩定”的幀速率。
每幀的持續時間應為 1s/75 -> ~13.33333ms。 在更新對象的 position 之前,您的代碼會檢查幀的持續時間是否高於 1s/60 -> ~16.66666ms。

在這個 75Hz 監視器上,每一幀都會失敗,因此 position 只會在下一幀更新:

第一幀 第二幀 第三幀 第 4 幀
時鍾時間 13.33333ms 26.66666ms 39.99999ms 53.33332ms
最后油漆 0ms 0ms 26.66666ms 26.66666ms
距最后一次油漆時間 13.33333ms 26.66666ms 13.33333ms 26.66666ms
地位 丟棄 丟棄
x position 0像素 6像素 6像素 12像素

當在具有相同穩定條件的 60Hz 顯示器上時

第一幀 第二幀 第三幀 第 4 幀
時鍾時間 16.66666ms 33.33333ms 49.99999ms 66.66666ms
最后油漆 0ms 16.66666ms 33.33333ms 49.99999ms
距最后一次油漆時間 16.66666ms 16.66666ms 16.66666ms 16.66666ms
地位
x position 6像素 12像素 18像素 24像素

因此,您可以看到 50 毫秒后,75Hz 設置的x值如何仍為 6 像素,而在最佳條件下它應該已經為 18 像素,以及我們如何最終僅以 37.5 FPS 而不是目標 60 FPS 進行繪畫。


您可能不在 75Hz 顯示器上,但在我的 macOS Firefox 上,它確實從 CPU 計算 rAF 的速率,而不是查看顯示器的刷新率,我最終會遇到更糟糕的情況,幀大約需要 16.65 毫秒,這意味着要遍歷 canvas,它需要的時間實際上是沒有幀速率限制的時間的兩倍。


為了避免這種情況,請使用增量時間來確定 object 的 position。 這樣,無論兩幀之間的延遲,無論顯示器的刷新率等,您的 object 都將在正確的 position 處呈現,即使您丟掉一兩幀,您的 Z6F1C25ED1523962F1BBF9DEE9BE509 也不會跳轉。

 const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ctx.canvas.width = 700; ctx.canvas.height = 300; var x = 0; const px_per_frame_at_60Hz = 6; const px_per_second = (px_per_frame_at_60Hz * 60); var update = function( elapsed_time ) { const distance = elapsed_time * px_per_second; x = (x + distance) % canvas.width; } var draw = function() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillRect(x, 10, 30, 30); } let lastRenderTime = 0 const frameRate = 60; function main(currentTime) { const secondsSinceLastRender = (currentTime - lastRenderTime) / 1000 update( secondsSinceLastRender ); draw(); lastRenderTime = currentTime; // better keep it at the end in case something throws in this callback, // we don't want it to throw every painting frames indefinitely window.requestAnimationFrame(main) } window.requestAnimationFrame(main)
 <canvas id="canvas"></canvas>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM