Add File
This commit is contained in:
112
src/elderly/components/TransparentMediaOverlay.tsx
Normal file
112
src/elderly/components/TransparentMediaOverlay.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { X } from 'lucide-react';
|
||||||
|
|
||||||
|
interface TransparentMediaOverlayProps {
|
||||||
|
mediaFilename: string;
|
||||||
|
mediaType: 'photo' | 'video';
|
||||||
|
avatarText?: string;
|
||||||
|
duration?: number; // 展示时长(秒)
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 透明窗口媒体展示组件
|
||||||
|
* 在数字人主页中部弹出,展示媒体文件
|
||||||
|
*/
|
||||||
|
export const TransparentMediaOverlay: React.FC<TransparentMediaOverlayProps> = ({
|
||||||
|
mediaFilename,
|
||||||
|
mediaType,
|
||||||
|
avatarText,
|
||||||
|
duration = 30, // 默认30秒
|
||||||
|
onClose,
|
||||||
|
}) => {
|
||||||
|
const [autoCloseTimer, setAutoCloseTimer] = useState<number>(duration);
|
||||||
|
const [isClosing, setIsClosing] = useState<boolean>(false);
|
||||||
|
|
||||||
|
// 构建媒体文件URL
|
||||||
|
const mediaUrl = `http://localhost:8000/uploads/${mediaFilename}`;
|
||||||
|
|
||||||
|
// 处理关闭(带淡出动画)
|
||||||
|
const handleClose = () => {
|
||||||
|
setIsClosing(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
onClose();
|
||||||
|
}, 300); // 等待淡出动画完成
|
||||||
|
};
|
||||||
|
|
||||||
|
// 自动关闭倒计时
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setAutoCloseTimer((prev) => {
|
||||||
|
if (prev <= 1) {
|
||||||
|
clearInterval(interval);
|
||||||
|
handleClose();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return prev - 1;
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`fixed inset-0 z-50 flex items-center justify-center transition-opacity duration-300 ${
|
||||||
|
isClosing ? 'opacity-0' : 'opacity-100'
|
||||||
|
}`}>
|
||||||
|
{/* 媒体内容容器 - 下调10% */}
|
||||||
|
<div className="relative w-full h-full flex flex-col items-center justify-center p-4 pt-[calc(1rem+10vh)]">
|
||||||
|
{/* 媒体内容 - 尽量大 */}
|
||||||
|
<div className="relative max-w-full max-h-full flex items-center justify-center">
|
||||||
|
{mediaType === 'photo' ? (
|
||||||
|
<img
|
||||||
|
src={mediaUrl}
|
||||||
|
alt="展示的照片"
|
||||||
|
className="max-w-full max-h-[90vh] object-contain rounded-2xl shadow-2xl"
|
||||||
|
onError={(e) => {
|
||||||
|
console.error('图片加载失败:', mediaUrl);
|
||||||
|
(e.target as HTMLImageElement).src = '/placeholder-photo.jpg';
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<video
|
||||||
|
src={mediaUrl}
|
||||||
|
controls
|
||||||
|
autoPlay
|
||||||
|
className="max-w-full max-h-[90vh] object-contain rounded-2xl shadow-2xl"
|
||||||
|
onError={(e) => {
|
||||||
|
console.error('视频加载失败:', mediaUrl);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 右上角: 倒计时 + 关闭按钮 */}
|
||||||
|
<div className="absolute top-3 right-3 flex items-center gap-2">
|
||||||
|
{/* 倒计时 */}
|
||||||
|
<div className="bg-black/40 backdrop-blur-md text-white px-3 py-1.5 rounded-full text-sm">
|
||||||
|
{autoCloseTimer}s
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 关闭按钮 */}
|
||||||
|
<button
|
||||||
|
onClick={handleClose}
|
||||||
|
className="w-10 h-10 flex items-center justify-center bg-black/40 backdrop-blur-md hover:bg-black/60 text-white rounded-full transition-all hover:scale-110 active:scale-95"
|
||||||
|
aria-label="关闭"
|
||||||
|
>
|
||||||
|
<X size={20} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 底部: 标题文字(小半透明) */}
|
||||||
|
{avatarText && (
|
||||||
|
<div className="absolute bottom-3 left-3 right-3">
|
||||||
|
<div className="bg-black/40 backdrop-blur-md text-white px-4 py-2 rounded-lg text-base text-center">
|
||||||
|
{avatarText}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user