打磨细节

This commit is contained in:
2026-01-28 17:55:01 +08:00
parent 08af95116e
commit 0a1bd07492
36 changed files with 1649 additions and 466 deletions

View File

@@ -0,0 +1,103 @@
'use client';
import { useEffect, useState, useMemo } from 'react';
interface ShatterTransitionProps {
isActive: boolean;
onComplete: () => void;
}
// 增加网格密度以获得更细腻的破碎感
const GRID_COLS = 10;
const GRID_ROWS = 10;
export default function ShatterTransition({ isActive, onComplete }: ShatterTransitionProps) {
const [isAnimating, setIsAnimating] = useState(false);
useEffect(() => {
if (isActive) {
setIsAnimating(true);
// 总动画时间 = 最大延迟 + 单个动画时长
// 这里预留充足时间确保最后一片落完
const timer = setTimeout(() => {
setIsAnimating(false);
onComplete();
}, 1500);
return () => clearTimeout(timer);
} else {
setIsAnimating(false);
}
}, [isActive, onComplete]);
// 预先计算碎片数据,避免重渲染时的性能抖动
const fragments = useMemo(() => {
return Array.from({ length: GRID_COLS * GRID_ROWS }, (_, i) => {
const col = i % GRID_COLS;
const row = Math.floor(i / GRID_COLS);
// 核心算法:计算距离左上角的距离 (欧几里得距离)
// 距离越远delay 越大
const distance = Math.sqrt(col * col + row * row);
const maxDistance = Math.sqrt(GRID_COLS * GRID_COLS + GRID_ROWS * GRID_ROWS);
const normalizedDistance = distance / maxDistance;
// 爆炸参数
const randomX = (Math.random() - 0.5) * 200; // X轴偏移
const randomY = Math.random() * 300 + 100; // Y轴主要向下掉落
const randomRotateX = (Math.random() - 0.5) * 720; // 剧烈翻滚
const randomRotateY = (Math.random() - 0.5) * 720;
const randomScale = 0.5 + Math.random() * 0.5; // 碎片大小不一
return {
id: i,
col,
row,
// 延迟时间:基准延迟 + 随机微扰 (让波浪不那么死板)
delay: normalizedDistance * 600 + (Math.random() * 100),
randomX,
randomY,
randomRotateX,
randomRotateY,
randomScale
};
});
}, []);
if (!isActive && !isAnimating) return null;
return (
<div
className="fixed inset-0 z-[9999] pointer-events-none overflow-hidden"
style={{ perspective: '1200px' }} // 开启3D透视这很重要
>
<div className="relative w-full h-full bg-transparent">
{fragments.map((frag) => (
<div
key={frag.id}
className="absolute shatter-fragment will-change-transform"
style={{
left: `${(frag.col / GRID_COLS) * 100}%`,
top: `${(frag.row / GRID_ROWS) * 100}%`,
width: `${100 / GRID_COLS + 0.1}%`, // +0.1% 防止渲染缝隙
height: `${100 / GRID_ROWS + 0.1}%`,
// 动画变量传入 CSS
'--delay': `${frag.delay}ms`,
'--tx': `${frag.randomX}px`,
'--ty': `${frag.randomY}px`,
'--rx': `${frag.randomRotateX}deg`,
'--ry': `${frag.randomRotateY}deg`,
'--s': `${frag.randomScale}`,
} as React.CSSProperties}
>
{/* 内部容器负责材质,外部容器负责位置,分离关注点 */}
<div className="w-full h-full glass-shard bg-white backdrop-blur-lg border-[0.5px] border-white/30 shadow-md" />
</div>
))}
</div>
{/* 可选:背景遮罩,防止碎片飞走后直接漏出底部内容,根据需求调整 */}
<div className="absolute inset-0 bg-white -z-10 animate-fade-out" />
</div>
);
}