Update barrage.js
This commit is contained in:
1 parent
50ca17341f
commit
e66ecb0c80
1 file changed
+130
-31
@@ -153,13 +153,69 @@
|
||||
|
||||
// 追踪当前屏幕上显示的弹幕 ID
|
||||
this.activeIds = new Set();
|
||||
// 最大同屏数量限制,防止卡顿
|
||||
// 最大同屏数量限制
|
||||
this.maxActiveItems = 30;
|
||||
|
||||
// 对象池
|
||||
this.pool = [];
|
||||
this.initPool();
|
||||
|
||||
// 绑定事件委托
|
||||
this.initEventDelegation();
|
||||
}
|
||||
|
||||
initPool() {
|
||||
// 初始化对象池,预创建 DOM
|
||||
const fragment = document.createDocumentFragment();
|
||||
for (let i = 0; i < this.maxActiveItems; i++) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'barrage-item';
|
||||
item.style.display = 'none'; // 初始隐藏
|
||||
item.style.willChange = 'transform'; // 提示浏览器优化
|
||||
|
||||
// 预构建结构
|
||||
const avatar = document.createElement('img');
|
||||
avatar.className = 'barrage-avatar';
|
||||
avatar.draggable = false;
|
||||
|
||||
const bubble = document.createElement('div');
|
||||
bubble.className = 'barrage-bubble';
|
||||
const textSpan = document.createElement('span');
|
||||
textSpan.className = 'barrage-text';
|
||||
bubble.appendChild(textSpan);
|
||||
|
||||
item.appendChild(avatar);
|
||||
item.appendChild(bubble);
|
||||
|
||||
// 绑定动画结束事件(只需绑定一次)
|
||||
item.addEventListener('animationend', () => {
|
||||
this.recycle(item);
|
||||
});
|
||||
|
||||
this.pool.push({
|
||||
el: item,
|
||||
avatarEl: avatar,
|
||||
textEl: textSpan,
|
||||
busy: false,
|
||||
id: null
|
||||
});
|
||||
fragment.appendChild(item);
|
||||
}
|
||||
this.container.appendChild(fragment);
|
||||
}
|
||||
|
||||
recycle(itemEl) {
|
||||
const poolItem = this.pool.find(p => p.el === itemEl);
|
||||
if (poolItem) {
|
||||
poolItem.busy = false;
|
||||
poolItem.el.style.display = 'none';
|
||||
if (poolItem.id) {
|
||||
this.activeIds.delete(poolItem.id);
|
||||
poolItem.id = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initEventDelegation() {
|
||||
// 鼠标进入容器暂停
|
||||
this.container.addEventListener('mouseover', (e) => {
|
||||
@@ -205,10 +261,24 @@
|
||||
|
||||
start(data = []) {
|
||||
this.stop();
|
||||
this.clearDOM();
|
||||
// 不再清除 DOM,而是重置池子
|
||||
this.resetPool();
|
||||
this.isDestroyed = false;
|
||||
this.updateData(data);
|
||||
}
|
||||
|
||||
resetPool() {
|
||||
this.pool.forEach(item => {
|
||||
item.busy = false;
|
||||
item.el.style.display = 'none';
|
||||
item.id = null;
|
||||
if (item.animation) {
|
||||
item.animation.cancel();
|
||||
item.animation = null;
|
||||
}
|
||||
});
|
||||
this.activeIds.clear();
|
||||
}
|
||||
|
||||
updateData(data = []) {
|
||||
// 数据预处理:去重 & ID 规范化
|
||||
@@ -258,20 +328,21 @@
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
// 注意:不清除 activeIds,允许已发射的弹幕飞完
|
||||
}
|
||||
|
||||
clearDOM() {
|
||||
if (this.container) {
|
||||
this.container.innerHTML = '';
|
||||
}
|
||||
this.activeIds.clear();
|
||||
// 兼容旧接口:实际上就是 resetPool
|
||||
this.resetPool();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.isDestroyed = true;
|
||||
this.stop();
|
||||
this.clearDOM();
|
||||
this.resetPool();
|
||||
// 彻底销毁时才移除 DOM
|
||||
if (this.container) {
|
||||
this.container.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
loop() {
|
||||
@@ -316,42 +387,70 @@
|
||||
shoot(itemData) {
|
||||
const { text, avatar, url, id } = itemData;
|
||||
|
||||
// 查找空闲的池对象
|
||||
const poolItem = this.pool.find(p => !p.busy);
|
||||
if (!poolItem) return; // 没有空闲对象,放弃本次发射
|
||||
|
||||
poolItem.busy = true;
|
||||
poolItem.id = id;
|
||||
this.activeIds.add(id);
|
||||
|
||||
const item = document.createElement('div');
|
||||
item.className = 'barrage-item';
|
||||
// 存储 URL 到 dataset 以供事件委托使用
|
||||
const { el, avatarEl, textEl } = poolItem;
|
||||
|
||||
// 更新数据
|
||||
if (url && url !== '#') {
|
||||
item.dataset.url = url;
|
||||
item.style.cursor = 'pointer';
|
||||
el.dataset.url = url;
|
||||
el.style.cursor = 'pointer';
|
||||
} else {
|
||||
el.removeAttribute('data-url');
|
||||
el.style.cursor = 'default';
|
||||
}
|
||||
|
||||
// 只有当有头像时才显示 img
|
||||
if (avatar) {
|
||||
avatarEl.src = avatar;
|
||||
avatarEl.style.display = 'block';
|
||||
// 错误处理
|
||||
avatarEl.onerror = function() {
|
||||
this.src = 'https://cravatar.cn/avatar/default?d=mp';
|
||||
};
|
||||
} else {
|
||||
avatarEl.style.display = 'none';
|
||||
}
|
||||
|
||||
let html = '';
|
||||
// 优化:只有当有头像时才渲染 img 标签
|
||||
// 移除 loading="lazy" 以修复弹幕头像加载问题
|
||||
if (avatar) {
|
||||
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;
|
||||
textEl.innerText = text;
|
||||
|
||||
// 样式与动画
|
||||
// 计算轨道
|
||||
const tracks = 10;
|
||||
const trackIndex = Math.floor(Math.random() * tracks);
|
||||
const top = 5 + (trackIndex * (90 / tracks));
|
||||
el.style.top = `${top}%`;
|
||||
|
||||
item.style.top = `${top}%`;
|
||||
|
||||
const duration = 18 + Math.random() * 7;
|
||||
item.style.animation = `barrage-slide-right ${duration}s linear forwards`;
|
||||
// 显示
|
||||
el.style.display = 'inline-flex';
|
||||
|
||||
// 仅绑定必要的生命周期事件
|
||||
item.addEventListener('animationend', () => {
|
||||
item.remove();
|
||||
this.activeIds.delete(id);
|
||||
// 使用 Web Animations API (WAAPI) 替代 CSS Animation
|
||||
// 优势:无需触发 offsetHeight 重绘,控制更精准
|
||||
const duration = (18 + Math.random() * 7) * 1000; // ms
|
||||
|
||||
const keyframes = [
|
||||
{ transform: 'translateX(-100%)', opacity: 0, offset: 0 },
|
||||
{ opacity: 1, offset: 0.05 },
|
||||
{ opacity: 1, offset: 0.95 },
|
||||
{ transform: 'translateX(100vw)', opacity: 0, offset: 1 }
|
||||
];
|
||||
|
||||
const animation = el.animate(keyframes, {
|
||||
duration: duration,
|
||||
easing: 'linear',
|
||||
fill: 'forwards'
|
||||
});
|
||||
|
||||
this.container.appendChild(item);
|
||||
animation.onfinish = () => {
|
||||
this.recycle(el);
|
||||
};
|
||||
|
||||
poolItem.animation = animation;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user