分享人:herry
弹幕篇:弹幕文化与HTML5
说说弹幕
弹幕文化
1什么是弹幕?
弹(dàn)幕(mù)在国内兴起已经有个把年了,相信很多朋友都差不多知道弹幕这个东西。
弹幕系统最初的起源是一家日本的网站,名叫“NICONICO”,被国内人们称为N站,是一家以动漫ACG为主的视频网站,。所谓弹幕,实际上就是观众对当前视频的评论吐槽。只是这句有一定的针对性了。在视频播放到某个时间段时用户对该时刻画面的评论。
国内首家弹幕视频网站是ACFUN,大家称之为A站。之后有出现了一个名为BILIBILI的弹幕站,大家称值为B站。
如下图展示:
(图:niconico)
(图:bilibili)
2弹幕文化
弹幕这种东西能给人一种能实时互动的错觉,虽然大家发送的时间有所不同,但是看的视频与吐槽的同步就会让人觉得并不止你一个人在看同一个视频。
就比如,如果在一个弹幕网站看一个恐怖视频,你还会觉得恐怖吗?
弹幕系统不断扩散,现如今,很多国内主流的一些视频网站也都相继有了弹幕系统功能,音乐网站、甚至漫画网站以及小说网站都有了弹幕,还有论坛贴吧有了弹幕(后面这些实际上大多是评论弹幕化,并没有使用时间轴的弹幕系统,为随机时间轴出现的弹幕)。
html5弹幕
说说html5能否实现效果
答案是可以的。现在国内市场弹幕网站PC端采用的是flash弹幕播放器来播放的,对于播放器来讲flash是非常适合做这个的,而且已经做很久了,所以这方面flash相对html5是更有优势的,而且能播放的资源格式编码也更多。但是现今的情况是,人们大多数会更喜欢使用移动端看看视频啥的了。IT界的人大多都知道移动端并不适合使用flash,就算可以使用flash的安卓系统,但是这个很费电/费流量,每次打开网页都要加载一次flash不说,还很卡。苹果则是直接放弃flash,所以现在flash只用在了PC网页上了,移动端一般都是调用APP里面自带的弹幕播放器进行播放,也不是html5播放的。为啥大多数网站不写个html5的弹幕播放器呢?实际上也不是没写,也有些网站是写了的,B站的wap站点就是采用的html5弹幕播放器,但是很多情况下几部看不到弹幕,为什么呢?因为大多数手机浏览器都有他们自带的视频播放器的,当浏览器检测的你的网页中有video元素,便会立马替换掉你的播放器为浏览器自带的视频播放器来播放,你的那个写里弹幕滚动的浮层完全就被浏览器的播放器给覆盖了(前端代码再牛逼也是牛不过浏览器的)。除非你的浏览器并不对video元素做处理,那就不会有什么问题了。
(图:bilibiliwap版)
弹幕的一般实现原理:
获取到需要展示的弹幕列表,放在一个列表中,每个弹幕对象都存在弹幕文本、弹幕出现时间、弹幕颜色、弹幕大小等属性。
当前音视频播放到该条弹幕的弹幕出现时间点时,该弹幕已某个形式展示出来,一般以从右到左的滚动为例,超出屏幕后隐藏。
这个逻辑html5是完全可以实现的。
好了,现在就不说这些了,更多弹幕相关的文章可以上百度看看。我们来看个h5实现的弹幕音频播放器吧,为什么要用音频呢,因为在微信播放video视频,video播放器就会被微信播放器替换掉,就看不到弹幕了。
3弹幕音频效果演示
4弹幕效果代码分列
HTML部分
<body onload="pollMsg(0)"> <div class="container"> <!--音频与弹幕播放层--> <div class="leftContent"> <div class="pool" id="pool"> <div class="abs_div"> <audio id="audio" width="100%" height="360" controls="controls" onplay="handle();" onpause="handle();"> <source src="xxx.mp3" type="audio/mp3"> </audio> </div> <!--预留弹幕层--> </div> </div> <!--弹幕列表--> <div class="list" style="display:none;"> <table border="0" id="list"> <tr style="background-color:#eee"> <th>时间</th><th>内容</th><th>发送日期</th> </tr> <tr> <td>00:00</td><td>测试弹幕</td><td>2012-01-01 00:00</td> </tr> </table> </div> </div> </body>
.container为全部内容div,.leftContent为弹幕和音频播放层,
预留弹幕层 span通过js装载弹幕从右到左滚动显示在此处,
.list为需要展示的弹幕列表,因为此处要在移动端显示,所以这个就隐藏掉吧。
CSS部分
body{text-align:center;margin:0;padding:0} .container{border-radius:5px;width:99%;height:365px;margin:5px auto;/*background-image:url(0.gif);*/background-size:100% 100%} .leftContent{width:100%;text-align:left;float:left} .sendBar{text-align:center;font-size:13px;text-shadow:1px 1px 1px gray} .sendBar input[type=text]{width:200px} .pool{border-radius:5px;width:99%;height:360px;margin:2px 2px 0 2px;position:relative;overflow:hidden} .list{border:1px solid gray;border-radius:5px;width:310px;height:382px;margin:2px 0 0 5px;float:left;white-space:nowrap;overflow-y:scroll} .list table{font-size:12px;padding:2px 2px 0 2px;width:100%;text-align:center} .comment{color:#000;font-size:20px;display:inline;position:absolute;white-space:nowrap;font-weight:700} .abs_div{position: absolute;bottom: 0;height: 40px;width: 100%;} #audio{margin: 0 auto;width: 100%;}
简单的说,就是如下图这种样子:黑色区域用于弹幕的滚动,下方是一个audio播放器
有了基本样式就可以让弹幕在上面跑了。要的效果是:点击audio的播放,就开始遍历弹幕,按时间点载入弹幕到黑幕中。
JS的执行逻辑
初始化
var commentList=[];//创建任意数量元素的评论列表数组 var track=new Array(14); for(var i=0;i<14;i++){ track[i]=0; } var docW=document.body.clientWidth;//取屏宽
/*取ajax对象*/ function getAJAX(){ if(window.XMLHttpRequest) return new XMLHttpRequest(); else if(window.ActiveXObject) return new ActiveXObject("Microsoft.XMLHTTP"); return null; }
/*将已秒计数的数字格式转为00:00的格式*/ function tick2str(tic){ var str=""; if(tic<60)str="00"; else if(tic<600)str="0"+parseInt(tic/60); else str=parseInt(tic/600)+""+parseInt(tic/60%10); str+=":"; tic%=60; str+=parseInt(tic/10)+""+parseInt(tic%10); return str; }
页面加载完毕执行pollMsg(0);
/*异步访问弹幕数据层页面,用于将弹幕对象写入隐藏的列表中*/ function pollMsg(from){ var ajax=getAJAX(); if(ajax==null){ return; } ajax.onreadystatechange=function(){//ajax相应 if(ajax.readyState==4 && ajax.status==200){ var resText=ajax.responseText;//取相应内容 var rec="(["+resText+"])";//json化 var objs=eval(rec); for(x in objs){ var node=document.createElement('tr'); node.innerHTML='<td>'+tick2str(objs[x].tick)+'</td><td>'+objs[x].text+'</td><td>'+objs[x].date+'</td>'; getE('list').appendChild(node); } commentList=commentList.concat(objs);//连接数组 } } var time=new Date().getTime(); ajax.open('GET','http://xx.com/dmdata.jsonx?from='+from+'&time='+time,true);//向数据页面get请求 http://xx.com/dmdata.jsonx是数据页面 ajax.send(null); }
audio标签上onplay="handle()" onpause="handle()";用于开关弹幕滚动计时器
var st=false;//true:开;false:关; var tmove,tcheck; //开关弹幕滚动计时器 function handle(){ if(st){ st=false; window.clearInterval(tmove);//停止滚动所有弹幕 window.clearInterval(tcheck);//停止检查弹幕列表 }else{ st=true; tmove=window.setInterval("moveAll()",50);//开启滚动所有弹幕 tcheck=window.setInterval("checkList()",1000);//开启检查弹幕列表 } }
播放时开启滚动后每50毫秒都执行moveAll()进行滚动所有弹幕和每秒执行checkList()检查弹幕列表,暂停或停止播放时停止2个计时器。
/*获取span 滚动所有弹幕 将span标签进行从右到左进行滚动*/ function moveAll(){ var sps=document.getElementsByTagName('span');//动态span for(var x=0;x<sps.length;x++){ var l=parseInt(sps[x].style.left); if(l>-sps[x].clientWidth){ var tr=parseInt(sps[x].style.top)/25; if(track[tr]==l+sps[x].clientWidth-docW) track[tr]-=8; sps[x].style.left=l-8+'px'; }else{ sps[x].parentNode.removeChild(sps[x]); } } }
//检查弹幕列表 function checkList(){ var ct=parseInt(getE('audio').currentTime);//当前所在时间 for(x in commentList){ if(commentList[x].tick==ct){ showComment(commentList[x]); } } }
//创建span show出弹幕 function showComment(c){ var node=document.createElement('span'); node.innerHTML=c.text; if(c.color)node.style.color=c.color; if(c.size)node.style.fontSize=c.size; node.style.left=document.body.clientWidth+"px"; var t=getTop(); node.style.top=t+"px"; node.className="comment"; getE('pool').appendChild(node); track[t/25]=node.clientWidth; }
/*取弹幕在滚动时的顶部距离*/ function getTop(){ var min=1000,id=0; for(x in track){ if(track[x]<=0){ return x*25; } if(track[x]<min){ min=track[x]; id=x; } } return id*25; }
5可行性延伸
咪咕阅读的客户端中某些书中存在视频导读这一区块,以及某些活动页中存在视频或音频这一栏。不过毕竟是阅读app,不是视频app,所以视频区块并不多。如果说在咪咕阅读中的视频区块加入开关弹幕效果。会是什么效果呢?此app当中的video并没有转换为系统自带的播放器的,所以用h5的没问题(好处:1.不用一下一下去翻评论区了,一次性知道用户的评价。坏处:数据量大将导致页面加载时间变长)
基本猜测:取用户评论区最新或最热的前100到200条评论(太多弹幕会导致页面加载变慢和视频都被弹幕盖了),评论颜色取设定随机色,评论出现时间为0到视频结束时间中的随机数。