video-flow-b/components/ui/FloatingGlassPanel.tsx
2025-08-09 16:41:05 +08:00

72 lines
2.1 KiB
TypeScript

'use client';
import { motion, AnimatePresence } from 'framer-motion';
import { ReactNode } from 'react';
type FloatingGlassPanelProps = {
open: boolean;
clickMaskClose?: boolean;
onClose?: () => void;
children: ReactNode;
width?: string;
r_key?: string | number;
panel_style?: React.CSSProperties;
};
export default function FloatingGlassPanel({ open, onClose, children, width = '320px', r_key, panel_style, clickMaskClose = true }: FloatingGlassPanelProps) {
// 定义弹出动画
const bounceAnimation = {
scale: [0.95, 1.02, 0.98, 1],
rotate: [0, -1, 1, -1, 0],
};
return (
<AnimatePresence>
{open && (
<div className="fixed inset-0 z-50 flex items-center justify-center">
<motion.div
key={r_key}
className="cursor-grab active:cursor-grabbing"
drag
dragElastic={0.2}
dragMomentum={false}
initial={{ opacity: 0, scale: 0.95, rotate: 0 }}
animate={{
opacity: 1,
...bounceAnimation,
}}
exit={{ opacity: 0, scale: 0.95, rotate: 0 }}
transition={{
duration: 0.6,
ease: [0.19, 1, 0.22, 1],
scale: {
duration: 0.4,
times: [0, 0.3, 0.6, 1]
},
rotate: {
duration: 0.4,
times: [0, 0.2, 0.4, 0.6, 1]
}
}}
>
<div
style={{ width, ...panel_style }}
className="rounded-xl backdrop-blur-md bg-white/10 border border-white/20 shadow-xl text-white p-4"
>
{children}
</div>
</motion.div>
{/* 添加遮罩层,点击时关闭面板 */}
<motion.div
className="fixed inset-0 bg-black/20 backdrop-blur-sm -z-10"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={clickMaskClose ? onClose : undefined }
/>
</div>
)}
</AnimatePresence>
);
}