1. 播放器
This commit is contained in:
@@ -39,12 +39,29 @@ let pollTimer = null
|
||||
let uid = 0
|
||||
/** @type {Map<string, number>} */
|
||||
const lastShownAt = new Map()
|
||||
/** 本批已飘过的业务弹幕 id */
|
||||
const shownIds = new Set()
|
||||
|
||||
const itemKey = (item) => {
|
||||
if (item?.id != null) return String(item.id)
|
||||
return `${item?.name || ''}|${item?.content || ''}|${item?.created_at || ''}`
|
||||
}
|
||||
|
||||
const maxItemId = () => {
|
||||
let max = 0
|
||||
for (const it of items.value) {
|
||||
const id = Number(it?.id) || 0
|
||||
if (id > max) max = id
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
const isBatchDone = () => {
|
||||
const list = items.value
|
||||
if (!list.length) return true
|
||||
return list.every((it) => it?.id != null && shownIds.has(Number(it.id)))
|
||||
}
|
||||
|
||||
const isCooling = (key) => {
|
||||
const t = lastShownAt.get(key)
|
||||
return t != null && Date.now() - t < DEDUPE_MS
|
||||
@@ -66,10 +83,15 @@ const pickNextItem = () => {
|
||||
return null
|
||||
}
|
||||
|
||||
const markShown = (item) => {
|
||||
if (item?.id != null) shownIds.add(Number(item.id))
|
||||
}
|
||||
|
||||
const buildRow = (item, { force = false } = {}) => {
|
||||
const key = itemKey(item)
|
||||
if (!force && isCooling(key)) return null
|
||||
lastShownAt.set(key, Date.now())
|
||||
markShown(item)
|
||||
|
||||
const tint = resolveDanmakuTint(item.color)
|
||||
const track = Math.floor(Math.random() * TRACKS)
|
||||
@@ -77,7 +99,6 @@ const buildRow = (item, { force = false } = {}) => {
|
||||
const delay = force ? 0 : Math.random() * 0.4
|
||||
const id = ++uid
|
||||
|
||||
// 发送者刚插入的弹幕强制「刚刚」,避免服务端时间解析偏差
|
||||
const timeLabel = force
|
||||
? '刚刚'
|
||||
: formatRelativeTime(item.created_at || Date.now())
|
||||
@@ -98,16 +119,36 @@ const buildRow = (item, { force = false } = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
const mergeItems = (incoming) => {
|
||||
if (!incoming.length) return
|
||||
const map = new Map(items.value.map((it) => [it.id, it]))
|
||||
for (const it of incoming) {
|
||||
if (it?.id != null) map.set(it.id, it)
|
||||
}
|
||||
items.value = Array.from(map.values()).sort((a, b) => (a.id || 0) - (b.id || 0))
|
||||
}
|
||||
|
||||
const fetchList = async () => {
|
||||
if (!props.enabled) {
|
||||
items.value = []
|
||||
shownIds.clear()
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await getDanmaku()
|
||||
const next = Array.isArray(res.data) ? res.data : []
|
||||
items.value = next
|
||||
if (next.length && cursor >= next.length) cursor = cursor % next.length
|
||||
const done = isBatchDone()
|
||||
const maxId = maxItemId()
|
||||
if (!done && maxId > 0) {
|
||||
const res = await getDanmaku({ after_id: maxId })
|
||||
const next = Array.isArray(res.data) ? res.data : []
|
||||
mergeItems(next)
|
||||
} else {
|
||||
const res = await getDanmaku()
|
||||
const next = Array.isArray(res.data) ? res.data : []
|
||||
items.value = next
|
||||
shownIds.clear()
|
||||
cursor = 0
|
||||
}
|
||||
if (items.value.length && cursor >= items.value.length) cursor = cursor % items.value.length
|
||||
} catch {
|
||||
/* keep previous items on transient failure */
|
||||
}
|
||||
@@ -177,6 +218,7 @@ watch(
|
||||
rows.value = []
|
||||
items.value = []
|
||||
lastShownAt.clear()
|
||||
shownIds.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,6 +237,7 @@ watch(
|
||||
rows.value = []
|
||||
items.value = []
|
||||
lastShownAt.clear()
|
||||
shownIds.clear()
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -236,7 +279,6 @@ defineExpose({ refresh: fetchList, pushItem })
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
backdrop-filter: blur(6px);
|
||||
/* 不裁切宽度:长祝福时末尾相对时间需完整露出并随整条飞过 */
|
||||
width: max-content;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ const burst = (count = 3) => {
|
||||
const id = ++uid
|
||||
const left = 10 + Math.random() * 200
|
||||
const drift = (Math.random() - 0.5) * 90
|
||||
const duration = 1.25 + Math.random() * 0.7
|
||||
const delay = i * 0.045
|
||||
const duration = 2.8 + Math.random() * 1.0
|
||||
const delay = i * 0.1
|
||||
const scale = 0.8 + Math.random() * 0.55
|
||||
hearts.value.push({
|
||||
id,
|
||||
@@ -82,7 +82,7 @@ defineExpose({ burst })
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translate3d(var(--drift, 0px), -46vh, 0) scale(var(--scale, 1));
|
||||
transform: translate3d(var(--drift, 0px), -55vh, 0) scale(var(--scale, 1));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user