Bilibili-Evolved 直播弹幕发送栏组件源码解析:网页全屏下的弹幕输入增强实现
【免费下载链接】Bilibili-Evolved强大的哔哩哔哩增强脚本项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved
导读
本文围绕 Bilibili-Evolved(哔哩哔哩增强脚本)内置组件「直播弹幕发送栏」展开,介绍其在直播网页全屏(player-full-win)与浏览器全屏模式下于底部控制栏展示弹幕输入框的实现原理,并深入剖析组件入口、Vue 组件与样式层的源码细节。读完本文,你将掌握该组件从「URL 匹配 → 控制栏挂载 → 原生输入框联动 → 发送回调」的完整调用链,理解其如何在不修改 B 站原始 DOM 的前提下完成输入增强。
组件是什么
该组件位于registry/lib/components/live/danmaku-sendbar/目录,其官方描述为:
在直播的网页全屏和全屏模式状态下,在底部显示弹幕栏。
即:当观众将直播播放器切换为网页全屏或浏览器全屏后,原始输入栏被隐藏,该组件会在播放器底部控制区注入一个轻量弹幕输入框,保持「边看全屏直播边发弹幕」的体验。其组件名为liveDanmakuSendbar,显示名称为「直播弹幕发送栏」,在设置面板中归属于「直播」(live)标签分类。
目录结构与关键文件
| 文件 | 职责 |
|---|---|
| index.ts | 组件入口,负责 URL 匹配、等待控制栏、插入输入框 DOM |
| DanmakuSendbar.vue | 弹幕输入框 UI 与原生输入框联动逻辑 |
| original-elements.ts | B 站原始直播页面的 DOM 选择器常量 |
| index.md | 组件描述文档 |
组件入口:从 URL 匹配到控制栏挂载
1. 登录态校验
入口函数首先通过getUID()判断用户是否登录,未登录(UID 为空)则直接返回,不做任何注入。这与 B 站弹幕发送需要登录的约束保持一致。
2. 直播页面 URL 匹配
组件元数据通过urlInclude: liveUrls限定生效范围。在 src/core/utils/urls.ts 中,liveUrls定义为:
export const liveUrls = [/^https:\/\/live\.bilibili\.com\/(blanc\/)?[\d]+/]即仅当页面 URL 匹配live.bilibili.com/{房间号}(可选blanc/前缀,用于 web 端与直播姬的区分)时组件才运行。
3. 等待控制栏出现
直播播放器的控制栏是「鼠标移入播放器时创建、移出时销毁」的动态 DOM。因此组件使用waitForControlBar(定义于 src/components/live/live-control-bar.ts)来监听控制栏容器的子节点变化:
- 通过
select轮询等待.bilibili-live-player-video-controller或.web-player-controller-wrap容器出现; - 通过
childList观察容器子节点变化,当.control-area(控制栏)被创建时执行回调。
4. 插入弹幕发送栏
在回调中,组件先定位控制栏内的.left-area(左侧控制器区域,选择器定义于 original-elements.ts),若找不到则抛出[danmakuSendBar] leftController not found错误。随后:
- 通过
dq(controlBar, '.danmaku-send-bar')检查是否已注入过,避免重复插入; - 使用
mountVueComponent将 DanmakuSendbar.vue 实例挂载为真实 DOM; - 通过
leftController.insertAdjacentElement('afterend', danmakuSendBarElement)将输入框插入到左侧控制器之后,与播放器控制栏布局融为一体。
5. 卸载与重载
组件定义了reload与unload钩子:卸载时向document.body添加danmaku-send-bar-unloaded类,重载时移除该类。样式层通过该类的有无决定是否显示输入框,从而实现「关闭组件后输入框立即隐藏、重新开启后立即恢复」的即时热切换,无需刷新页面。
弹幕输入框:Vue 组件与原生 DOM 的联动
DanmakuSendbar.vue 是核心 UI 组件,模板极为精简:
<div class="danmaku-send-bar"> <input type="text" placeholder="发个弹幕呗~" :value="value" maxlength="30" @keydown.enter="send()" @input="updateValue($event.target.value)" /> </div>要点说明:
maxlength="30"与 B 站弹幕 30 字上限保持一致;- 回车触发
send()发送弹幕; - 输入事件实时同步到原生输入框。
与原生弹幕输入框的双向同步
组件并不自己发起弹幕请求,而是「代理」B 站原始输入框。在mounted钩子中:
const originalTextArea = (await select(originalTextAreaSelector)) as HTMLTextAreaElement const sendButton = (await select(sendButtonSelector)) as HTMLButtonElement其中select是 src/core/spin-query.ts 提供的轮询查询工具(带超时与最大次数限制),用于等待原始弹幕输入框与发送按钮渲染完成。选择器常量如下:
export const originalTextAreaSelector = '.control-panel-ctnr .chat-input-ctnr .chat-input' export const sendButtonSelector = '.control-panel-ctnr .chat-input-ctnr ~ .bottom-actions .bl-button--primary'即原始输入框位于直播面板的chat-input-ctnr容器内,发送按钮为其后的bl-button--primary主按钮。
输入同步的三条路径
- 自定义输入 → 原生:
updateValue(newValue)将值写入originalTextArea.value,并通过raiseEvent派发input事件(raiseEvent定义于 src/core/utils/index.ts),让 B 站自己的输入处理逻辑感知变化。 - 原生 → 自定义:监听原生输入框的
input与change事件,将最新值回填到组件value,保证从原始入口(如直播面板底部输入框)输入时,全屏输入框同步显示。 - 属性级拦截兜底:当首次挂载时,代码重写
originalTextArea实例上的value属性 setter:
Object.defineProperty(originalTextArea, 'value', { ...original, set(value: string) { original.set?.call(this, value) raiseEvent(originalTextArea, 'input') }, })这样即使 B 站脚本直接给原生输入框赋值,也会自动补发input事件,保证同步不遗漏。该操作通过模块级变量changeEventHook保证只执行一次,避免重复拦截。
发送逻辑
send() { if (!this.sendButton.disabled) { this.value = '' this.sendButton.click() } }send()仅在发送按钮可用(未禁用)时执行:先清空自定义输入框,再模拟点击原生发送按钮,完全复用 B 站原生的弹幕发送链路。组件卸载(beforeDestroy)时会移除对原生输入框的事件监听,避免内存泄漏。
样式层:全屏模式下的展示控制
组件的<style>部分(scss)体现了「默认隐藏、全屏展示」的策略:
.live-web-player-controller { .danmaku-send-bar { display: none; } } .live-web-player-controller { background-image: linear-gradient(to bottom, transparent 20%, rgba(0, 0, 0, 0.9)); }默认情况下输入框隐藏;同时为控制栏追加自底部向上渐变至半透明的背景,提升底部区域可读性。
在@media screen and (min-width: 1038px)视口宽度下,配合.player-full-win(网页全屏)与:not(.danmaku-send-bar-unloaded)(组件未卸载)条件:
.live-web-player-controller .control-area { .danmaku-send-bar { display: flex; margin: 0 24px; flex: 1 1 0; height: 24px; justify-content: center; align-items: center; input { border: none; border-bottom: 2px solid #fff8; background-color: transparent; color: #fff; flex: 1; max-width: 400px; min-width: 70px; &:focus-within { border-color: var(--theme-color); } } } .right-area { flex: 0 0 auto !important; } }样式要点:
- 输入框采用「透明背景 + 白色下划线」的沉浸式设计,聚焦时下划线变为主题色(
var(--theme-color),跟随 Bilibili-Evolved 的主题色系统); - 输入框宽度弹性伸缩(
flex: 1),最大 400px、最小 70px,保证窄屏可用; .right-area强制flex: 0 0 auto,避免右侧控制按钮被挤压变形。
与项目的整体关系
从更宏观的视角看,该组件体现了 Bilibili-Evolved 几个通用设计模式:
- 组件元数据驱动:通过
defineComponentMetadata(定义于 src/components/define.ts)声明name、displayName、tags、urlInclude、entry、reload、unload等字段,统一接入设置面板与生命周期管理。其tags: [componentsTags.live]使用的「直播」标签定义于 src/components/types.ts。 - 不改动原始 DOM 语义:通过插入旁路元素 + 代理原生控件的方式实现增强,卸载时可完整还原页面。
- v1 兼容迁移:在 registry/lib/components/utils/v1-migrate/migrate.ts 中存在
featureMap('danmakuSendBar', 'liveDanmakuSendbar', 'component')映射,说明 v1 时代的同名功能在 v2 中更名为liveDanmakuSendbar,旧用户导入 v1 设置时可自动迁移到新组件。
使用方式
在 Bilibili-Evolved 的设置面板中,进入「直播」标签分类即可找到「直播弹幕发送栏」,将其开启后,进入任意直播间并切换为网页全屏(或全屏)模式,底部控制栏即会出现带占位文案「发个弹幕呗~」的输入框,回车即可发送弹幕。
注意:该组件依赖 B 站直播页面的 DOM 结构(.control-panel-ctnr .chat-input-ctnr .chat-input等选择器),若 B 站改版导致这些结构变化,组件会抛出ref elements not found错误并停止工作;同时其仅在视口宽度 ≥ 1038px 时展示,小窗口下输入框会保持隐藏。
【免费下载链接】Bilibili-Evolved强大的哔哩哔哩增强脚本项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考