59 lines
1.8 KiB
TypeScript
59 lines
1.8 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import { motion } from "framer-motion";
|
|
import { useState } from "react";
|
|
import { ARTIST_PLACEHOLDER_SRC } from "@/lib/constants";
|
|
|
|
type Props = {
|
|
src: string;
|
|
alt: string;
|
|
};
|
|
|
|
export function ReleaseCover({ src: initialSrc, alt }: Props) {
|
|
const [imgError, setImgError] = useState(false);
|
|
const [imgLoaded, setImgLoaded] = useState(false);
|
|
|
|
const src = imgError ? ARTIST_PLACEHOLDER_SRC : initialSrc;
|
|
|
|
return (
|
|
<motion.div
|
|
key={src}
|
|
className=""
|
|
initial={{ opacity: 0, filter: "blur(10px)" }}
|
|
animate={
|
|
imgLoaded ? { opacity: 1, filter: "blur(0px)" } : { opacity: 0, filter: "blur(10px)" }
|
|
}
|
|
transition={{ duration: 1, ease: "easeInOut" }}
|
|
>
|
|
<div className="mx-auto w-full">
|
|
<div className="group relative isolate mx-auto aspect-square w-full overflow-visible rounded-2xl">
|
|
<div
|
|
className="pointer-events-none absolute -inset-6 z-0"
|
|
style={{ "--cover-url": `url(${src})` } as React.CSSProperties}
|
|
>
|
|
<div
|
|
id="blurred-bg"
|
|
key={src}
|
|
aria-hidden="true"
|
|
className={`release-blur pointer-events-none absolute -inset-6 z-0 scale-105 rounded-3xl ${imgLoaded ? "loaded" : ""}`}
|
|
/>
|
|
</div>
|
|
|
|
<div className="absolute inset-0 z-10 overflow-hidden rounded-2xl">
|
|
<Image
|
|
src={src}
|
|
alt={alt}
|
|
fill
|
|
sizes="(max-width: 800px) calc(100vw - 32px), 600px"
|
|
priority
|
|
style={{ objectFit: "cover" }}
|
|
onLoad={() => setImgLoaded(true)}
|
|
onError={() => setImgError(true)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
}
|