perf(preview): debounce full-res /proxy preload by 400ms
Rapid arrow-nav was firing one /proxy fetch per photo with no way to abort (new Image() has no abort). Holding the right arrow through ten photos in two seconds left ten multi-MB transfers in flight competing for bandwidth and the RAW/HEIC transcoder. Now the preload only kicks in if the user lingers on a photo for 400ms; otherwise the timer is cleared and no /proxy request is made.
This commit is contained in:
@@ -59,21 +59,30 @@ function PreviewStillImage({ photo }: { photo: Photo }) {
|
|||||||
const fullResSrc = getPreviewFullResSrc(photo)
|
const fullResSrc = getPreviewFullResSrc(photo)
|
||||||
const src = usingFullRes ? fullResSrc : thumbSrc
|
const src = usingFullRes ? fullResSrc : thumbSrc
|
||||||
|
|
||||||
// Reset everything when the photo changes, then start the background
|
// Reset everything when the photo changes, then queue a *debounced*
|
||||||
// full-res preload. Cancel the preloader's callback on unmount/change so a
|
// background full-res preload. Debouncing matters because `new Image()`
|
||||||
// late-arriving onload from the previous photo can't flip state for the
|
// requests can't be aborted: rapid arrow-nav would otherwise leave a
|
||||||
// current one.
|
// dozen multi-MB /proxy fetches in flight, saturating the user's
|
||||||
|
// bandwidth (and the backend's transcoder for RAW/HEIC) for photos the
|
||||||
|
// user already navigated past. Only photos the user lingers on for
|
||||||
|
// FULL_RES_PRELOAD_DELAY_MS trigger the /proxy fetch.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoaded(false)
|
setLoaded(false)
|
||||||
setUsingFullRes(false)
|
setUsingFullRes(false)
|
||||||
setScale(1)
|
setScale(1)
|
||||||
setOffset({ x: 0, y: 0 })
|
setOffset({ x: 0, y: 0 })
|
||||||
|
|
||||||
const preloader = new Image()
|
const FULL_RES_PRELOAD_DELAY_MS = 400
|
||||||
preloader.onload = () => setUsingFullRes(true)
|
let preloader: HTMLImageElement | null = null
|
||||||
preloader.src = fullResSrc
|
const timer = window.setTimeout(() => {
|
||||||
|
preloader = new Image()
|
||||||
|
preloader.onload = () => setUsingFullRes(true)
|
||||||
|
preloader.src = fullResSrc
|
||||||
|
}, FULL_RES_PRELOAD_DELAY_MS)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
preloader.onload = null
|
window.clearTimeout(timer)
|
||||||
|
if (preloader) preloader.onload = null
|
||||||
}
|
}
|
||||||
}, [photo.id, fullResSrc])
|
}, [photo.id, fullResSrc])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user