ik hou van kaas

This commit is contained in:
ben
2026-08-19 11:56:44 +02:00
parent 8fe812e8e6
commit 468c34ff92
9 changed files with 320 additions and 107 deletions
@@ -0,0 +1,81 @@
"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<Layer[]>(() => [{ id: idRef.current, src }])
const [revealed, setRevealed] = useState<Set<number>>(() => 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 (
<div className="absolute inset-0 overflow-hidden">
{layers.map((layer, i) => {
const isTop = i === layers.length - 1
const isRevealed = revealed.has(layer.id)
return (
<Image
key={layer.id}
src={layer.src || "/placeholder.svg"}
alt=""
fill
priority
className="object-cover transition-opacity ease-in-out"
style={{
transitionDuration: `${durationMs}ms`,
opacity: isRevealed ? 1 : 0,
}}
onLoadingComplete={() => reveal(layer.id)}
onTransitionEnd={() => {
if (isTop && layer.id === topId) handleTopLayerTransitionEnd(layer.id)
}}
/>
)
})}
</div>
)
}