弹幕
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
animationDuration: `${danmaku.duration}ms`,
|
animationDuration: `${danmaku.duration}ms`,
|
||||||
animationDelay: `${danmaku.delay}ms`
|
animationDelay: `${danmaku.delay}ms`
|
||||||
}"
|
}"
|
||||||
|
@animationend="handleAnimationEnd(danmaku.id)"
|
||||||
>
|
>
|
||||||
<div :class="['danmaku-content', `danmaku-content-${danmaku.status}`]">
|
<div :class="['danmaku-content', `danmaku-content-${danmaku.status}`]">
|
||||||
<span :class="['company-name', `company-name-${danmaku.status}`]">{{ danmaku.companyName || '未知企业' }}</span>
|
<span :class="['company-name', `company-name-${danmaku.status}`]">{{ danmaku.companyName || '未知企业' }}</span>
|
||||||
@@ -154,13 +155,14 @@ const addDanmakuToQueue = (item) => {
|
|||||||
startAt: item.start_at || item.created_at,
|
startAt: item.start_at || item.created_at,
|
||||||
timeAgo: calculateTimeAgo(item.start_at || item.created_at),
|
timeAgo: calculateTimeAgo(item.start_at || item.created_at),
|
||||||
top: 0,
|
top: 0,
|
||||||
duration: props.danmakuSpeed,
|
duration: 0, // 将在添加到DOM后计算
|
||||||
delay: 0
|
delay: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算垂直位置(避免重叠)
|
// 计算垂直位置(随机分散,避免重叠)
|
||||||
danmaku.top = calculateTopPosition()
|
danmaku.top = calculateTopPosition()
|
||||||
danmaku.delay = Math.random() * 500 // 随机延迟0-500ms,让弹幕错开
|
// 增加随机延迟,让弹幕错开时间出现(0-2000ms)
|
||||||
|
danmaku.delay = Math.random() * 2000
|
||||||
|
|
||||||
danmakuQueue.value.push(danmaku)
|
danmakuQueue.value.push(danmaku)
|
||||||
|
|
||||||
@@ -169,41 +171,66 @@ const addDanmakuToQueue = (item) => {
|
|||||||
danmakuQueue.value.shift()
|
danmakuQueue.value.shift()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加到活动弹幕列表
|
// 延迟添加弹幕,让它们错开时间出现
|
||||||
activeDanmakus.value.push(danmaku)
|
|
||||||
|
|
||||||
// 弹幕动画结束后移除
|
|
||||||
setTimeout(() => {
|
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 calculateTopPosition = () => {
|
||||||
const maxLines = 2 // 最多显示两行
|
|
||||||
const containerHeight = 80 // 容器高度80px
|
const containerHeight = 80 // 容器高度80px
|
||||||
const lineHeight = 40 // 每行高度40px
|
const minTop = 10 // 最小顶部距离
|
||||||
const totalHeight = maxLines * lineHeight // 两行总高度80px
|
const maxTop = containerHeight - 40 // 最大顶部距离(留出弹幕高度空间)
|
||||||
const startTop = (containerHeight - totalHeight) / 2 // 垂直居中起始位置
|
|
||||||
|
|
||||||
// 计算当前有多少条弹幕
|
// 获取当前活动的弹幕位置
|
||||||
const currentCount = activeDanmakus.value.length
|
const activePositions = activeDanmakus.value.map(d => d.top).filter(pos => pos > 0)
|
||||||
|
|
||||||
// 如果已经有两条或更多,移除最旧的,保持在两行
|
// 尝试找到一个不重叠的位置
|
||||||
if (currentCount >= maxLines) {
|
let newTop
|
||||||
// 移除最旧的弹幕
|
let attempts = 0
|
||||||
const oldestDanmaku = activeDanmakus.value[0]
|
const minGap = 35 // 最小间距35px
|
||||||
if (oldestDanmaku) {
|
|
||||||
removeDanmaku(oldestDanmaku.id)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算新弹幕的位置(两行垂直居中)
|
return Math.round(newTop)
|
||||||
if (activeDanmakus.value.length === 0) {
|
|
||||||
return startTop // 第一条从居中位置开始
|
|
||||||
} else {
|
|
||||||
return startTop + lineHeight // 第二条在下一行
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除弹幕
|
// 移除弹幕
|
||||||
@@ -295,7 +322,7 @@ onUnmounted(() => {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
transform: translateX(-200%);
|
transform: translateX(-300%);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user