fix(viewer): kick render on resume (#516)

Co-authored-by: mvanhorn <mvanhorn@users.noreply.github.com>
This commit is contained in:
Aymeric Rabot
2026-07-19 17:46:17 +02:00
committed by GitHub
co-authored by mvanhorn
parent f67787077e
commit e484e17f82
@@ -37,13 +37,15 @@ const FrameLimiter: React.FC<FrameLimiterProps> = ({ fps = 50 }) => {
let timer: ReturnType<typeof setInterval> | null = null let timer: ReturnType<typeof setInterval> | null = null
let sizeSynced = false let sizeSynced = false
const interval = 1000 / fps const interval = 1000 / fps
function syncSize() {
if (sizeSynced) return
renderer.setPixelRatio(dpr)
renderer.setSize(size.width, size.height, false)
sizeSynced = true
}
function tick(t: DOMHighResTimeStamp) { function tick(t: DOMHighResTimeStamp) {
raf = requestAnimationFrame(tick) raf = requestAnimationFrame(tick)
if (!sizeSynced) { syncSize()
renderer.setPixelRatio(dpr)
renderer.setSize(size.width, size.height, false)
sizeSynced = true
}
elapsed = t - then elapsed = t - then
if (elapsed > interval) { if (elapsed > interval) {
advance(i) advance(i)
@@ -51,6 +53,14 @@ const FrameLimiter: React.FC<FrameLimiterProps> = ({ fps = 50 }) => {
then = t - (elapsed % interval) then = t - (elapsed % interval)
} }
} }
function kick() {
syncSize()
i += 1 / 1000
advance(i)
}
function onVisibilityChange() {
if (document.visibilityState === 'visible') kick()
}
// Set frameloop to never, it will shut down the default render loop // Set frameloop to never, it will shut down the default render loop
set({ frameloop: 'never' }) set({ frameloop: 'never' })
if (DRAW_DISABLED) { if (DRAW_DISABLED) {
@@ -61,6 +71,11 @@ const FrameLimiter: React.FC<FrameLimiterProps> = ({ fps = 50 }) => {
} else { } else {
// Kick off custom render loop // Kick off custom render loop
raf = requestAnimationFrame(tick) raf = requestAnimationFrame(tick)
// rAF can stall while a tab is hidden, unfocused, or occluded. With the
// default loop disabled, force one current frame as soon as it resumes.
document.addEventListener('visibilitychange', onVisibilityChange)
window.addEventListener('focus', kick)
window.addEventListener('pageshow', kick)
} }
// Restore initial setting // Restore initial setting
return () => { return () => {
@@ -70,6 +85,9 @@ const FrameLimiter: React.FC<FrameLimiterProps> = ({ fps = 50 }) => {
if (timer) { if (timer) {
clearInterval(timer) clearInterval(timer)
} }
document.removeEventListener('visibilitychange', onVisibilityChange)
window.removeEventListener('focus', kick)
window.removeEventListener('pageshow', kick)
set({ frameloop: initFrameloop }) set({ frameloop: initFrameloop })
} }
}, [advance, dpr, fps, initFrameloop, renderPaused, renderer, set, size.height, size.width]) }, [advance, dpr, fps, initFrameloop, renderPaused, renderer, set, size.height, size.width])