feat: add card skins support

This commit is contained in:
2026-03-12 13:19:46 +08:00
parent e759576b58
commit 9f92b57371
14 changed files with 904 additions and 154 deletions

View File

@@ -0,0 +1,13 @@
import type { CardFaceProps } from "./types";
/** 卡面:光泽 + 装饰圆形 */
export function CirclesFace(_: CardFaceProps) {
return (
<>
<div className="absolute inset-0 bg-linear-to-tr from-white/20 via-transparent to-transparent" />
<div className="absolute -right-8 -top-8 size-44 rounded-full bg-white/10" />
<div className="absolute right-4 -bottom-12 size-36 rounded-full bg-white/[0.07]" />
<div className="absolute -left-6 -bottom-4 size-24 rounded-full bg-black/10" />
</>
);
}

View File

@@ -0,0 +1,48 @@
import type { CardFaceProps } from "./types";
/** 卡面:渐变光晕 + 噪点纹理 */
export function GlowFace(_: CardFaceProps) {
return (
<>
{/* 噪点纹理 SVG filter */}
<svg className="absolute size-0">
<filter id="card-noise">
<feTurbulence
type="fractalNoise"
baseFrequency="0.65"
numOctaves="3"
stitchTiles="stitch"
/>
<feColorMatrix type="saturate" values="0" />
<feBlend in="SourceGraphic" mode="overlay" result="blend" />
<feComposite in="blend" in2="SourceGraphic" operator="in" />
</filter>
</svg>
{/* 噪点层 */}
<div
className="absolute inset-0 opacity-[0.18] mix-blend-overlay"
style={{ filter: "url(#card-noise)" }}
/>
{/* 左下光晕 */}
<div
className="absolute -bottom-8 -left-8 size-48 rounded-full opacity-60 blur-3xl"
style={{ background: "radial-gradient(circle, white 0%, transparent 70%)" }}
/>
{/* 右上光晕 */}
<div
className="absolute -right-6 -top-6 size-40 rounded-full opacity-40 blur-2xl"
style={{ background: "radial-gradient(circle, white 0%, transparent 70%)" }}
/>
{/* 中心微光 */}
<div
className="absolute inset-x-0 top-1/2 mx-auto size-32 -translate-y-1/2 rounded-full opacity-20 blur-3xl"
style={{ background: "radial-gradient(circle, white 0%, transparent 70%)" }}
/>
{/* 顶部高光条 */}
<div className="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-white/50 to-transparent" />
</>
);
}

View File

@@ -0,0 +1,17 @@
import { LineFace } from "./line";
import { CirclesFace } from "./circles";
import { GlowFace } from "./glow";
import { VipFace } from "./vip";
import { RedeyeFace } from "./redeye";
export type { CardFaceProps, CardFaceComponent } from "./types";
import type { CardFaceComponent } from "./types";
export const CARD_FACE_REGISTRY = {
line: LineFace,
circles: CirclesFace,
glow: GlowFace,
vip: VipFace,
redeye: RedeyeFace,
} satisfies Record<string, CardFaceComponent>;
export type CardFaceName = keyof typeof CARD_FACE_REGISTRY;

View File

@@ -0,0 +1,17 @@
import type { CardFaceProps } from "./types";
/** 卡面:斜线纹理 + 右上角光晕 */
export function LineFace(_: CardFaceProps) {
return (
<>
<div
className="absolute inset-0 opacity-[0.07]"
style={{
backgroundImage:
"repeating-linear-gradient(135deg, #fff 0px, #fff 1px, transparent 1px, transparent 12px)",
}}
/>
<div className="absolute -right-10 -top-10 size-48 rounded-full bg-white/15 blur-2xl" />
</>
);
}

View File

@@ -0,0 +1,241 @@
import type { CardFaceProps } from "./types";
/** 卡面:深黑底色 + 深红光晕与锐利几何线条 */
export function RedeyeFace(_: CardFaceProps) {
return (
<>
{/* 深黑底色 */}
<div
className="absolute inset-0"
style={{
background: "linear-gradient(145deg, #0a0505 0%, #120808 45%, #0d0404 100%)",
}}
/>
{/* 红色主光晕:左上 */}
<div
className="absolute -left-8 -top-8 size-56 rounded-full opacity-25 blur-3xl"
style={{
background: "radial-gradient(circle, #cc1020 0%, #7a0810 50%, transparent 75%)",
}}
/>
{/* 红色副光晕:右下 */}
<div
className="absolute -bottom-10 -right-6 size-44 rounded-full opacity-15 blur-3xl"
style={{
background: "radial-gradient(circle, #e01828 0%, transparent 70%)",
}}
/>
{/* 顶部红色高光边 */}
<div
className="absolute inset-x-0 top-0 h-[1.5px]"
style={{
background:
"linear-gradient(to right, transparent 5%, #cc1020 25%, #ff2233 50%, #cc1020 75%, transparent 95%)",
}}
/>
{/* 左侧竖向红线 */}
<div
className="absolute bottom-0 left-5 top-0 w-px opacity-40"
style={{
background:
"linear-gradient(to bottom, transparent, #cc1020 20%, #ff2233 50%, #cc1020 80%, transparent)",
}}
/>
{/* 右侧双竖线 */}
<div
className="absolute bottom-3 right-4 top-3 w-px opacity-30"
style={{
background:
"linear-gradient(to bottom, transparent, #993010 30%, #cc2010 60%, transparent)",
}}
/>
<div
className="absolute bottom-3 right-[18px] top-3 w-px opacity-15"
style={{
background:
"linear-gradient(to bottom, transparent, #993010 30%, #cc2010 60%, transparent)",
}}
/>
{/* 红色横向扫描线组 */}
<div
className="absolute inset-x-0 opacity-[0.06]"
style={{
top: "30%",
height: "1px",
background:
"linear-gradient(to right, transparent 0%, #ff2233 40%, #ff5566 60%, transparent 100%)",
}}
/>
<div
className="absolute inset-x-0 opacity-[0.04]"
style={{
top: "32%",
height: "1px",
background: "linear-gradient(to right, transparent 10%, #ff2233 50%, transparent 90%)",
}}
/>
{/* 电路板风格折角线:左上 */}
<svg
className="absolute left-6 top-3 opacity-20"
width="40"
height="28"
viewBox="0 0 40 28"
fill="none"
>
<polyline points="0,28 0,8 12,0 40,0" stroke="#ff2233" strokeWidth="1" fill="none" />
<circle cx="12" cy="0" r="1.5" fill="#ff2233" />
</svg>
{/* 电路板风格折角线:右下 */}
<svg
className="absolute bottom-3 right-6 opacity-20"
width="40"
height="28"
viewBox="0 0 40 28"
fill="none"
>
<polyline points="40,0 40,20 28,28 0,28" stroke="#cc1020" strokeWidth="1" fill="none" />
<circle cx="28" cy="28" r="1.5" fill="#cc1020" />
</svg>
{/* 风格菱形 */}
<svg
className="absolute opacity-[0.12]"
style={{ right: "28px", top: "50%", transform: "translateY(-50%)" }}
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
>
<polygon points="16,1 31,16 16,31 1,16" stroke="#ff2233" strokeWidth="1.5" fill="none" />
<polygon points="16,7 25,16 16,25 7,16" stroke="#ff2233" strokeWidth="1" fill="none" />
<circle cx="16" cy="16" r="2" fill="#ff2233" />
</svg>
{/* 徽标 — 八角框 + 内部对角交叉 + 中心菱形 */}
<svg
className="absolute opacity-60"
style={{
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)",
filter: "drop-shadow(0 0 6px #cc102088)",
}}
width="56"
height="56"
viewBox="0 0 56 56"
fill="none"
>
{/* 外八角轮廓 */}
<polygon
points="16,2 40,2 54,16 54,40 40,54 16,54 2,40 2,16"
stroke="#cc1020"
strokeWidth="1.5"
fill="#0d0404"
fillOpacity="0.85"
/>
{/* 内八角 */}
<polygon
points="20,8 36,8 48,20 48,36 36,48 20,48 8,36 8,20"
stroke="#cc1020"
strokeWidth="0.75"
strokeOpacity="0.5"
fill="none"
/>
{/* 对角线:左上 → 右下 */}
<line
x1="16"
y1="16"
x2="40"
y2="40"
stroke="#cc1020"
strokeWidth="1"
strokeOpacity="0.7"
/>
{/* 对角线:右上 → 左下 */}
<line
x1="40"
y1="16"
x2="16"
y2="40"
stroke="#cc1020"
strokeWidth="1"
strokeOpacity="0.7"
/>
{/* 横向中轴 */}
<line
x1="6"
y1="28"
x2="50"
y2="28"
stroke="#cc1020"
strokeWidth="0.75"
strokeOpacity="0.4"
/>
{/* 纵向中轴 */}
<line
x1="28"
y1="6"
x2="28"
y2="50"
stroke="#cc1020"
strokeWidth="0.75"
strokeOpacity="0.4"
/>
{/* 中心菱形 */}
<polygon
points="28,18 38,28 28,38 18,28"
stroke="#ff2233"
strokeWidth="1.25"
fill="#1a0505"
fillOpacity="0.9"
/>
{/* 中心圆点 */}
<circle cx="28" cy="28" r="3" fill="#ff2233" />
<circle cx="28" cy="28" r="1.5" fill="#ff6677" />
{/* 四个顶点刻度点 */}
<circle cx="28" cy="6" r="1.25" fill="#cc1020" fillOpacity="0.8" />
<circle cx="50" cy="28" r="1.25" fill="#cc1020" fillOpacity="0.8" />
<circle cx="28" cy="50" r="1.25" fill="#cc1020" fillOpacity="0.8" />
<circle cx="6" cy="28" r="1.25" fill="#cc1020" fillOpacity="0.8" />
</svg>
{/* 噪点 SVG filter */}
<svg className="absolute size-0">
<filter id="arasaka-noise">
<feTurbulence
type="fractalNoise"
baseFrequency="0.8"
numOctaves="4"
stitchTiles="stitch"
/>
<feColorMatrix type="saturate" values="0" />
<feBlend in="SourceGraphic" mode="overlay" result="blend" />
<feComposite in="blend" in2="SourceGraphic" operator="in" />
</filter>
</svg>
{/* 噪点层 */}
<div
className="absolute inset-0 opacity-[0.1] mix-blend-overlay"
style={{ filter: "url(#arasaka-noise)" }}
/>
{/* 底部红色压边 */}
<div
className="absolute inset-x-0 bottom-0 h-px opacity-30"
style={{
background:
"linear-gradient(to right, transparent 10%, #7a0810 40%, #cc1020 60%, transparent 90%)",
}}
/>
</>
);
}

View File

@@ -0,0 +1,10 @@
import type { ComponentType } from "react";
/**
* 卡面(卡底装饰)组件的 props。
* 卡面仅负责视觉装饰(颜色纹理、光效、几何图形等),不接收任何业务数据。
*/
export type CardFaceProps = Record<string, never>;
/** 卡面组件类型 */
export type CardFaceComponent = ComponentType<CardFaceProps>;

View File

@@ -0,0 +1,129 @@
import type { CardFaceProps } from "./types";
/** 卡面:黑金 VIP — 深黑底色 + 金色光晕与描边,体现尊贵身份 */
export function VipFace(_: CardFaceProps) {
return (
<>
{/* 完全覆盖底部 palette建立黑金底色 */}
<div
className="absolute inset-0"
style={{
background: "linear-gradient(135deg, #0f0f0f 0%, #1a1610 40%, #0d0d0d 100%)",
}}
/>
{/* 金色主光晕:右上角 */}
<div
className="absolute -right-10 -top-10 size-52 rounded-full opacity-30 blur-3xl"
style={{
background: "radial-gradient(circle, #d4a843 0%, #9a6f1a 50%, transparent 75%)",
}}
/>
{/* 金色副光晕:左下角 */}
<div
className="absolute -bottom-12 -left-6 size-44 rounded-full opacity-20 blur-3xl"
style={{
background: "radial-gradient(circle, #c49b2e 0%, transparent 70%)",
}}
/>
{/* 中部横向光带 */}
<div
className="absolute inset-x-0 top-1/2 h-px -translate-y-1/2 opacity-20"
style={{
background:
"linear-gradient(to right, transparent 0%, #d4a843 30%, #f0d060 50%, #d4a843 70%, transparent 100%)",
}}
/>
{/* 顶部金色高光边 */}
<div
className="absolute inset-x-0 top-0 h-px"
style={{
background:
"linear-gradient(to right, transparent 5%, #c8992a 30%, #f5d060 50%, #c8992a 70%, transparent 95%)",
}}
/>
{/* 底部深金色压边 */}
<div
className="absolute inset-x-0 bottom-0 h-px opacity-50"
style={{
background:
"linear-gradient(to right, transparent 10%, #9a6f1a 40%, #b8881f 60%, transparent 90%)",
}}
/>
{/* 右侧竖向装饰线 */}
<div
className="absolute bottom-4 right-5 top-4 w-px opacity-15"
style={{
background:
"linear-gradient(to bottom, transparent, #d4a843 30%, #f0d060 60%, transparent)",
}}
/>
{/* 细噪点 SVG filter */}
<svg className="absolute size-0">
<filter id="vip-noise">
<feTurbulence
type="fractalNoise"
baseFrequency="0.75"
numOctaves="4"
stitchTiles="stitch"
/>
<feColorMatrix type="saturate" values="0" />
<feBlend in="SourceGraphic" mode="overlay" result="blend" />
<feComposite in="blend" in2="SourceGraphic" operator="in" />
</filter>
</svg>
{/* 噪点层:增加高端质感 */}
<div
className="absolute inset-0 opacity-[0.12] mix-blend-overlay"
style={{ filter: "url(#vip-noise)" }}
/>
{/* 整体金色微光叠层 */}
<div
className="absolute inset-0 opacity-[0.04]"
style={{
background: "linear-gradient(120deg, transparent 20%, #d4a843 50%, transparent 80%)",
}}
/>
{/* 菱形网格纹 */}
<div
className="absolute inset-0 opacity-[0.06]"
style={{
backgroundImage:
"repeating-linear-gradient(45deg, #d4a843 0px, #d4a843 1px, transparent 1px, transparent 14px), repeating-linear-gradient(-45deg, #d4a843 0px, #d4a843 1px, transparent 1px, transparent 14px)",
}}
/>
{/* VIP 大水印文字 */}
<div
className="pointer-events-none absolute -bottom-4 -left-2 select-none font-black leading-none tracking-widest opacity-[0.07]"
style={{
fontSize: "84px",
color: "#d4a843",
fontFamily: "serif",
letterSpacing: "0.15em",
}}
>
VIP
</div>
{/* 斜向扫光带 */}
<div
className="absolute -inset-y-4 w-12 -rotate-12 opacity-[0.08] blur-sm"
style={{
left: "38%",
background:
"linear-gradient(to bottom, transparent, #f5d060 30%, #fff8dc 50%, #f5d060 70%, transparent)",
}}
/>
</>
);
}