fixed 弹幕修复 bug
This commit is contained in:
1 parent
a4c74cbb0d
commit
0714b8eb48
11 files changed
+446
-139
No files matched your search
@@ -6,6 +6,7 @@
|
||||
function getArtalkComments() {
|
||||
return new Promise((resolve) => {
|
||||
let isResolved = false;
|
||||
let interval = null;
|
||||
|
||||
// 安全获取评论列表的辅助函数
|
||||
const safeGetComments = () => {
|
||||
@@ -22,66 +23,58 @@
|
||||
const tryResolve = (data, source) => {
|
||||
if (isResolved) return;
|
||||
|
||||
// 调试日志
|
||||
if (data && Array.isArray(data)) {
|
||||
console.log(`[Barrage] 数据检查 (${source}): ${data.length} 条`);
|
||||
} else {
|
||||
console.log(`[Barrage] 数据检查 (${source}): 无效数据`, data);
|
||||
}
|
||||
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
isResolved = true;
|
||||
resolve(data);
|
||||
if (Array.isArray(data)) {
|
||||
// 如果是事件触发(说明已加载完成),或者是轮询且有数据,则认为成功
|
||||
if (source === 'event' || data.length > 0) {
|
||||
console.log(`[Barrage] 数据获取成功 (${source}): ${data.length} 条`);
|
||||
isResolved = true;
|
||||
if (interval) clearInterval(interval);
|
||||
resolve(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 1. 立即检查
|
||||
const immediateData = safeGetComments();
|
||||
if (immediateData) {
|
||||
tryResolve(immediateData, 'immediate');
|
||||
} else {
|
||||
console.log('[Barrage] Artalk 实例未就绪 (Immediate)');
|
||||
}
|
||||
// 检查 Artalk 实例并尝试获取数据
|
||||
const checkArtalk = () => {
|
||||
if (isResolved) return;
|
||||
|
||||
if (isResolved) return;
|
||||
if (window.artalk) {
|
||||
// 1. 尝试绑定事件 (只绑定一次)
|
||||
if (!window.artalk._barrageListenerAttached && typeof window.artalk.on === 'function') {
|
||||
window.artalk.on('list-loaded', (data) => {
|
||||
tryResolve(data, 'event');
|
||||
});
|
||||
window.artalk._barrageListenerAttached = true;
|
||||
console.log('[Barrage] Artalk 事件监听已绑定');
|
||||
}
|
||||
|
||||
// 2. 监听加载完成事件 (推荐)
|
||||
if (window.artalk && typeof window.artalk.on === 'function') {
|
||||
window.artalk.on('list-loaded', (data) => {
|
||||
console.log('[Barrage] list-loaded 事件触发');
|
||||
// 优先使用事件传递的数据(如果有),否则尝试主动获取
|
||||
const comments = (Array.isArray(data) && data.length > 0) ? data : (safeGetComments() || []);
|
||||
tryResolve(comments, 'event');
|
||||
});
|
||||
}
|
||||
|
||||
// 3. 轮询 (后备)
|
||||
let pollCount = 0;
|
||||
const interval = setInterval(() => {
|
||||
if (isResolved) {
|
||||
clearInterval(interval);
|
||||
return;
|
||||
// 2. 尝试直接获取数据
|
||||
const data = safeGetComments();
|
||||
if (data) {
|
||||
tryResolve(data, 'poll');
|
||||
}
|
||||
}
|
||||
pollCount++;
|
||||
|
||||
const pollData = safeGetComments();
|
||||
if (pollData && Array.isArray(pollData) && pollData.length > 0) {
|
||||
tryResolve(pollData, `poll-${pollCount}`);
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// 4. 超时处理 (5秒)
|
||||
// 立即检查
|
||||
checkArtalk();
|
||||
|
||||
// 轮询检查 (直到获取数据或超时)
|
||||
// 增加轮询频率,以便更快响应 Artalk 初始化
|
||||
interval = setInterval(checkArtalk, 200);
|
||||
|
||||
// 超时处理 (8秒)
|
||||
setTimeout(() => {
|
||||
if (!isResolved) {
|
||||
clearInterval(interval);
|
||||
console.warn('[Barrage] 等待评论数据超时 (5s)');
|
||||
if (interval) clearInterval(interval);
|
||||
console.warn('[Barrage] 获取数据超时');
|
||||
|
||||
// 尝试从 DOM 获取作为最后兜底
|
||||
// 最后的尝试:DOM 兜底 (适用于静态页面或 API 失败但 DOM 已渲染的情况)
|
||||
const domComments = document.querySelectorAll('.atk-comment-wrap');
|
||||
if (domComments.length > 0) {
|
||||
console.log(`[Barrage] 从 DOM 挽救数据: ${domComments.length} 条`);
|
||||
const fallbackData = Array.from(domComments).map(el => {
|
||||
const idStr = el.id || ''; // atk-comment-123
|
||||
const idStr = el.id || '';
|
||||
const id = idStr.replace('atk-comment-', '');
|
||||
const contentEl = el.querySelector('.atk-content');
|
||||
const avatarEl = el.querySelector('.atk-avatar img');
|
||||
@@ -92,21 +85,19 @@
|
||||
id: id,
|
||||
content: contentEl.innerText || contentEl.textContent,
|
||||
avatar: avatarEl ? avatarEl.src : '',
|
||||
// 模拟 Artalk 数据结构
|
||||
ua: null
|
||||
};
|
||||
}).filter(Boolean);
|
||||
|
||||
if (fallbackData.length > 0) {
|
||||
isResolved = true;
|
||||
resolve(fallbackData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
resolve([]); // 真的没有数据
|
||||
resolve([]);
|
||||
}
|
||||
}, 5000);
|
||||
}, 8000);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -200,7 +191,10 @@
|
||||
this.stop();
|
||||
this.clearDOM();
|
||||
this.isDestroyed = false;
|
||||
this.updateData(data);
|
||||
}
|
||||
|
||||
updateData(data = []) {
|
||||
// 数据预处理:去重 & ID 规范化
|
||||
const uniqueData = [];
|
||||
const seenIds = new Set();
|
||||
@@ -214,11 +208,33 @@
|
||||
});
|
||||
|
||||
this.data = uniqueData;
|
||||
console.log(`[Barrage] 启动: 原始 ${data.length}, 去重后 ${this.data.length}`);
|
||||
// 如果数据更新时列表变短了,重置索引以防越界
|
||||
if (this.index >= this.data.length) {
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
if (this.data.length === 0) return;
|
||||
console.log(`[Barrage] 数据更新: ${this.data.length} 条`);
|
||||
|
||||
this.loop();
|
||||
if (this.data.length > 0 && !this.timer && !this.isPaused) {
|
||||
this.loop();
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (this.container) {
|
||||
this.container.style.opacity = '0';
|
||||
this.container.style.pointerEvents = 'none';
|
||||
}
|
||||
this.isPaused = true;
|
||||
}
|
||||
|
||||
show() {
|
||||
if (this.container) {
|
||||
this.container.style.opacity = '1';
|
||||
this.container.style.pointerEvents = 'auto';
|
||||
}
|
||||
this.isPaused = false;
|
||||
if (!this.timer) this.loop();
|
||||
}
|
||||
|
||||
stop() {
|
||||
@@ -226,24 +242,24 @@
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
this.activeIds.clear();
|
||||
// 注意:不清除 activeIds,允许已发射的弹幕飞完
|
||||
}
|
||||
|
||||
clearDOM() {
|
||||
if (this.container) {
|
||||
this.container.innerHTML = '';
|
||||
}
|
||||
this.activeIds.clear();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.isDestroyed = true;
|
||||
this.stop();
|
||||
this.clearDOM();
|
||||
// 移除事件监听需要保存引用,这里因为容器可能被销毁,且是单例,暂不处理复杂的解绑
|
||||
}
|
||||
|
||||
loop() {
|
||||
if (this.isDestroyed) return;
|
||||
if (this.isDestroyed || this.isPaused) return;
|
||||
|
||||
// 容器检测
|
||||
if (!document.body.contains(this.container)) {
|
||||
@@ -251,7 +267,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isPaused && this.data.length > 0) {
|
||||
if (this.data.length > 0) {
|
||||
// 只有当前屏幕数量未满时才发射
|
||||
if (this.activeIds.size < this.maxActiveItems) {
|
||||
this.tryShoot();
|
||||
@@ -264,6 +280,8 @@
|
||||
}
|
||||
|
||||
tryShoot() {
|
||||
if (this.data.length === 0) return;
|
||||
|
||||
let attempts = 0;
|
||||
const maxAttempts = this.data.length;
|
||||
|
||||
@@ -294,8 +312,9 @@
|
||||
|
||||
let html = '';
|
||||
// 优化:只有当有头像时才渲染 img 标签
|
||||
// 移除 loading="lazy" 以修复弹幕头像加载问题
|
||||
if (avatar) {
|
||||
html += `<img src="${avatar}" class="barrage-avatar" draggable="false" loading="lazy">`;
|
||||
html += `<img src="${avatar}" class="barrage-avatar" draggable="false" onerror="this.src='https://cravatar.cn/avatar/default?d=mp'">`;
|
||||
}
|
||||
html += `<div class="barrage-bubble"><span class="barrage-text">${text}</span></div>`;
|
||||
item.innerHTML = html;
|
||||
@@ -334,6 +353,19 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// 检测是否存在评论区
|
||||
const hasCommentSection = document.getElementById('artalk') ||
|
||||
document.getElementById('Comments') ||
|
||||
document.querySelector('.atk-comment-wrap');
|
||||
|
||||
if (!hasCommentSection) {
|
||||
// console.log('[Barrage] 当前页面无评论区,隐藏弹幕');
|
||||
if (barrageInstance) {
|
||||
barrageInstance.hide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 获取容器,如果没有则动态创建
|
||||
let container = document.querySelector('.barrager');
|
||||
if (!container) {
|
||||
@@ -343,10 +375,9 @@
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
// 2. 清理旧实例
|
||||
// 确保显示(无论是否有评论区,都允许弹幕存在,可能显示的是上一页的数据或 Hitokoto)
|
||||
if (barrageInstance) {
|
||||
barrageInstance.destroy();
|
||||
barrageInstance = null;
|
||||
barrageInstance.show();
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -371,13 +402,27 @@
|
||||
if (!text.trim()) return null;
|
||||
|
||||
let avatar = '';
|
||||
const domComment = document.getElementById(`atk-comment-${c.id}`);
|
||||
if (domComment) {
|
||||
const img = domComment.querySelector('.atk-avatar img');
|
||||
if (img && img.src) avatar = img.src;
|
||||
|
||||
// 1. 优先尝试使用 email_encrypted 构造 Gravatar (适用于跨页面无 DOM 的情况)
|
||||
if (c.email_encrypted) {
|
||||
avatar = `https://cravatar.cn/avatar/${c.email_encrypted}?d=mp&s=240`;
|
||||
} else if (c.email_hash) {
|
||||
avatar = `https://cravatar.cn/avatar/${c.email_hash}?d=mp&s=240`;
|
||||
}
|
||||
|
||||
// 2. 如果没有 hash,尝试从 API 对象直接获取
|
||||
if (!avatar && c.avatar) {
|
||||
avatar = c.avatar;
|
||||
}
|
||||
|
||||
// 3. 尝试从 UA 获取
|
||||
if (!avatar && c.ua && c.ua.avatar) {
|
||||
avatar = c.ua.avatar;
|
||||
}
|
||||
|
||||
// 4. 兜底默认头像
|
||||
if (!avatar) {
|
||||
avatar = c.avatar || (c.ua && c.ua.avatar) || 'https://cravatar.cn/avatar/default?d=mp';
|
||||
avatar = 'https://cravatar.cn/avatar/default?d=mp';
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -403,17 +448,18 @@
|
||||
|
||||
if (barrageList.length === 0) {
|
||||
console.log('[Barrage] 无弹幕数据');
|
||||
// 即使无数据,也不要销毁实例,保持空转即可
|
||||
return;
|
||||
}
|
||||
|
||||
// 再次检查实例 (竞态保护)
|
||||
// 更新实例或创建新实例
|
||||
if (barrageInstance) {
|
||||
barrageInstance.destroy();
|
||||
barrageInstance.updateData(barrageList);
|
||||
} else {
|
||||
console.log(`[Barrage] 初始化: ${barrageList.length} 条`);
|
||||
barrageInstance = new BarrageEngine({ container });
|
||||
barrageInstance.start(barrageList);
|
||||
}
|
||||
|
||||
console.log(`[Barrage] 最终加载: ${barrageList.length} 条`);
|
||||
barrageInstance = new BarrageEngine({ container });
|
||||
barrageInstance.start(barrageList);
|
||||
|
||||
} catch (e) {
|
||||
console.warn('[Barrage] 加载异常:', e);
|
||||
@@ -427,5 +473,10 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
setTimeout(loadBarrager, 500);
|
||||
});
|
||||
|
||||
// PJAX 适配
|
||||
document.addEventListener('pjax:complete', () => {
|
||||
setTimeout(loadBarrager, 500);
|
||||
});
|
||||
|
||||
})();
|
||||
Reference in new issue
Block a user