Bilibili-Evolved 直播弹幕发送栏组件源码解析:网页全屏下的弹幕输入增强实现
2026/9/19 9:49:54 网站建设 项目流程

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.tsB 站原始直播页面的 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. 卸载与重载

组件定义了reloadunload钩子:卸载时向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主按钮。

输入同步的三条路径

  1. 自定义输入 → 原生updateValue(newValue)将值写入originalTextArea.value,并通过raiseEvent派发input事件(raiseEvent定义于 src/core/utils/index.ts),让 B 站自己的输入处理逻辑感知变化。
  2. 原生 → 自定义:监听原生输入框的inputchange事件,将最新值回填到组件value,保证从原始入口(如直播面板底部输入框)输入时,全屏输入框同步显示。
  3. 属性级拦截兜底:当首次挂载时,代码重写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)声明namedisplayNametagsurlIncludeentryreloadunload等字段,统一接入设置面板与生命周期管理。其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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询