feat: 添加预览效果页面并优化交互效果

refactor: 优化代码健壮性和类型安全

style: 更新字体样式和全局CSS

fix: 修复IntersectionObserver潜在空引用问题

chore: 更新依赖和ESLint配置

build: 更新构建ID和路由配置
This commit is contained in:
张翔
2026-02-24 10:24:05 +08:00
parent 64165c4499
commit fecbfd1990
239 changed files with 3403 additions and 5181 deletions
+71 -41
View File
@@ -19,7 +19,7 @@ export function InkDrop({
color = '#1C1C1C',
blur = 0,
delay = 0,
duration = 8,
duration: _duration = 8,
className = ''
}: InkDropProps) {
return (
@@ -251,7 +251,7 @@ export function FloatingInk({ count = 15, className = '' }: FloatingInkProps) {
}, []);
const elements = useMemo(() => {
if (!isMounted) return [];
if (!isMounted) {return [];}
const items = [];
for (let i = 0; i < count; i++) {
@@ -351,12 +351,50 @@ interface InkDecorationProps {
className?: string;
}
export function InkDecoration({ variant = 'balanced', className = '' }: InkDecorationProps) {
const [isMounted, setIsMounted] = useState(false);
interface DropPosition {
left: string;
top: string;
size: number;
opacity: number;
blur: number;
isRed: boolean;
duration: number;
}
useEffect(() => {
setIsMounted(true);
}, []);
interface SplashPosition {
left: string;
top: string;
size: number;
duration: number;
}
interface SealPosition {
left: string;
top: string;
size: number;
duration: number;
}
interface StainPosition {
left: string;
top: string;
size: number;
duration: number;
}
interface StrokePosition {
left: string;
top: string;
width: number;
duration: number;
}
export function InkDecoration({ variant = 'balanced', className = '' }: InkDecorationProps) {
const [dropPositions, setDropPositions] = useState<DropPosition[]>([]);
const [splashPositions, setSplashPositions] = useState<SplashPosition[]>([]);
const [sealPositions, setSealPositions] = useState<SealPosition[]>([]);
const [stainPositions, setStainPositions] = useState<StainPosition[]>([]);
const [strokePositions, setStrokePositions] = useState<StrokePosition[]>([]);
const config = {
minimal: { drops: 3, splashes: 1, seals: 1, stains: 1, strokes: 1 },
@@ -366,53 +404,45 @@ export function InkDecoration({ variant = 'balanced', className = '' }: InkDecor
const { drops, splashes, seals, stains, strokes } = config[variant];
const dropPositions = useMemo(() => {
if (!isMounted) return [];
return Array.from({ length: drops }, (_, i) => ({
useEffect(() => {
setDropPositions(Array.from({ length: drops }, (_, i) => ({
left: `${15 + (i * 70 / drops)}%`,
top: `${20 + Math.random() * 60}%`,
size: 6 + Math.random() * 14,
opacity: 0.06 + Math.random() * 0.1,
blur: Math.random() * 3,
isRed: i % 3 === 0,
}));
}, [drops, isMounted]);
const splashPositions = useMemo(() => {
if (!isMounted) return [];
return Array.from({ length: splashes }, (_, i) => ({
duration: 5 + Math.random() * 3,
})));
setSplashPositions(Array.from({ length: splashes }, (_, i) => ({
left: `${20 + (i * 60 / splashes)}%`,
top: `${15 + Math.random() * 70}%`,
size: 40 + Math.random() * 40,
}));
}, [splashes, isMounted]);
const sealPositions = useMemo(() => {
if (!isMounted) return [];
return Array.from({ length: seals }, (_, i) => ({
duration: 7 + Math.random() * 3,
})));
setSealPositions(Array.from({ length: seals }, (_, i) => ({
left: `${25 + (i * 50 / seals)}%`,
top: `${25 + Math.random() * 50}%`,
size: 25 + Math.random() * 25,
}));
}, [seals, isMounted]);
const stainPositions = useMemo(() => {
if (!isMounted) return [];
return Array.from({ length: stains }, (_, i) => ({
duration: 6 + Math.random() * 2,
})));
setStainPositions(Array.from({ length: stains }, (_, i) => ({
left: `${10 + (i * 80 / stains)}%`,
top: `${30 + Math.random() * 40}%`,
size: 80 + Math.random() * 60,
}));
}, [stains, isMounted]);
const strokePositions = useMemo(() => {
if (!isMounted) return [];
return Array.from({ length: strokes }, (_, i) => ({
duration: 8 + Math.random() * 4,
})));
setStrokePositions(Array.from({ length: strokes }, (_, i) => ({
left: `${15 + (i * 70 / strokes)}%`,
top: `${40 + Math.random() * 30}%`,
width: 100 + Math.random() * 100,
}));
}, [strokes, isMounted]);
duration: 6 + Math.random() * 3,
})));
}, [drops, splashes, seals, stains, strokes]);
return (
<div className={`absolute inset-0 pointer-events-none overflow-hidden ${className}`}>
@@ -426,7 +456,7 @@ export function InkDecoration({ variant = 'balanced', className = '' }: InkDecor
scale: [1, 1.1, 1],
}}
transition={{
duration: 5 + Math.random() * 3,
duration: pos.duration,
delay: i * 0.2,
repeat: Infinity,
ease: 'easeInOut',
@@ -452,7 +482,7 @@ export function InkDecoration({ variant = 'balanced', className = '' }: InkDecor
rotate: [0, 5, -5, 0],
}}
transition={{
duration: 7 + Math.random() * 3,
duration: pos.duration,
delay: i * 0.3,
repeat: Infinity,
ease: 'easeInOut',
@@ -472,7 +502,7 @@ export function InkDecoration({ variant = 'balanced', className = '' }: InkDecor
rotate: [-8, -5, -10, -8],
}}
transition={{
duration: 6 + Math.random() * 2,
duration: pos.duration,
delay: i * 0.25,
repeat: Infinity,
ease: 'easeInOut',
@@ -492,7 +522,7 @@ export function InkDecoration({ variant = 'balanced', className = '' }: InkDecor
opacity: [0.04, 0.06, 0.04],
}}
transition={{
duration: 8 + Math.random() * 4,
duration: pos.duration,
delay: i * 0.35,
repeat: Infinity,
ease: 'easeInOut',
@@ -512,7 +542,7 @@ export function InkDecoration({ variant = 'balanced', className = '' }: InkDecor
opacity: [0.08, 0.12, 0.08],
}}
transition={{
duration: 6 + Math.random() * 3,
duration: pos.duration,
delay: i * 0.3,
repeat: Infinity,
ease: 'easeInOut',