转载

HTML5弹幕视频播放器

前言

大家年前好,马上就要元旦了,在很久没有写文章之后,想到这篇文章将会成为本人今年的

绝响也是有点蛋疼。不过也好,毕竟本人算不得什么勤快的生物,而且比起那些大神来说也差远了,就作为自己工作半年后的一次沉淀算了。

文中有些思想可能不见得是最好的解决办法,大神们请轻喷。

这个项目本来是我自己准备的一个开源插件,但由于上班写这个的过程中被老板发现了(∑(O_O;)),就说把这个东西写成公司用的吧,然后,然后开源就GG了(我了个大擦,我说我这个是自己的项目也没用),虽然说也可以偷偷放出来,但毕竟我还在职,就原谅我这次没办法把代码放出来吧。我会在讲解过程中尽可能讲解详细的。以上。。。。开始一波交♂易吧,骚年们(手动滑稽)

demo : 我是你亲爱的demo (弹幕数2000)

准备

至少需要有canvas和js的编程经验,然后养成一个好的代码习惯(不然一段时间后你都不知道你自己干了什么),下面讲解的内容不会涉及到具体程序和功能的编写,更多地是在强调性能优化这方面,完全当教程看的可以略过了。。。大神请随意

一. 选取合适的方案

其实实现弹幕的方式可以有多种,比如说用dom和canvas实现都是可以的,但我们这里选取的是canvas来绘制弹幕。

考虑到dom 只适合在单位时间内绘制少量弹幕 ,这对于我们播放器来说明显是不合需求的。其实去过B站的都知道,因为播放器有时会出现短时间内的“弹幕轰炸”,你想想如果是用dom来写,页面瞬间创建几百个、甚至上千个element。。。那酸爽,简直让人不敢相信。。。所以说,dom只适用于那些对时间把握要求不那么高的项目,因为你可以通过控制单位时间内弹幕的绘制数量来达到缓解性能的目的。

使用canvas好处有几点:
    1.不用频繁的操控dom元素
    2.有强大的api,操作简单
    3.在移动设备上也能比较高性能地运行
    4.强化你的代码组织能力(如何更好地分离功能什么的,而不是加个css3动画就了事)

二. 尽可能地多考虑移动设备和PC的差异

其实最简单的就体现在api上,比如说实现全屏模式,其实android和ios到目前为止都还是不支持元素全屏的,所以说你这里要考虑 requestFullScreen 不能用的情况下,对移动设备的降级处理。还有一点就是移动设备和PC的性能差距还是有点大的,这就需要你考虑在某些消耗性能的功能和用户体验之间做些取舍。

三. 在特效和性能之间做出平衡

其实这里也是考虑各个主流浏览器的性能差异,对某些功能做出适当的调整。其实在使用canvas text API的时候,有些效果的使用是会大大地加重浏览器的负担,比如说:文字阴影、渐变、变换等;不见得每个浏览器都能很迅速的绘制出你想要的效果,即使在你自己看起来不怎么需要时间。 这里,chrome内核的浏览器表现极其优秀,反而让我一脸懵逼的是火狐。。在这我整理了一些主流浏览器在绘制方面的差异(参考数据):

upperNum : {
        //当弹幕数超过一定数额时,取消文字的全局特效(如阴影等)
        //ps:火狐爸爸不给力啊,向谷歌势力低头
        "firefox" : 500,
        "safari" : 800,
        "chrome" : 2000,
        "ie" : 900,
        "edge" : 1100
    },
    scale : 1,
    rotate : 0

从上面可以看出,其实浏览器性能之间的差异还是有的,此时就需要我们根据数据的多少来调整特效的展现,你可以分别用火狐和chrome打开demo页面看效果

四. 减少api的操作次数(主要部分)

如全局文字颜色等一些能在循环体外设置的api,就千万不要放到循环体内部去给每个弹幕单独设置(除非全局样式发生了改变)。

还有就是优化循环,没错,就是优化循环。。。。。当你的弹幕池达到数千甚至上万的时候,你一个循环就能浪费掉许多时间,其实有些条件达到的时候你是没必要再进行循环了的。对于视频弹幕来说,我们需要的只是 “把某一个时间点的所有弹幕进行一次绘制”,这样我们就已经确立了一部分优化原则:在当前播放时间之前的弹幕我不需要管,在当前播放时间之后的弹幕我不需要管。至于怎么实现,我们可以改变for循环的start下标来达到忽略前面的弹幕,可以通过判断当前弹幕的时间和播放器时间的大小来达到跳出循环的目的。 在下面的循环体代码(部分)就可以看出来优化的操作

if(!this.Canvas.globalStyleHasChanged)
Canvas.setBaseTextStyle(cxt);    //设置文字基本样式,我们在这里设置好统一样式

//我这里是弹幕循环
for( var i = DMsystem.idx, DM; DM = DMs[i++]; ){
    if( DM.currentTime > currentTime ){
        break; 
    }
    if( DM.currentTime < currentTime && !DM.isDisplaying ){
        DM.hasShowed = true;
        continue;
    }
    DMsystem.refresh(DM);             //更新位置
    Canvas.drawDM(cxt,DM,options);     //绘制
    DMsystem.recovery(DM,currentTime);          //判断弹幕是否显示完毕并回收相关行
}

五. 避免某些误区

其实在这一版之前我还写过1.0版,不过在移动设备上的性能太糟糕所以舍弃了(你们可以用手机打开这一版demo看看效果,性能其实还是可以的),原因是我把每一条弹幕都new了一个实例。。。当时忙着js的伪面向对象编程实践:“弹幕嘛,每一条弹幕都应该是一个成熟的个体,不仅有自己独特的颜色,还有所在的位置.....”。然后呢?当我把弹幕数调整为5000的时候---然后就没有然后了。。。。too young too simple,sometimes naive.....浏览器直接炸掉了。。。。。所以说我们在面向对象的时候要考虑哪些才是真正应该new的。。(尽管看起来好像就我自己有点蠢....)

以上就是要写的所有的东西了,尽管我知道各位看起来可能有点不那么方便(毕竟代码少,而且讲得很笼统),当你自己开始写的时候就会明白项目中大部分东西都很简单(各种api的调用),真正麻烦的还是性能的优化,以及对设备和浏览器差异的处理。。如果不懂的话欢迎留言询问,我这个还不是最终版,接下来还要加入模式的切换。。。比如说 直播播放器模式(live)和视频播放器模式(player)的优化细节又有点不一样,还有高级弹幕的处理(图片,图形等)。。后续有空闲时间可能会继续更新,最后再说一次------------提前祝大家新年快乐

原文  https://segmentfault.com/a/1190000007951317
正文到此结束
Loading...