原文地址
前言
在对于web
端播放器的渲染基本是属于videoJs一家独大的情况,但是videoJs
对于弹幕功能是没有支持的,而且各种小功能都得自己手写,比如热键等。自定义程度较高但是如果希望快速实现一个还算是美观的播放器,videoJs
就稍显逊色了。如果我们希望实现弹幕功能,自己手写确实是一种方法,但是消耗时间比较多,需要反复调试,如果采用现有轮子的话,CommentCoreLibrary是相对来说比较好的选择,可是如果需要同时引用多个video
渲染包可能需要考虑兼容性等等问题,而且我看了下CommentCoreLibrary
的几个拓展组件都没那么完整,基本都已经是不维护的状态了。CommentCoreLibrary
还有一个比较麻烦的问题,不能直接进行模块化导入,虽然不影响使用,但是确实有点不太舒服
这里我们选择的是DPlayer
,先说结论,这个视频播放组件是我看到最适合【快速搭建一个带有弹幕还算美观的播放器】的选择。但是如果想真正做好的话还是推荐使用videoJs
和CommentCoreLibrary
的组合模式,DPlayer
的坑还是不少的,这一点看Issues
也能看出来,而且作者目前的维护状态堪忧
构建
他的测试地址很久之前就过期了,但是也没有在比较明显的给出弹幕接口的数据结构,我还得看源码猜他的数据结构,然后再写自己数据接口,看到issues
才找到他新的测试数据地址master/demo/demo.js,我们可以看到他的接口数据大概长这样
{
"code": 0,
"data": [
[
230.523,
0,
16777215,
"618c713c",
"键盘妹子挺好看?"
],
[
25.837,
0,
16777215,
"6b2884ac",
"Goose house炒鸡棒!!! 银之匙种草他们组合"
],
[
235.243,
1,
16707842,
"929815d3",
"刚才说比其他翻唱都好听的你是认真的么,这。就是原唱"
]
}
数据结构稍显奇怪,看下源码的处理
read: (options) => {
axios
.get(options.url)
.then((response) => {
const data = response.data;
if (!data || data.code !== 0) {
options.error && options.error(data && data.msg);
return;
}
options.success &&
options.success(
data.data.map((item) => ({
time: item[0],
type: item[1],
color: item[2],
author: item[3],
text: item[4],
}))
);
})
.catch((e) => {
console.error(e);
options.error && options.error();
});
},
这里他给的回调的数据结构还算是正常的数据结构,但是后端的给的数据结构有点过于离谱了,猜测这个人应该主要是前端工程师,这个数据结构应该是直接解析弹幕的xml
文件解析出来
弹幕xml
文件大概长这样,
<i>
<chatserver/>
<chatid>67173454</chatid>
<mission/>
<maxlimit/>
<state/>
<real_name/>
<source/>
<d p="0,1,25,16777215,1544891416,1,f6063692,9305714206965760">DIY</d>
<d p="0,1,25,16777215,1544891446,1,fb287ca7,9305730067726336">占楼</d>
<d p="0.698,5,25,15138834,1544891472,1,25e650c7,9305743667757056">大家早啊</d>
<d p="0.887,1,25,16777215,1544891485,1,efe26648,9305750776053760">爱丽丝</d>
<d p="0.957,1,25,16777215,1544891676,1,750501a,9305850754105344">真好</d>
<d p="0.198,5,25,15138834,1544892099,1,64f18b27,9306072548376576">沙发</d>
<d p="0.241,5,25,41194,1544892435,1,9fefa12,9306248345288704">借号的微笑B</d>
<d p="0.404,1,25,16777215,1544893076,1,36ff170a,9306584581668864">早早早</d>
</i>
如果导入弹幕文件的话,需要解析这个xml
文件,也是有一些小坑的,不过不在本文的讨论范围内就不赘述了
文档中有给可以自定义弹幕接口的字段danmaku.api
,但是你要是用这个就上当拉,这里他会在你写的api
后面增加一些自己的想法
let apiurl;
if (this.options.api.maximum) {
apiurl = `${this.options.api.address}v3/?id=${this.options.api.id}&max=${this.options.api.maximum}`;
} else {
apiurl = `${this.options.api.address}v3/?id=${this.options.api.id}`;
}
可以这么说,我接触这个组件所需要的功能,如果只是看文档,根本开发不下去,只能看源码。要是规定后端接口和数据必须得写成这样,还不如沙了我,还好提供了覆写的接口,我们这里的数据结构是
{
"status": 200,
"message": null,
"data": [
{
"time": 49,
"text": "1661309439313203200",
"color": 16777215,
"player": null,
"type": 0,
"author": "YU4324234234"
},
{
"time": 7,
"text": "1661309439313203201",
"color": 16777215,
"player": null,
"type": 0,
"author": "YU4324234234"
}
]
这里我们需要覆写他的danmaku.apiBackend
,那么这个时候外层的danmaku.api
就无所谓了反正我们的后端接口也不取这个数值,而且如果用这个还不好对axio
进行全局配置,这里给出用例
import {getVideoBarrage} from "@/api/barrage";
export default {
send: (endpoint, danmakuData) => {
//send 一样处理这里就不写了
console.log('假装通过 WebSocket 发送数据', danmakuData, endpoint.data);
},
read: (options) => {
getVideoBarrage(videoId).then((response) => {
const data = response.data;
if (!data || data.code !== 200) {
options.error && options.error(data && data.msg);
return;
}
options.success(data.data);
}).catch((e) => {
console.error(e);
options.error && options.error();
});
},
};
那么这样我们的基本视频组件就构建完成了
const dp = new DPlayer({
container: document.getElementById('videoPlayer'),
hotkey: true,
playbackSpeed: [0.5, 0.75, 1, 1.5, 2, 3],
video: {
url: 'https://admin.com/xxx/1.mp4',
type: 'auto',
},
danmaku: {
id: currentPlayVideoData.value.id,
api: 'https://www.baidu.com'
},
apiBackend: defaultApiBackend,
});
当然不是拉,现在会遇到最大的坑,这么大的问题9个月没有被解决也是厉害,我看了一下提交记录,有问题的来源于这一条提交,增加了动画效果速度参数
我不是很清楚animation-name
的引用加单引号是否是某个版本可行的写法,但是起码他不是一个兼容的写法,这种写法会导致播放器弹幕没有动画功能,看起来像是没有弹幕一样,但其实这是由于弹幕动画没有播放导致弹幕直接从左边闪现到了右边,解决方法也很简单,覆写样式去掉引号即可。但是这个问题很容易让你以为是弹幕本身的原因,尤其是在之前踩了那么多弹幕的坑情况下
这个有问题的提交MR
到现在已经差不多两年半了(两年半?警觉)然后九个月前合并的,这个问题反馈的issue也是有的,但是作者没有回应,所以我说目前项目现在基本不是一个比较活跃的状态,随时可能去世,大家有时间还是按前文方式构建弹幕视频播放器,但其实我浪费的时间也够这么构建就是了
优化
删除右击菜单的三个默认栏目
最简单的方法,如果你不使用菜单栏,直接覆写样式
.dplayer-menu {
height: 0 !important;
width: 0 !important;
}
如果你也想用菜单栏就稍微有点麻烦了,当然方法有很多,我们这里选择直接覆写源码使用patch-package
,位置在contextmenu.concat
,删除他默认的栏目,然后再用文档方式添加自己的栏目即可
删除默认的日志打印
同样,都已经覆写源码了,也直接把控制台打印去掉就完事了,位置在源文件的最后
最后
这个项目的使用还是建议下载源码,然后二次开发成自己想要的样子然后再推到包管理器然后引用,因为他可以自定义的部分太少了,但是代码本身写的还不错,二次开发相对比较轻松