Add media card flare

This commit is contained in:
mrjvs 2023-08-20 21:03:59 +02:00
parent 88beacde1a
commit 7251b39cc3
5 changed files with 83 additions and 32 deletions

View File

@ -1,9 +1,11 @@
import c from "classnames";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { TMDBMediaToId } from "@/backend/metadata/tmdb"; import { TMDBMediaToId } from "@/backend/metadata/tmdb";
import { MWMediaMeta } from "@/backend/metadata/types/mw"; import { MWMediaMeta } from "@/backend/metadata/types/mw";
import { DotList } from "@/components/text/DotList"; import { DotList } from "@/components/text/DotList";
import { Flare } from "@/components/utils/Flare";
import { IconPatch } from "../buttons/IconPatch"; import { IconPatch } from "../buttons/IconPatch";
import { Icons } from "../Icon"; import { Icons } from "../Icon";
@ -39,19 +41,27 @@ function MediaCardContent({
if (media.year) dotListContent.push(media.year); if (media.year) dotListContent.push(media.year);
return ( return (
<div <Flare.Base
className={`group -m-3 mb-2 rounded-xl bg-denim-300 bg-opacity-0 transition-colors duration-100 ${ className={`group -m-3 mb-2 rounded-xl bg-background-main transition-colors duration-100 ${
canLink ? "hover:bg-opacity-100" : "" canLink ? "hover:bg-mediaCard-hoverBackground" : ""
}`} }`}
> >
<article <Flare.Light
flareSize={300}
cssColorVar="--colors-mediaCard-hoverAccent"
backgroundClass="bg-mediaCard-hoverBackground duration-100"
className={c({
"rounded-xl bg-background-main group-hover:opacity-100": canLink,
})}
/>
<Flare.Child
className={`pointer-events-auto relative mb-2 p-3 transition-transform duration-100 ${ className={`pointer-events-auto relative mb-2 p-3 transition-transform duration-100 ${
canLink ? "group-hover:scale-95" : "" canLink ? "group-hover:scale-95" : ""
}`} }`}
> >
<div <div
className={[ className={[
"relative mb-4 aspect-[2/3] w-full overflow-hidden rounded-xl bg-denim-500 bg-cover bg-center transition-[border-radius] duration-100", "relative mb-4 aspect-[2/3] w-full overflow-hidden rounded-xl bg-mediaCard-hoverBackground bg-cover bg-center transition-[border-radius] duration-100",
closable ? "" : "group-hover:rounded-lg", closable ? "" : "group-hover:rounded-lg",
].join(" ")} ].join(" ")}
style={{ style={{
@ -61,13 +71,12 @@ function MediaCardContent({
{series ? ( {series ? (
<div <div
className={[ className={[
"absolute right-2 top-2 rounded-md bg-denim-200 px-2 py-1 transition-colors", "absolute right-2 top-2 rounded-md bg-mediaCard-badge px-2 py-1 transition-colors",
closable ? "" : "group-hover:bg-denim-500",
].join(" ")} ].join(" ")}
> >
<p <p
className={[ className={[
"text-center text-xs font-bold text-slate-400 transition-colors", "text-center text-xs font-bold text-mediaCard-badgeText transition-colors",
closable ? "" : "group-hover:text-white", closable ? "" : "group-hover:text-white",
].join(" ")} ].join(" ")}
> >
@ -82,19 +91,19 @@ function MediaCardContent({
{percentage !== undefined ? ( {percentage !== undefined ? (
<> <>
<div <div
className={`absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-denim-300 to-transparent transition-colors ${ className={`absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-mediaCard-shadow to-transparent transition-colors ${
canLink ? "group-hover:from-denim-100" : "" canLink ? "group-hover:from-mediaCard-hoverShadow" : ""
}`} }`}
/> />
<div <div
className={`absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-denim-300 to-transparent transition-colors ${ className={`absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-mediaCard-shadow to-transparent transition-colors ${
canLink ? "group-hover:from-denim-100" : "" canLink ? "group-hover:from-mediaCard-hoverShadow" : ""
}`} }`}
/> />
<div className="absolute inset-x-0 bottom-0 p-3"> <div className="absolute inset-x-0 bottom-0 p-3">
<div className="relative h-1 overflow-hidden rounded-full bg-denim-600"> <div className="relative h-1 overflow-hidden rounded-full bg-mediaCard-barColor">
<div <div
className="absolute inset-y-0 left-0 rounded-full bg-bink-700" className="absolute inset-y-0 left-0 rounded-full bg-mediaCard-barFillColor"
style={{ style={{
width: percentageString, width: percentageString,
}} }}
@ -105,13 +114,13 @@ function MediaCardContent({
) : null} ) : null}
<div <div
className={`absolute inset-0 flex items-center justify-center bg-denim-200 bg-opacity-80 transition-opacity duration-200 ${ className={`absolute inset-0 flex items-center justify-center bg-mediaCard-badge bg-opacity-80 transition-opacity duration-200 ${
closable ? "opacity-100" : "pointer-events-none opacity-0" closable ? "opacity-100" : "pointer-events-none opacity-0"
}`} }`}
> >
<IconPatch <IconPatch
clickable clickable
className="text-2xl text-slate-400" className="text-2xl text-mediaCard-badgeText"
onClick={() => closable && onClose?.()} onClick={() => closable && onClose?.()}
icon={Icons.X} icon={Icons.X}
/> />
@ -121,8 +130,8 @@ function MediaCardContent({
<span>{media.title}</span> <span>{media.title}</span>
</h1> </h1>
<DotList className="text-xs" content={dotListContent} /> <DotList className="text-xs" content={dotListContent} />
</article> </Flare.Child>
</div> </Flare.Base>
); );
} }

View File

@ -30,13 +30,14 @@ function Light(props: FlareProps) {
function mouseMove(e: MouseEvent) { function mouseMove(e: MouseEvent) {
if (!outerRef.current) return; if (!outerRef.current) return;
const rect = outerRef.current.getBoundingClientRect(); const rect = outerRef.current.getBoundingClientRect();
const halfSize = size / 2;
outerRef.current.style.setProperty( outerRef.current.style.setProperty(
"--bg-x", "--bg-x",
`${(e.clientX - rect.left - size / 2).toFixed(0)}px` `${(e.clientX - rect.left - halfSize).toFixed(0)}px`
); );
outerRef.current.style.setProperty( outerRef.current.style.setProperty(
"--bg-y", "--bg-y",
`${(e.clientY - rect.top - size / 2).toFixed(0)}px` `${(e.clientY - rect.top - halfSize).toFixed(0)}px`
); );
} }
document.addEventListener("mousemove", mouseMove); document.addEventListener("mousemove", mouseMove);
@ -58,13 +59,12 @@ function Light(props: FlareProps) {
backgroundImage: `radial-gradient(circle at center, rgba(var(${cssVar}), 1), rgba(var(${cssVar}), 0) 70%)`, backgroundImage: `radial-gradient(circle at center, rgba(var(${cssVar}), 1), rgba(var(${cssVar}), 0) 70%)`,
backgroundPosition: `var(--bg-x) var(--bg-y)`, backgroundPosition: `var(--bg-x) var(--bg-y)`,
backgroundRepeat: "no-repeat", backgroundRepeat: "no-repeat",
backgroundAttachment: "fixed",
backgroundSize: `${size.toFixed(0)}px ${size.toFixed(0)}px`, backgroundSize: `${size.toFixed(0)}px ${size.toFixed(0)}px`,
}} }}
> >
<div <div
className={c( className={c(
"absolute inset-[2px] overflow-hidden", "absolute inset-[1px] overflow-hidden",
props.className, props.className,
props.backgroundClass props.backgroundClass
)} )}
@ -75,7 +75,6 @@ function Light(props: FlareProps) {
background: `radial-gradient(circle at center, rgba(var(${cssVar}), 1), rgba(var(${cssVar}), 0) 70%)`, background: `radial-gradient(circle at center, rgba(var(${cssVar}), 1), rgba(var(${cssVar}), 0) 70%)`,
backgroundPosition: `var(--bg-x) var(--bg-y)`, backgroundPosition: `var(--bg-x) var(--bg-y)`,
backgroundRepeat: "no-repeat", backgroundRepeat: "no-repeat",
backgroundAttachment: "fixed",
backgroundSize: `${size.toFixed(0)}px ${size.toFixed(0)}px`, backgroundSize: `${size.toFixed(0)}px ${size.toFixed(0)}px`,
}} }}
/> />

View File

@ -1,11 +1,24 @@
.lightbar { .lightbar, .lightbar-visual {
position: absolute; position: absolute;
left: -25vw; top: 0;
top: 0; width: 150vw;
width: 150vw;
height: 800px; height: 800px;
pointer-events: none; pointer-events: none;
user-select: none; user-select: none;
}
.lightbar {
left: -25vw;
display: flex;
justify-content: center;
align-items: center;
--d: 3s;
--animation: cubic-bezier(.75,-0.00,.25,1);
animation: boot var(--d) var(--animation) forwards;
}
.lightbar-visual {
left: 0;
--top: theme('colors.background.main'); --top: theme('colors.background.main');
--bottom: theme('colors.lightBar.light'); --bottom: theme('colors.lightBar.light');
--first: conic-gradient(from 90deg at 80% 50%,var(--top),var(--bottom)); --first: conic-gradient(from 90deg at 80% 50%,var(--top),var(--bottom));
@ -19,13 +32,30 @@
transform: rotate(180deg) translateZ(0px) translateY(400px); transform: rotate(180deg) translateZ(0px) translateY(400px);
transform-origin: center center; transform-origin: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
display: flex; animation: lightbarBoot var(--d) var(--animation) forwards;
justify-content: center;
align-items: center;
} }
.lightbar canvas { .lightbar canvas {
width: 40%; width: 40%;
height: 300px; height: 300px;
transform: translateY(-50%) rotate(180deg); transform: translateY(-250px);
}
@keyframes boot {
from {
opacity: 0.25;
}
to {
opacity: 1;
}
}
@keyframes lightbarBoot {
0% {
transform: rotate(180deg) translateZ(0px) translateY(400px) scaleX(0.8);
}
100% {
transform: rotate(180deg) translateZ(0px) translateY(400px) scaleX(1);
}
} }

View File

@ -139,6 +139,7 @@ export function Lightbar(props: { className?: string }) {
<div className={props.className}> <div className={props.className}>
<div className="lightbar"> <div className="lightbar">
<ParticlesCanvas /> <ParticlesCanvas />
<div className="lightbar-visual" />
</div> </div>
</div> </div>
); );

View File

@ -83,6 +83,18 @@ module.exports = {
placeholder: "#4A4A71", placeholder: "#4A4A71",
icon: "#545476", icon: "#545476",
text: "#FFFFFF" text: "#FFFFFF"
},
// media cards
mediaCard: {
hoverBackground: "#161622",
hoverAccent: "#4D79A8",
hoverShadow: "#0A0A10",
shadow: "#161622",
barColor: "#4B4B63",
barFillColor: "#BA7FD6",
badge: "#151522",
badgeText: "#5F5F7A"
} }
} }
} }