main
mula.liu 2025-12-01 00:45:53 +08:00
parent 155eb06f8a
commit 5ae442c4b2
3 changed files with 8 additions and 117 deletions

View File

@ -1,114 +0,0 @@
import { useEffect, useState, useRef } from 'react';
import { request } from '../utils/request';
interface DanmakuMessage {
id: string;
uid: string;
username: string;
text: string;
ts: number;
// Runtime properties for animation
top?: number;
duration?: number;
startTime?: number;
}
interface DanmakuLayerProps {
enabled: boolean;
}
export function DanmakuLayer({ enabled }: DanmakuLayerProps) {
const [visibleMessages, setVisibleMessages] = useState<DanmakuMessage[]>([]);
const processedIds = useRef<Set<string>>(new Set());
const containerRef = useRef<HTMLDivElement>(null);
// Polling for new messages
useEffect(() => {
if (!enabled) return;
const fetchDanmaku = async () => {
try {
const { data } = await request.get('/danmaku/list');
if (Array.isArray(data)) {
// Filter out messages we've already seen or processed locally
// Actually, for a "live" feel, we only want *recent* messages or messages we haven't shown in this session.
// But since the backend returns a 24h window, we don't want to replay all 24h history at once on load.
// Strategy: On first load, maybe only show last 20? Or just start listening for new ones?
// Let's show recent ones (last 1 min) on load, then polling.
const now = Date.now() / 1000;
const newMessages = data.filter((msg: DanmakuMessage) => {
if (processedIds.current.has(msg.id)) return false;
// Only show messages from the last 5 minutes to avoid flooding history on reload
if (now - msg.ts > 300) return false;
return true;
});
if (newMessages.length > 0) {
console.log(`[Danmaku] Received ${newMessages.length} new messages`, newMessages);
newMessages.forEach((msg: DanmakuMessage) => processedIds.current.add(msg.id));
// Add to queue
addMessagesToTrack(newMessages);
}
}
} catch (err) {
console.error("Failed to fetch danmaku", err);
}
};
fetchDanmaku(); // Initial fetch
const interval = setInterval(fetchDanmaku, 3000); // Poll every 3s
return () => clearInterval(interval);
}, [enabled]);
const addMessagesToTrack = (newMsgs: DanmakuMessage[]) => {
// Assign random vertical position and duration
const tracks = newMsgs.map(msg => ({
...msg,
top: Math.floor(Math.random() * 60) + 10, // 10% to 70% height
duration: Math.floor(Math.random() * 5) + 8, // 8-13 seconds duration
startTime: Date.now()
}));
setVisibleMessages(prev => [...prev, ...tracks]);
};
// Cleanup finished animations
const handleAnimationEnd = (id: string) => {
setVisibleMessages(prev => prev.filter(m => m.id !== id));
};
if (!enabled) return null;
return (
<div
ref={containerRef}
className="absolute inset-0 pointer-events-none z-50 overflow-hidden"
style={{ userSelect: 'none' }}
>
{visibleMessages.map(msg => (
<div
key={msg.id}
className="absolute whitespace-nowrap text-white font-bold text-shadow-md will-change-transform"
style={{
top: `${msg.top}%`,
left: '100%',
fontSize: '1.5rem', // Increased size for visibility
textShadow: '0 0 4px rgba(0,0,0,0.8), 0 0 2px rgba(0,0,0,1)', // Stronger shadow
animation: `danmaku-move ${msg.duration}s linear forwards`
}}
onAnimationEnd={() => handleAnimationEnd(msg.id)}
>
{msg.text}
</div>
))}
<style>{`
@keyframes danmaku-move {
0% { transform: translateX(0); }
100% { transform: translateX(-100vw - 100%); }
}
`}</style>
</div>
);
}

View File

@ -41,9 +41,15 @@ export function MessageBoard({ open, onClose }: MessageBoardProps) {
fetchMessages().finally(() => setLoading(false));
intervalRef.current = window.setInterval(fetchMessages, 3000);
} else {
clearInterval(intervalRef.current);
if (intervalRef.current !== null) {
clearInterval(intervalRef.current);
}
}
return () => clearInterval(intervalRef.current);
return () => {
if (intervalRef.current !== null) {
clearInterval(intervalRef.current);
}
};
}, [open]);
useEffect(() => {

View File

@ -67,7 +67,6 @@ export function TerminalModal({
}
.terminal-modal .ant-modal-close {
color: #2ea043 !important;
top: 12px !important;
}
.terminal-modal .ant-modal-close:hover {
background-color: rgba(35, 134, 54, 0.2) !important;