这可能是见过的最好用的弹幕库 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
imtaotao
V2EX    前端开发

这可能是见过的最好用的弹幕库

  •  1
     
  •   imtaotao 2024-09-02 20:20:16 +08:00 2013 次点击
    这是一个创建于 474 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近把五年前写的一个弹幕库给重构了一下,本来两年前就想着做这件事,但是其中有一段工作时间压力很大,所以就搁置了,导致没有时间来做这件事情,最近周末 + 晚上花一些时间重构了下,并把文档好好写了一下。言归正传,这篇文章会介绍部分这个弹幕库有的能力,如果正好符合你的需求或者感兴趣,可以帮忙点点 star 来支持一下。

    我们有哪些能力

    我们提供了灵活调整轨道自定义弹幕和容器样式弹幕运动算法等能力,还在提供非常丰富的钩子来让用户处理自定义的行为,只要你想要的,都能做到,本文档会简单介绍一些能力和一些功能的实现。

    快速开始

    对于一个开箱即用的 demo ,可以非常简单的接入,如下所示:

    import { create } from 'danmu'; const manager = create(); manager.mount('#root'); manager.startPlaying(); // 发送弹幕 manager.push('弹幕内容') 

    对轨道进行调整

    我们对支持类似 CSS calc 表达式的能力,一些位置/宽高等信息都可以用表达式来计算。所以对于轨道来说可以很方便的进行调整。

    1. **number**:默认单位为px
    2. **string**:表达式计算。支持(+,-,*,/)数学计算,只支持%px两种单位。
    // 例如,这里的 100% 是指容器宽度(如果是高度相关的配置 100% 就是容器的高度) manager.setGap('(100% - 10px) / 5'); 

    限制为顶部 3 条弹幕

    // 如果我们希望轨道高度为 50px manager.setTrackHeight('100% / 3'); // 如果不设置渲染区域,轨道的高度会根据默认的 container.height / 3 得到, // 这可能导致轨道高度不是你想要的 manager.setArea({ y: { start: 0, // 3 条轨道的总高度为 150px end: 150, }, }); 

    限制为中间 3 条弹幕

    manager.setTrackHeight('100% / 3'); manager.setArea({ y: { start: `50%`, end: `50% + 150`, }, }); 

    限制为几条不连续的轨道

    限制为几条不连续的轨道,除了要做和连续轨道的操作之外,还需要借助willRender这个钩子来实现。

    // 如果我们希望轨道高度为 50px ,并渲染 0 ,2 ,4 这几条轨道 manager.setTrackHeight('100% / 6'); // 设置容器的渲染区域 manager.setArea({ y: { start: 0, // 6 条轨道的总高度为 300px end: 300, }, }); manager.use({ willRender(ref) { // 高级弹幕和轨道不强相关,没有 trackIndex 这个属性 if (ref.trackIndex === null) return ref; // 如果为 1 ,3 ,5 这几条轨道就阻止渲染,并重新添加等待下次渲染 if (ref.trackIndex % 2 === 1) { ref.prevent = true; manager.unshift(ref.danmaku); } return ref; }, }); 

    自定义渲染

    弹幕和容器都允许自定义的渲染样式,你可以很方便的做到。

    自定义弹幕的样式

    1. 通过manager.setStyle来设置

    import { create } from 'danmu'; // 需要添加的样式 const styles = { color: 'red', fontSize: '15px', // . }; const manager = create(); // 后续渲染的弹幕和当前已经渲染的弹幕会设置上这些样式。 for (const key in styles) { manager.setStyle(key, styles[key]); } 

    2. 通过danamaku.setStyle来设置

    import { create } from 'danmu'; // 需要添加的样式 const styles = { color: 'red', fontSize: '15px', // . }; // 初始化的时候添加钩子处理,这样当有新的弹幕渲染时会自动添加上这些样式 const manager = create({ plugin: { $moveStart(danmaku) { for (const key in styles) { danmaku.setStyle(key, styles[key]); } // 你也可以在这里给弹幕 DOM 添加 className danmaku.node.classList.add('className'); }, }, }); // 对当前正在渲染的弹幕添加样式 manager.asyncEach((danmaku) => { for (const key in styles) { danmaku.setStyle(key, styles[key]); } }); 

    自定义容器样式

    import { create } from 'danmu'; // 需要添加的样式 const styles = { background: 'red', // . }; const manager = create({ plugin: { // 你可以在初始化的时候添加钩子处理 init(manager) { for (const key in styles) { manager.container.setStyle(key, styles[key]); } // 你也可以在这里给容器 DOM 添加 className manager.container.node.classList.add('className'); }, }, }); // 或者直接调用 api for (const key in styles) { manager.container.setStyle(key, styles[key]); } 

    高级弹幕的示例

    本章节将介绍如何将弹幕固定在某一位置,以topleft这两个位置举例。由于我们需要自定义位置,所以我们需要使用高级弹幕的能力。

    将弹幕固定在顶部

    // 这条弹幕将会居中距离顶部 10px 的位置悬停 5s manager.pushFlexibleDanmaku('弹幕内容', { duration: 5000, direction: 'none', position(danmaku, container) { return { x: `50% - ${danmaku.getWidth() / 2}`, y: 10, // 具体容器顶部的距离为 10px }; }, }); 

    固定在顶部第 2 条轨道上

    // 这条弹幕将会在第二条轨道居中的位置悬停 5s manager.pushFlexibleDanmaku('弹幕内容', { duration: 5000, direction: 'none', position(danmaku, container) { // 渲染在第 3 条轨道中 const { middle } = manager.getTrackLocation(2); return { x: `50% - ${danmaku.getWidth() / 2}`, y: middle - danmaku.getHeight() / 2, }; }, }); 

    将弹幕固定在左边

    // 这条弹幕将会在容器中间距离左边 10px 的地方停留 5s manager.pushFlexibleDanmaku('弹幕内容', { duration: 5000, direction: 'none', position(danmaku, container) { // 渲染在第 3 条轨道中 const { middle } = manager.getTrackLocation(2); return { x: 10, y: `50% - ${danmaku.getHeight() / 2}`, }; }, }); 

    发送带图片的弹幕

    要让弹幕里面能够携带图片,要在弹幕的节点内部添加自定义的内容,实际上不止图片,你可以往弹幕的节点里面添加任何的内容。

    本章节的组件以React来实现演示。

    开发弹幕组件

    export function Danmaku({ danmaku }) { return ( <div> <img src="https://abc.jpg" /> {danmaku.data} </div> ); } 

    渲染弹幕

    import ReactDOM from 'react-dom/client'; import { create } from 'danmu'; import { Danmaku } from './Danmaku'; const manager = create<string>({ plugin: { // 将组件渲染到弹幕的内置节点上 $createNode(danmaku) { ReactDOM.createRoot(danmaku.node).render(<Danmaku danmaku={danmaku} />); }, }, }); 

    编写一个插件

    编写一个插件是很简单的,但是借助内核暴露出来的钩子API,你可以很轻松的实现强大且定制化的需求。由于内核没有暴露出来根据条件来实现过滤弹幕的功能,原因在于内核不知道弹幕内容的数据结构,这和业务的诉求强相关,所以我们在此通过插件来实现精简弹幕的功能用来演示。

    编写一个插件

    • 你编写的插件应当取一个name,以便于调试定位问题(注意不要和其他插件冲突了)。
    • 插件可以选择性的声明一个version,这在你的插件作为独立包发到npm上时很有用。
    export function filter({ userIds, keywords }) { return (manager) => { return { name: 'filter-keywords-or-user', version: '1.0.0', // version 字段不是必须的 willRender(ref) { const { userId, content } = ref.danmaku.data.value; console.log(ref.type); // 可以根据此字段来区分是普通弹幕还是高级弹幕 if (userIds && userIds.includes(userId)) { ref.prevent = true; } else if (keywords) { for (const word of keywords) { if (content.includes(word)) { ref.prevent = true; break; } } } return ref; }, }; }; } 

    注册插件

    你需要通过mananger.use()来注册插件。

    import { create } from 'danmu'; const manager = create<{ userId: number; content: string; }>(); manager.use( filter({ userIds: [1], keywords: ['菜'], }), ); 

    发送弹幕

    • 被插件阻止渲染
    manager.push({ userId: 1, content: '', }); 
    • 被插件阻止渲染
    manager.push({ userId: 2, content: '你真菜', }); 
    • 不会被插件阻止渲染
    manager.push({ userId: 2, content: '', }); 
    • 不会被插件阻止渲染
    manager.push({ userId: 2, content: '你真棒', }); 

    总结

    本文档只是简单介绍了下现在的部分能力,更详细的文档在官网可以查看,如果对你的业务或者学习有帮助的,给个 star 支持一下作者,也欢迎大家评论探讨(不止弹幕,哈哈)。

    3 条回复    2024-09-03 14:06:04 +08:00
    v1
        1
    v1  
       2024-09-02 20:27:02 +08:00
    +1s
    Tubbs
        2
    Tubbs  
       2024-09-03 10:58:44 +08:00 via iPhone
    看着很不错,支持一下
    imtaotao
        3
    imtaotao  
    OP
       2024-09-03 14:06:04 +08:00
    @Tubbs 感谢
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2655 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 12:18 PVG 20:18 LAX 04:18 JFK 07:18
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86