bilibili弹幕类型网站的HTML5完美实现

我们今天用html5来实现国内知名弹幕网站bilibili的功能, 首先要后段配合前端, 然后一切都会变得很简单
下面先说说弹幕文件的分析



言归正传,播放器的弹幕列表是一个XML格式文件,它的地址是:
http://www.bilibili.tv/视频的cid.xml

关于cid的获取可以使用bilibili弹幕网API, wiki.bilibili.tv 我这里就不说了

在IE中输入这个文件,我们可以看到:
<?xml version="1.0" encoding="UTF-8" ?>

<d p="0,1,25,16777215,1312863760,0,eff85771,42759017">前排占位置</d>


<d p="16.6,1,25,16777215,1312863769,0,0edc8d09,42759052">站位..</d>


<d p="0,1,25,16777215,1312863770,0,658291b6,42759064">诶 蘑菇汤出了~</d>

内容部分
<d p="0,1,25,16777215,1312863760,0,eff85771,42759017">前排占位置</d>

这行内容的意义呢
先说内容“前排站位置”就不解释了
p这个字段里面的内容:
0,1,25,16777215,1312863760,0,eff85771,42759017
中几个逗号分割的数据
第一个参数是弹幕出现的时间 以秒数为单位。
第二个参数是弹幕的模式1..3 滚动弹幕 4底端弹幕 5顶端弹幕 6.逆向弹幕 7精准定位 8高级弹幕
第三个参数是字号, 12非常小,16特小,18小,25中,36大,45很大,64特别大
第四个参数是字体的颜色 以HTML颜色的十位数为准
第五个参数是Unix格式的时间戳。基准时间为 1970-1-1 08:00:00
第六个参数是弹幕池 0普通池 1字幕池 2特殊池 【目前特殊池为高级弹幕专用】
第七个参数是发送者的ID,用于“屏蔽此弹幕的发送者”功能
第八个参数是弹幕在弹幕数据库中rowID 用于“历史弹幕”功能。

比如
<d p="166.1,1,25,16777215,1312864104,0,ce083fe2,42760591">安全帽....安全X也不见得就一定安全啊</d>

一句代表着:
在整个视频166.1秒的时候,上部出现普通滚动弹幕,字号为12,颜色为16777215(10进制,16进制为&HFFFFFF,即白色),弹幕的发布日期为1312864104(UNIX时间戳,为1970年1月1日8时到弹幕发布时间之间的秒数差),弹幕发出者的ID为ce083fe2,内容为:“安全帽。。。。。”


然后我们就可以开始用php把弹幕抓下来分析了

PHP代码 
  1. <?php
  2. error_reporting(0);
  3. $cid = $_GET['c'];
  4. if (!is_numeric($cid))
  5. {
  6.     die('not vaild cid');
  7. }
  8. header('Content-Type: text/javascript; charset=utf-8');
  9. //$danmaku = file_get_contents('d.xml');
  10. $url = 'http://comment.bilibili.tv/'.$cid.'.xml';
  11. $header = array(
  12.     "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36",
  13.     "Cookie: sid=4d0833b9; pgv_pvi=3868490752; DedeUserID=269563; DedeUserID__ckMd5=49d806d3343627e2; SESSDATA=2f6fa663%2C1396281758%2Cf3077cb7; _cnt_dyn=0; _cnt_pm=0; _cnt_notify=5; __utma=107677085.885893332.1388156294.1394854794.1394860318.91; __utmz=107677085.1394800027.86.20.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided)",
  14. );
  15. $ch = curl_init();
  16. curl_setopt($ch, CURLOPT_URL, $url);
  17. curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
  18. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  19. curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  20. $danmaku = curl_exec($ch);
  21. //die($danmaku);
  22. preg_match_all('/<d.*?p="(.*)">(.*)?<\/d>/'$danmaku$raw);
  23. //print_r($raw);
  24. //exit;
  25. foreach ($raw[1] as $value)
  26. {
  27.     $foo = explode(','$value);
  28.     $time[] = round($foo[0] * 1000) / 1000;
  29.     unset($foo[0]);
  30.     $param['s'][] = array_values($foo);
  31. }
  32. $param['d'] = $raw[2];
  33. //print_r($time);
  34. function danmakusort($a$b)
  35. {
  36.   if ($a == $breturn 0;
  37.   return ($a > $b) ? 1 : -1;
  38. }
  39. usort($time'danmakusort');
  40. //print_r($time);
  41. //print_r($param);
  42. foreach ($time as $key => $value)
  43. {
  44.     $dmk['t'][] = $value;
  45.     $dmk['s'][] = $param['s'][$key];
  46.     $dmk['d'][] = $param['d'][$key];
  47. }
  48. //print_r($dmk);
  49. if (isset($_GET['debug']))
  50. {
  51.     foreach ($dmk['t'as $key => $value)
  52.     {
  53.       $s = $dmk['s'][$key];
  54.       $s = array_reverse($s);
  55.       $s[] = $value;
  56.       $s = array_reverse($s);
  57.       $s = implode(','$s);
  58.       echo '<d p="'.$s.'">'.$dmk['d'][$key].'</d>'."\n";
  59.     }
  60. }
  61. else
  62. {
  63.     $res = json_encode($dmk);
  64.     if ($res == null or $res == 'null')
  65.     {
  66.       unset($res);
  67.       $res['error'] = 'could not fetch';
  68.       $res = json_encode($res);
  69.     }
  70.     die('callback('.$res.')');
  71. }

思路如下
1.先用php抓bilibili的xml弹幕, 这里用curl实现, 记得替换成你自己的cookie
2.按时间排序每一条弹幕
3.用正则解析xml, 并且输出json格式的弹幕方便前端解析


然后为了方便使用, 我的前端就转变为json, json弹幕格式示范可以看这里
http://12dora.sinaapp.com/pre/n.php?c=1456367&callback=callback&_=1395639194178
t属性里面储存所有弹幕的时间
d就是文字
s就是各种属性, 颜色字体什么的

然后就是前端解析部分, 主要分两大部分, 这里把后端库贴出来,代码很简单, 我这里大概说个思路
后端js库: http://12dora.sinaapp.com/static/js/danmaku.js
前端js库(偷懒直接写主页) h
ttp://12dora.sinaapp.com/index.html

 

1. 后端init()函数, 负责把json弹幕格式解析为元素存入全局变量
2.绑定setInterval事件, 10毫秒抓一次video tag的播放时长, 然后读入弹幕数组的第一条, 如果时间差在20毫秒内则交由gennerate_danmaku()生成弹幕并且显示
3.移动指针到下一条(这里不用遍历, 因为服务器已经按时间排序了, 所以如果当前指针的弹幕时间还没到后面的肯定时间也没到)
说一下gennerate_danmaku()函数的功能:
1.读取当前弹幕画布哪一行是空的, 指派弹幕位置
2.读取设置并用css描述设置(例如字体颜色...), 并且生成一个css3动画绑定到本条弹幕上

然后说一下前端, 前端用了一个video标签读入视频, 然后用一个一模一样大小的div用position absolute重叠在上面, 作为弹幕显示的虚拟画布, 方便控制, 这个相信各位都能轻松做出来.

然后就是弹幕动画部分, 这里我采用了css3的动画属性, 有的人建议我用canvas这样子性能会高, 不过canvas的话功能十分有限, 例如你想把当前评论的文字复制下来的话, canvas是完全无法做到的, 所以我这里采用原始的div, 每一条弹幕指派一个div, 并且赋予一个独一无二的id方便css3动画绑定元素, 而css3动画的话, 需要特别注意的是如果用from left:0px to left:100px 这样的语法整个页面弹幕一多就会崩溃, 所以这里选择用css3的translate属性, 因为这个会有硬件加速.

JavaScript代码 
  1. var classNameGen = function(charsLength,chars) {
  2. var length = charsLength;
  3. if (!chars)
  4. var chars = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ1234567890";
  5. var randomChars = "";
  6. for(x=0; x<length; x++) {
  7. var i = Math.floor(Math.random() * chars.length);
  8. randomChars += chars.charAt(i);
  9. }
  10. return 'h' + randomChars;
  11. };

上面的就是生成唯一id的函数, 然后绑定一个onanimationend事件, 让动画播放完毕就直接回收div.
**提示, css3的class名字不能以数字开头, 这个必须要注意, 不然那个class就会失效

然后贴一个单条弹幕的css例子

JavaScript代码 
  1. .hPMBMafziBSEheRYi {
  2.     text-shadow: 1px 1px 1px #000000;
  3.     left: 1109px;
  4.     top: 0px;
  5.     color: #FFFFFF;
  6.     -webkit-animation: hPMBMafziBSEheRYi linear 5s;
  7.     -webkit-animation-iteration-count: 1;
  8.     -webkit-transform-origin: 50% 0%;
  9.     -webkit-backface-visibility: hidden;
  10.     -webkit-perspective: 1000;
  11. }
  12. @-webkit-keyframes hPMBMafziBSEheRYi {
  13.     from {
  14.       -webkit-transform: translate3d(1px, 0, 0);
  15.     }
  16.     to {
  17.       -webkit-transform: translate3d(-1194px, 0, 0);
  18.     }
  19. }

text-shadow是为了加一个阴影以免弹幕与背景一体色, 然后另外的重点就是-webkit-transform, 这样子写会比left定位效率高得多, 还有动画规则的话, 这里用了线性5秒动画, 有需求可以自己改, 5秒算是比较接近官方速度的了

这个技术其实不只是可以令html5的设备可以播放弹幕, 而且只要网站允许的话, 其实每一个网站都可以为自己的视频加上一层弹幕, 增加访客看视频的互动性.

 

***还有补充一点, 上面没提到的, 就是因为我们需要为每一条弹幕的css style绑定一个动画, 这个是不能直接用style=""来解决的, 所以必须生成一个<style></style>去储存, 然后现在js没有原生函数可以去删除一个class, 所以我这里把每一条弹幕都生成了一个<style>标签, 动画完成之后直接回收对应的弹幕<div>还有<style>标签, 免去了正则匹配删除class的麻烦事

最后发一个demo
http://12dora.sinaapp.com/
希望以后css的动画功能更加强大, 可以把繁杂的flash抛弃还希望可以把弹幕这种文化普及, 因为这种弹幕形式的评论交流实在是太赞了!

 

http://cloudbbs.org/forum.php?mod=viewthread&tid=21977

猜你喜欢

转载自qianxunniao.iteye.com/blog/2123384