storytellurx-v1/app/components/global-background-image.tsx
Elias Rhouzlane a82706a764 feat: Add Lottie to SVG conversion utility and integrate zipper animation
- Created a new utility script `lottie-to-svg.ts` to convert Lottie animations to SVG format using `bodymovin-to-smil`.
- Added a new Lottie animation file `zipper.json` for the zipper animation.
- Generated the corresponding SVG file `zipper.svg` from the Lottie animation.
- Updated `deno.json` to include new dependencies and references for the zipper animation.
- Modified `main.css` to introduce new color variables and styles for the zipper animation.
2025-07-01 04:23:37 +02:00

65 lines
2.2 KiB
TypeScript

"use client";
import { useEffect, useRef, useState } from "react";
import { useBackground } from "../game-engine/background-provider.tsx";
import cn from "@bureaudouble/outils/cn.ts";
export default function GlobalBackgroundImageDisplay(
{ className }: { className?: string },
) {
const { backgroundState } = useBackground();
const [currentImage, setCurrentImage] = useState<string | null>(null);
const [currentOpacity, setCurrentOpacity] = useState<number>(0);
const imgRef = useRef<HTMLImageElement>(null);
useEffect(() => {
if (backgroundState.imageUrl) {
// Preload image and then fade in
const img = new Image();
img.onload = () => {
setCurrentImage(backgroundState.imageUrl);
setCurrentOpacity(backgroundState.opacity);
};
img.src = backgroundState.imageUrl;
} else {
// // No image, fade out
// setCurrentOpacity(0);
// // Delay hiding the image to allow fade-out transition
// const timer = setTimeout(() => {
// setCurrentImage(null);
// }, 500); // Match CSS transition duration
// return () => clearTimeout(timer);
}
}, [backgroundState.imageUrl, backgroundState.opacity, backgroundState.key]);
const filters = [
"filter-[invert(1)_grayscale(100%)_sepia(100%)_hue-rotate(305deg)_saturate(150%)]",
"filter-[invert(100%)_grayscale(100%)_sepia(100%)_hue-rotate(305deg)_saturate(150%)_brightness(130%)] mix-blend-multiply",
"after:mix-blend-hard-light after:bg-mix-background after:z-10 after:inset-0 after:block after:relative after:size-full [&_img]:filter-[invert(1)_grayscale(1)_contrast(1.4)]",
];
return (
<div
key={backgroundState.key}
className={cn(
"absolute inset-0 z-0 pointer-events-none",
"fixed max-w-sm w-full m-auto",
filters.at(-1),
className,
)}
style={{
opacity: currentOpacity,
transition: "opacity 0.5s ease-in-out",
}}
>
{currentImage && (
<img
ref={imgRef}
src={currentImage}
alt="Narrative Background"
className="absolute inset-0 w-full h-full object-cover"
/>
)}
</div>
);
}