diff --git a/src/components/common/DanmakuBar.vue b/src/components/common/DanmakuBar.vue
index 24840a1..76dc111 100644
--- a/src/components/common/DanmakuBar.vue
+++ b/src/components/common/DanmakuBar.vue
@@ -26,6 +26,7 @@
animationDuration: `${danmaku.duration}ms`,
animationDelay: `${danmaku.delay}ms`
}"
+ @animationend="handleAnimationEnd(danmaku.id)"
>
{{ danmaku.companyName || '未知企业' }}
@@ -154,13 +155,14 @@ const addDanmakuToQueue = (item) => {
startAt: item.start_at || item.created_at,
timeAgo: calculateTimeAgo(item.start_at || item.created_at),
top: 0,
- duration: props.danmakuSpeed,
+ duration: 0, // 将在添加到DOM后计算
delay: 0
}
- // 计算垂直位置(避免重叠)
+ // 计算垂直位置(随机分散,避免重叠)
danmaku.top = calculateTopPosition()
- danmaku.delay = Math.random() * 500 // 随机延迟0-500ms,让弹幕错开
+ // 增加随机延迟,让弹幕错开时间出现(0-2000ms)
+ danmaku.delay = Math.random() * 2000
danmakuQueue.value.push(danmaku)
@@ -169,41 +171,66 @@ const addDanmakuToQueue = (item) => {
danmakuQueue.value.shift()
}
- // 添加到活动弹幕列表
- activeDanmakus.value.push(danmaku)
-
- // 弹幕动画结束后移除
+ // 延迟添加弹幕,让它们错开时间出现
setTimeout(() => {
- removeDanmaku(danmaku.id)
- }, danmaku.duration + danmaku.delay + 1000)
+ // 使用固定的动画时间,确保弹幕完全移出左侧
+ // translateX(-200%) 意味着向左移动自身宽度的200%,应该足够移出容器
+ danmaku.duration = props.danmakuSpeed
+
+ activeDanmakus.value.push(danmaku)
+ // 弹幕会在动画结束时通过 animationend 事件自动移除
+ }, danmaku.delay)
}
-// 计算弹幕的垂直位置(只显示两行,垂直居中)
+// 处理动画结束事件
+const handleAnimationEnd = (id) => {
+ // 动画结束后,弹幕已经完全移出左侧,可以安全移除
+ removeDanmaku(id)
+}
+
+// 计算弹幕的垂直位置(随机分散,避免重叠)
const calculateTopPosition = () => {
- const maxLines = 2 // 最多显示两行
const containerHeight = 80 // 容器高度80px
- const lineHeight = 40 // 每行高度40px
- const totalHeight = maxLines * lineHeight // 两行总高度80px
- const startTop = (containerHeight - totalHeight) / 2 // 垂直居中起始位置
+ const minTop = 10 // 最小顶部距离
+ const maxTop = containerHeight - 40 // 最大顶部距离(留出弹幕高度空间)
- // 计算当前有多少条弹幕
- const currentCount = activeDanmakus.value.length
+ // 获取当前活动的弹幕位置
+ const activePositions = activeDanmakus.value.map(d => d.top).filter(pos => pos > 0)
- // 如果已经有两条或更多,移除最旧的,保持在两行
- if (currentCount >= maxLines) {
- // 移除最旧的弹幕
- const oldestDanmaku = activeDanmakus.value[0]
- if (oldestDanmaku) {
- removeDanmaku(oldestDanmaku.id)
+ // 尝试找到一个不重叠的位置
+ let newTop
+ let attempts = 0
+ const minGap = 35 // 最小间距35px
+
+ do {
+ // 在容器高度范围内随机生成位置
+ newTop = minTop + Math.random() * (maxTop - minTop)
+ attempts++
+
+ // 检查是否与现有弹幕重叠
+ const tooClose = activePositions.some(pos => Math.abs(pos - newTop) < minGap)
+
+ if (!tooClose || attempts > 10) {
+ break
+ }
+ } while (attempts < 10)
+
+ // 如果尝试10次还是重叠,就使用一个固定但分散的位置
+ if (attempts >= 10) {
+ const positions = [20, 50] // 两行固定位置
+ const usedPositions = activePositions.filter(pos =>
+ positions.some(fixedPos => Math.abs(pos - fixedPos) < minGap)
+ )
+ if (usedPositions.length < positions.length) {
+ newTop = positions.find(pos =>
+ !activePositions.some(used => Math.abs(used - pos) < minGap)
+ ) || positions[0]
+ } else {
+ newTop = minTop + Math.random() * (maxTop - minTop)
}
}
- // 计算新弹幕的位置(两行垂直居中)
- if (activeDanmakus.value.length === 0) {
- return startTop // 第一条从居中位置开始
- } else {
- return startTop + lineHeight // 第二条在下一行
- }
+ return Math.round(newTop)
}
// 移除弹幕
@@ -295,7 +322,7 @@ onUnmounted(() => {
opacity: 1;
}
to {
- transform: translateX(-200%);
+ transform: translateX(-300%);
opacity: 1;
}
}