- store: boomKey counter + boom() action
- Countdown fires boom() once when it reaches 0 (timer expiry only)
- Boom component (portal to body) plays a fullscreen white flash + giant 💥
- rendered at RoomPage level, keyed by boomKey so it plays fully (independent
of the countdown unmounting on reveal) and replays each round
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
35 lines
1.1 KiB
TypeScript
35 lines
1.1 KiB
TypeScript
import { createPortal } from "react-dom"
|
|
import { motion } from "framer-motion"
|
|
|
|
/**
|
|
* Explosion plein écran : flash blanc + 💥 géant.
|
|
* Rendue dans document.body via portal pour échapper aux transforms parents,
|
|
* et montée avec une `key` qui change → l'animation rejoue à chaque boom.
|
|
*/
|
|
export function Boom() {
|
|
return createPortal(
|
|
<div className="pointer-events-none fixed inset-0 z-50 flex items-center justify-center overflow-hidden">
|
|
<motion.div
|
|
className="absolute inset-0 bg-white"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: [0, 0.9, 0] }}
|
|
transition={{ duration: 0.5, times: [0, 0.12, 1], ease: "easeOut" }}
|
|
/>
|
|
<motion.span
|
|
className="relative leading-none select-none"
|
|
style={{ fontSize: "45vmin" }}
|
|
initial={{ scale: 0, rotate: -20, opacity: 0 }}
|
|
animate={{
|
|
scale: [0, 1.35, 1.1],
|
|
rotate: [-20, 12, 0],
|
|
opacity: [0, 1, 0],
|
|
x: [0, 12, -8, 0],
|
|
}}
|
|
transition={{ duration: 0.8, times: [0, 0.3, 1], ease: "easeOut" }}
|
|
>
|
|
💥
|
|
</motion.span>
|
|
</div>,
|
|
document.body
|
|
)
|
|
}
|