@@ -147,6 +147,7 @@
:class="[getDanmakuClass(item), { 'danmaku-paused': pausedDanmakuIds.has(item.id) }]"
@mouseenter="pauseDanmaku(item.id)"
@mouseleave="resumeDanmaku(item.id)"
+ @animationend="onDanmakuAnimationEnd(item.id)"
>
{
let params = {}
if (isFirstLoad.value) {
- // 首次加载:获取最近20条记录,并计算每条记录的调用次数
+ // 首次加载:获取最近3条记录,并计算每条记录的调用次数
params = {
page: 1,
- page_size: 20,
+ page_size: 3,
sort_by: 'created_at',
sort_order: 'desc'
}
@@ -1548,26 +1552,52 @@ const loadDanmakuData = async () => {
}))
if (isFirstLoad.value) {
- // 首次加载:直接设置这20条记录(即使为空也要设置)
+ // 首次加载:先把最新的3条记录放入堆栈,然后逐个显示
// 确保所有记录的调用次数都已计算完成
const sortedItems = newItems.sort((a, b) => b.timestamp - a.timestamp)
- danmakuItems.value = sortedItems
+
+ // 如果没有数据,直接标记为已加载,不显示任何弹幕
+ if (sortedItems.length === 0) {
+ isFirstLoad.value = false
+ lastFetchTime.value = now
+ danmakuItems.value = []
+ console.log('首次加载完成,没有记录')
+ return
+ }
+
+ // 先把3条记录放入堆栈(但不立即显示)
+ firstLoadItems.value = sortedItems
+ firstLoadDisplayIndex.value = 0
+ danmakuItems.value = [] // 先清空显示列表
isFirstLoad.value = false
lastFetchTime.value = now
- console.log('首次加载完成,已加载', sortedItems.length, '条记录,每条记录都已计算调用次数')
+ console.log('首次加载完成,已加载', sortedItems.length, '条记录到堆栈,每条记录都已计算调用次数')
+
+ // 逐个显示弹幕,每次间隔300ms
+ if (firstLoadItems.value.length > 0) {
+ firstLoadDisplayTimer.value = setInterval(() => {
+ if (firstLoadDisplayIndex.value < firstLoadItems.value.length) {
+ // 从堆栈中取出并显示
+ danmakuItems.value.push(firstLoadItems.value[firstLoadDisplayIndex.value])
+ firstLoadDisplayIndex.value++
+ } else {
+ if (firstLoadDisplayTimer.value) {
+ clearInterval(firstLoadDisplayTimer.value)
+ firstLoadDisplayTimer.value = null
+ }
+ firstLoadItems.value = [] // 清空首次加载数据
+ }
+ }, 300)
+ }
} else {
- // 持续轮询:将新记录添加到堆栈中(按时间戳倒序,最新的在前)
+ // 持续轮询:将新记录添加到堆栈中(只添加新记录,不保留旧记录)
if (newItems.length > 0) {
const existingIds = new Set(danmakuItems.value.map(item => item.id))
const uniqueNewItems = newItems.filter(item => !existingIds.has(item.id))
if (uniqueNewItems.length > 0) {
- // 合并新旧数据,按时间戳排序,保留最新的100条
- const allItems = [...danmakuItems.value, ...uniqueNewItems]
- .sort((a, b) => b.timestamp - a.timestamp) // 最新的在前
- .slice(0, MAX_DANMAKU_ITEMS) // 只保留100条
-
- danmakuItems.value = allItems
+ // 只添加新记录到堆栈,不保留旧记录(旧记录会在播放完成后自动移除)
+ danmakuItems.value.push(...uniqueNewItems)
}
}
// 无论是否有新记录,都要更新时间
@@ -1592,9 +1622,9 @@ const getDanmakuStyle = (item, index) => {
return {}
}
- const containerHeight = danmakuContainer.value.clientHeight || 128
+ const containerHeight = danmakuContainer.value.clientHeight || 80
const itemHeight = 36 // 每条弹幕的高度(包含间距)
- const maxRows = Math.floor(containerHeight / itemHeight)
+ const maxRows = 2 // 固定为两行
// 计算弹幕应该在哪一行(垂直位置)
// 使用索引和时间戳的组合来分配行,避免重叠
@@ -1688,6 +1718,17 @@ const resumeDanmaku = (itemId) => {
pausedDanmakuIds.value.delete(itemId)
}
+// 弹幕动画结束回调 - 从堆栈中移除已播放完成的弹幕
+const onDanmakuAnimationEnd = (itemId) => {
+ // 只有当弹幕不在暂停状态时才移除(避免鼠标悬停时误删)
+ if (!pausedDanmakuIds.value.has(itemId)) {
+ const index = danmakuItems.value.findIndex(item => item.id === itemId)
+ if (index !== -1) {
+ danmakuItems.value.splice(index, 1)
+ }
+ }
+}
+
// 启动弹幕轮询
const startDanmakuPolling = () => {
// 立即加载一次
@@ -1705,6 +1746,11 @@ const stopDanmakuPolling = () => {
clearInterval(danmakuPollingTimer.value)
danmakuPollingTimer.value = null
}
+ // 清理首次加载显示定时器
+ if (firstLoadDisplayTimer.value) {
+ clearInterval(firstLoadDisplayTimer.value)
+ firstLoadDisplayTimer.value = null
+ }
}
onMounted(() => {
@@ -1729,9 +1775,9 @@ onUnmounted(() => {