From 60e9130891af527d78e6ab6d83f435a5cee4a353 Mon Sep 17 00:00:00 2001 From: Emi Matchu Date: Wed, 12 Jun 2024 17:05:53 -0700 Subject: [PATCH] Fix bug where changing tabs would *always* pause the outfit animations We have a feature to check the movie's FPS, and pause it if it gets too low, as a guard against especially low-performance movies. But this was triggering in an *expected* case, where browsers intentionally throttle interval events when a page is in the background (e.g. you switch to another tab). Now, our rendering is aware of page visibility: when the page is hidden, don't bother rendering, and keep resetting the FPS counter state, so that we can pick up with a fresh FPS counter when the page comes back. --- .../components/OutfitMovieLayer.js | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/app/javascript/wardrobe-2020/components/OutfitMovieLayer.js b/app/javascript/wardrobe-2020/components/OutfitMovieLayer.js index 5706e017..2423e3ce 100644 --- a/app/javascript/wardrobe-2020/components/OutfitMovieLayer.js +++ b/app/javascript/wardrobe-2020/components/OutfitMovieLayer.js @@ -196,26 +196,37 @@ function OutfitMovieLayer({ let lastFpsLoggedAtInMs = performance.now(); let numFramesSinceLastLogged = 0; const intervalId = setInterval(() => { - updateStage(); - - numFramesSinceLastLogged++; - const now = performance.now(); const timeSinceLastFpsLoggedAtInMs = now - lastFpsLoggedAtInMs; const timeSinceLastFpsLoggedAtInSec = timeSinceLastFpsLoggedAtInMs / 1000; + const fps = numFramesSinceLastLogged / timeSinceLastFpsLoggedAtInSec; + const roundedFps = Math.round(fps * 100) / 100; - if (timeSinceLastFpsLoggedAtInSec > 2) { - const fps = numFramesSinceLastLogged / timeSinceLastFpsLoggedAtInSec; - const roundedFps = Math.round(fps * 100) / 100; + // If the page is visible, render the next frame, and track that we did. + // And if it's been 2 seconds since the last time we logged the FPS, + // compute and log the FPS during those two seconds. (Checking the page + // visibility is both an optimization to avoid rendering the movie, but + // also makes "low FPS" tracking more accurate: browsers already throttle + // intervals when the page is hidden, so a low FPS is *expected*, and + // wouldn't indicate a performance problem like a low FPS normally would.) + if (!document.hidden) { + updateStage(); + numFramesSinceLastLogged++; - console.debug( - `[OutfitMovieLayer] FPS: ${roundedFps} (Target: ${targetFps}) (${libraryUrl})`, - ); + if (timeSinceLastFpsLoggedAtInSec > 2) { + console.debug( + `[OutfitMovieLayer] FPS: ${roundedFps} (Target: ${targetFps}) (${libraryUrl})`, + ); + if (onLowFps && fps < 2) { + onLowFps(fps); + } - if (onLowFps && fps < 2) { - onLowFps(fps); + lastFpsLoggedAtInMs = now; + numFramesSinceLastLogged = 0; } - + } else { + // Otherwise, if the page is hidden, keep resetting the FPS tracker + // state, to be able to pick up counting fresh once we come back. lastFpsLoggedAtInMs = now; numFramesSinceLastLogged = 0; }