"use client" import { useEffect, useRef, useState } from "react" import Image from "next/image" interface RoomBackgroundProps { src: string durationMs?: number } interface Layer { id: number src: string } // Stacks background images and crossfades between them whenever `src` // changes. Each new layer only starts fading in once it has actually // finished loading, and layers below a fully-faded-in layer are dropped // on transitionend rather than a guessed timeout — this keeps rapid // room switches (and slow/cold-cache images) visually consistent. export function RoomBackground({ src, durationMs = 700 }: RoomBackgroundProps) { const idRef = useRef(0) const [layers, setLayers] = useState(() => [{ id: idRef.current, src }]) const [revealed, setRevealed] = useState>(() => new Set([idRef.current])) useEffect(() => { setLayers((prev) => { if (prev[prev.length - 1]?.src === src) return prev idRef.current += 1 return [...prev, { id: idRef.current, src }] }) }, [src]) const reveal = (id: number) => { setRevealed((prev) => { if (prev.has(id)) return prev const next = new Set(prev) next.add(id) return next }) } const handleTopLayerTransitionEnd = (id: number) => { // Once the newest layer has finished fading in, everything below it // is fully hidden and can be dropped. setLayers((prev) => { const idx = prev.findIndex((l) => l.id === id) if (idx <= 0) return prev return prev.slice(idx) }) } const topId = layers[layers.length - 1]?.id return (
{layers.map((layer, i) => { const isTop = i === layers.length - 1 const isRevealed = revealed.has(layer.id) return ( reveal(layer.id)} onTransitionEnd={() => { if (isTop && layer.id === topId) handleTopLayerTransitionEnd(layer.id) }} /> ) })}
) }