NPlayer播放器挂载弹幕

608 阅读2分钟

NPlayer播放器挂载弹幕

官网地址: nplayer.js.org/docs/

NPlayer 是由 Typescript 加 Sass 编写,无任何第三方运行时依赖,兼容 IE11,支持移动端、支持 SSR、支持直播。高度可定制,所有图标、主题色等都可以替换,并且提供了内置组件方便二次开发。你可以自定义任意多个断点,不仅仅是兼容移动端,只要愿意,你可以非常轻松的兼容手机竖屏、手机横屏、平板等设备。它还拥有插件系统,弹幕功能就是使用插件形式提供,使用时按需引入即可。该播放器还可以接入任何流媒体,如 hls、dash 和 flv 等。

NPlayer安装和使用

通过 npm 、yarn或pnpm 安装

npm i -S nplayer

使用

import Player from 'nplayer'

// 播放器配置
const options = reactive({
    ······
})

const player = new Player(options)
// #app 页面中容纳视频的元素的 id
player.mount('#app')

其余配置项请前往官网查看。

实现挂载弹幕

安装弹幕插件

npm i -S @nplayer/danmaku

快速上手

interface BulletOption {
  color?: string; // 弹幕颜色
  text: string; // 弹幕文字
  time: number; // 弹幕出现时间(s)
  type?: 'top' | 'bottom' | 'scroll'; // 弹幕类型,默认为滚动类型
  isMe?: boolean; // 是否是当前用户发送的
  force?: boolean; // 是否强制展示该弹幕(弹幕较多,并且是防碰撞模式时,可能会丢弃一部分弹幕)
}
import Player from 'nplayer'
import Danmaku from '@nplayer/danmaku'

const danmakuOptions = {
  autoInsert: true,
  items: [
    { time: 1, text: '弹幕~' }
  ]
}
const options = reactive({
    ······,
    plugins: [new Danmaku(danmakuOptions)]
})


const player = new Player(options)

player.mount("#app")

注意事项,见官网文档:

image.png

  • 弹幕数组items必须按时间 time 从小到大排序

解析 xml 弹幕

  • 浏览器内置的DOMParser来解析 XML 数据
  • 排序用的 sort 方法,数据量过大时可自行优化排序方式
    // 是否开启彩色弹幕
    const colorfulBarrage = ref(true)
    // 挂载弹幕
    const mountBarrage = () => {
        // 选取本地文件
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = '.xml';
        fileInput.addEventListener('change', (event) => {
            const file = (event.target as HTMLInputElement).files![0];
            const reader = new FileReader();
            reader.onload = (e) => {
                const xmlData = e.target!.result as string;
                const parser = new DOMParser();
                const xmlDoc = parser.parseFromString(xmlData, "application/xml");
                const danmakuElements = xmlDoc.querySelectorAll('d');
                const danmuArr: any = [];
                danmakuElements.forEach((danmakuElement) => {
                    const attributes = danmakuElement.getAttribute('p')!.split(',');
                    const timeInSeconds = parseFloat(attributes[0]);
                    const danmakuText = danmakuElement.textContent;
                    // 直接使用展开语法来创建对象,更简洁高效
                    danmuArr.push({
                        time: Math.round(timeInSeconds * 1),
                        text: danmakuText,
                        color: colorfulBarrage.value ? "#" + attributes[6] : "#FFFFFF",
                        type: attributes[1] == '1' ? 'scroll' : attributes[1] == '5' ? 'bottom' : 'top',
                    });
                });

                // 使用requestAnimationFrame替代setTimeout,优化渲染时机
                requestAnimationFrame(() => {
                    // danmuArr 按时间排序
                    danmuArr.sort((a, b) => a.time - b.time);
                    danmakuSource.value = danmuArr;
                    if (player) {
                        player.danmaku.resetItems(danmuArr);
                    } else {
                        danmakuOptions.items = danmuArr;
                    }
                });
            }
            reader.readAsText(file);
        });
        fileInput.click();
    }