那些H5用到的技术(4)——弹幕
前言
以前玩卷轴射击游戏的时候,大量的BOSS子弹让我们无路可逃的时候,让我见识到了真正弹幕的威力,可自从A站B站火了之后,大量评论留言参与到了视频的播放中,也让我见识到了“弹幕”的威力,压根视频就没法看了……全看评论去了,就是那么好玩。
现在没有弹幕功能都不好意思说是做视频or直播网站的。而我们也不能落后呐,产品提需求了,活动H5里面弄个弹幕留言,看起来就高大上有木有啊,以前的静态留言形势都太古板啦,弹幕才能用户high起来啊!好吧,本来说这玩意已经比较成熟了,找轮子,结果发现貌似没有html&js版本的,索性就自己写一个吧,暂时满足简单的需求就行。
思路
如下图所示
主要有3个步骤
1、生成弹幕
根据相应的参数,设置弹幕的字体大小,字体颜色,甚至是弹幕背景(VIP功能,类似QQ聊天气泡),总之可以根据需求设计各种不同的弹幕样式。
2、展示弹幕
生成好弹幕我们就要对其进行展示了,目前主流的展示方式有2种,一种是从屏幕的右往左漂移展示,一种是在屏幕的上、中、下进行展示,这里需要考虑到尽量避免弹幕重叠,有效率用屏幕空间,当实在是没有空间了,才考虑重叠。

3、清除弹幕
在设定时间内显示完弹幕,为了节约资源,所以要对其进行移除清理,或者对其进行复用。
实现
话不多说,上代码看注释
/*** by Leestar54* 简易弹幕插件 V1.0*/;(function($) {$.fn.danmu = function(options) {//默认参数var defaults = {fontSize: 16,color: 'black',showTime: 10000}//使用jQuery.extend 覆盖插件默认参数var options = $.extend(defaults, options);var hideTime = defaults.showTime + 500;//弹幕的行数var dm_lines = Math.floor($(this).height() / 16);//当前弹幕行是否有空间进行显示var dm_line_empty = Array(dm_lines);for (var i = 0; i < dm_line_empty.length; i++) {dm_line_empty[i] = true;}//中文和英文的宽度要分别算function getDivWidth(txt, fontSize) {var len = 0;var charRatio = fontSize * 5 / 8;for (var i = 0; i < txt.length; i++) {if (txt[i].match(/[^\x00-\xff]/ig) != null) //全角中文len += fontSize;elselen += charRatio; //非中文经过测试得出比例}return len;}//公开函数this.add = function(txt, id, line, color, fontSize) {if (txt === undefined || txt === '') {//抛出异常throw 'txt should not be null';}if (line === undefined || line === '') {for (var i = 0; i < dm_line_empty.length; i++) {if (dm_line_empty[i]) {dm_line_empty[i] = false;line = i;break;}}//如果都满了,就随机覆盖了if (line === undefined || line === '') {line = Math.floor(Math.random() * dm_lines);}}if (color === undefined || color === '') {color = defaults.color;}if (fontSize === undefined || fontSize === '') {fontSize = defaults.fontSize;}if (id === undefined || id === '') {id = Math.floor(Math.random() * 100000000);}//使用dom构造单个弹幕divvar div = document.createElement("div");div.id = 'txt_dm' + id;div.innerHTML = txt;div.style.webkitAnimation = 'run 1s 8s linear forwards';div.style.position = 'absolute';div.style.top = line * fontSize + 'px';div.style.left = $(this).width() + 'px';//设置宽度,否则字体会自适应换行,影响显示div.style.width = getDivWidth(txt, fontSize) + 'px';div.style.fontSize = fontSize + 'px';div.style.color = color;$(this).append(div);$(div).velocity({translateX: '-' + ($(this).width() + $(div).width()) + 'px'},defaults.showTime,'linear');//屏幕完全显示完弹幕,就可以添加下一条了, +1为了用一定缓冲距离var fullShowTime = Math.floor($(div).width() / $(this).width() * defaults.showTime);setTimeout(function() {dm_line_empty[line] = true;}, fullShowTime);//删除显示完的弹幕setTimeout(function() {$('#txt_dm' + id).remove();}, hideTime);}return this;}})(jQuery);
需要注意的是
- 中文与英文混合的情况下,宽度计算需要注意,经过测试,得出5/8的比例刚刚好,否则不是太长,就是不够导致换行。
- 使用
var dm_line_empty = Array(dm_lines);来对每行的弹幕能否显示做判断,添加弹幕的时候,哪行空闲就忘哪行添加数据,提高屏幕的利用率。 - 弹幕移动使用的是velocity.js,否则效率太低,在移动设备上卡的飞起。
- 并没有实现屏幕上中下的弹幕模式……
使用起来非常的简单,样式自行设置即可
var dm = $('.container').danmu();dm.add('123');dm.add('双击66666666666666666');dm.add('老铁没毛病!')
模式
插件是有了,但是应用到业务上还需要我们自己做空置,一般来说有2个模式,供不同场景使用
这里设计弹幕的基础数据库格式为,至于扩展和业务字段自行添加。
id:自增
txt:用户输入的弹幕
time:用户提交时的总秒数,进入页面就开始从0每秒自增,提交时就是当时的总数,如果是视频,就是当时的播放时间。
type:弹幕模式,可以包括从左往右,从右往左,屏幕上方,中间,下方等等。由于只实现了一种,这个字段我们这里去掉。
可以考虑time,type作为复合索引。
无限循环模式
这应该是大多情况使用的模式,因为H5活动不像看视频,不可能按照发布时间来展示弹幕,因为也许用户发布一条评论的时间有20秒,那么岂不是前20秒整个弹幕都空空的?所以我们首要目的就是呈现那种热闹劲,一次性的读取全部弹幕,如果数量过多,可以做一些取舍,循环播放,每隔一定时间,重复显示弹幕。
//--------------------------------重复展示弹幕----------------------------------------//重复展示时间间隔,如果弹幕少,就设置小一些,这样重复的多。var recycle_time = 10;var load_recycle_interval;var recycle_load_timeout;//重复展示弹幕function dmRecycleStart(data) {for (var i = 0; i < data.length; i++) {var id = data[i]['id'];var txt = data[i]['txt'];//闭包&立即执行传递参数,返回新的方法,否则add方法会被立即执行recycle_load_timeout[i] = setTimeout(function(txt, id) {return function() {dm.add(txt, id);};}(txt, id), 1000 * Math.floor(Math.random() * recycle_time) + 1); //随机时间点显示文字}}recycle_load_timeout = Array(data.length);//先显示一次dmRecycleStart(data);//然后每隔一段时间都展示一次load_recycle_interval = setInterval(function() {dmRecycleStart(data);}, recycle_time * 1000);
时间线模式
这种模式就是和视频弹幕一样的模式了,按照用户发送弹幕的时间进行播放, 我们需要设置个定时器,每一秒显示一次该时间点的弹幕,每隔一段时间读取时间段内的所有弹幕。
我这里设计时,为了方便直接调用,服务器做了些格式转化,预先把消息按照时间点进行分组了,php参考代码如下:
public function getdm(){$start_time = I('get.start_time');$end_time = I('get.end_time');$mode = I('get.type');$db_result = M('dm')->query("select id,txt,ptime from dm where and ptime between %d and %d" and type=%d, $start_time, $end_time, $type);$ret = array();//根据时间进行分组foreach ($db_result as $key => $value) {$ret[$value['ptime']][] = array($value['id'], $value['txt']);}$this->ajaxReturn($ret);}
JS代码参考如下:
//---------------------------------时间线弹幕-----------------------------------------var dm_get_count = 0;//用户提交弹幕的时候,时间参数可以以此为基准。var dm_time = 0;var load_interval;var load_timeout;//加载弹幕function dmLoadTimeLine() {//每秒钟执行一次,更新时间,这里设置为每30秒为间隔显示一次弹幕(16秒开始预加载),根据时间点投放load_interval = setInterval(function() {if (dm_time % 16 == 0) {var start_time = dm_get_count * 30;var end_time = (dm_get_count + 1) * 30;//中间预加载// $.get('./getdm', {// start_time: start_time,// end_time: end_time,// type: typeid,// }, function(data) {dm_pre = line_data;console.log(start_time + '-' + end_time + '预加载完成');//第一次加载完成直接显示if (dm_get_count == 0) {dmTimeLineStart();}dm_get_count++;// });} else if (dm_time % 30 == 0) {dmTimeLineStart();}dm_time++;}, 1000);//每秒1个弹幕,30秒则有30个load_timeout = Array(30);}//时间线弹幕function dmTimeLineStart() {var start_time = dm_get_count * 30;var end_time = (dm_get_count + 1) * 30;console.log(start_time + '-' + end_time + '显示弹幕');//深度拷贝数组,如果是从接口获取数据,最好拷贝一次。var dm_current = $.extend(true, {}, dm_pre);//时间点内显示弹幕for (var i = start_time; i < end_time; i++) {if (dm_current[i] != null) {for (var j = 0; j < dm_current[i].length; j++) {var id = dm_current[i][j]['id'];var txt = dm_current[i][j]['txt'];//内部函数立即执行,闭包传递参数,load_timeout[i] = setTimeout(function(txt, id) {return function() {dm.add(txt, id)};}(txt, id), 1000 * i + 1); //指定时间点显示文字}}}}
停止显示弹幕
当用户关闭弹幕时,可以用下列代码,也说明了上面代码中一些参数的用处
function dmStop() {clearInterval(load_interval);clearInterval(load_recycle_interval);dm_time = 0;dm_get_count = 0;dm_pre = Array();if (load_timeout != undefined) {for (var i = 0; i < load_timeout.length; i++) {clearTimeout(load_timeout[i]);}}if (recycle_load_timeout != undefined) {for (var i = 0; i < recycle_load_timeout.length; i++) {clearTimeout(recycle_load_timeout[i]);}}}
最终demo效果如下:

demo地址:
https://github.com/leestar54/h5-demo/blob/master/danmu.html
那些H5用到的技术(4)——弹幕的更多相关文章
- H5的本地存储技术及其与Cookie的比较
第一部分: H5的本地存储技术 HTML5 提供了两种在客户端存储数据的新方法.先看下面的例子: 例1:var mySelection = {name:"car", amount: ...
- 那些H5用到的技术(1)——素材加载
编码环境前言什么时候用到素材加载?loading提示,让用户等待图片的加载音频的加载利用神器PreloadJS总结 编码环境 Sublime Text 3 插件包括: Autoprefixer 自动补 ...
- H5 视频直播相关技术
一.移动视频直播发展 大家首先来看下面这张图: 可以看到,直播从 PC 到一直发展到移动端,越来越多的直播类 App 上线,同时移动直播进入了前所未有的爆发阶段,但是对于大多数移动直播来说,还是要以 ...
- H5传奇世界服务器架设技术及源码
以前是传奇迷,虽然现在不玩,但当作兴趣,研究了一下H5传奇世界的架设,架设成功并分享给大家.注意,此技术只可用于个人娱乐,不可用于商业用途. 首先下载 传奇世界H5源码 450M的样子. H5传奇 ...
- 那些H5用到的技术(6)——屏幕适配
前言长屏适配单页适配参考 前言 曾经屏幕适配一直是个头疼的问题,各种坑,各种浏览器&设备兼容问题,好在的是,随着技术&标准的不断发展,这个问题得到了极大程度的解决,这篇文章主要对之前开 ...
- 那些H5用到的技术(2)——音频和视频播放
前言audio标签Web Audio API自动播放的问题背景音乐的实现立即播放的问题SoundJSvideo标签播放样式的问题格式的问题总结 前言 正常情况,除了非常简陋的小功能H5,音乐播放是必不 ...
- 手机QQ会员H5加速方案——sonic技术内幕
版权声明:本文由况鹰原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/141 来源:腾云阁 https://www.qclou ...
- 那些H5用到的技术(6)——数字滚动特效
前言原理源码使用方式补充CountUp.js 前言 会有这么一种情况,H5页面需要进行数字统计展示,以此来强调产品or工作的成果.如果只是静态显示一个数字,总是感觉生硬.对比如下: 是不是瞬间高大上了 ...
- 将海康大华等网络摄像机RTSP流进行网页Flash rtmp和H5 hls直播的技术方案
前言 再小的技术点也会有他的市场! 一直以来,都有一些不被看好,认为是成本太高,无法大规模展开的软件和产品形态,就好比每一座城市都会有他的著名小吃一样,即使是慕名而来的人源源不断,受众群体也总是有限, ...
随机推荐
- 一些..C#知识点总结
C# 知识点汇总 (其实C#与Java多少有区别,对于咱这个幼儿园大班生来说) 1.认识C#程序 (1)namespqce关键字 namespqce(命名空间)是C#组织代码的方式,它的作用类似于Ja ...
- java上转型之instanceof--避免引用类型强制转化出错
Object obj="hello"; 上面的obj是什么类型? object?NO!String?NO? 答案:编译阶段是Object类型,而在运行阶段是String类型.实际上 ...
- HDU 5119 Happy Matt Friends(DP || 高斯消元)
题目链接 题意 : 给你n个数,让你从中挑K个数(K<=n)使得这k个数异或的和小于m,问你有多少种异或方式满足这个条件. 思路 : 正解据说是高斯消元.这里用DP做的,类似于背包,枚举的是异或 ...
- Spring 事务不回滚
为了打印清楚日志,很多方法我都加tyr catch,在catch中打印日志.但是这边情况来了,当这个方法异常时候 日志是打印了,但是加的事务却没有回滚. 例: 类似这样的方法不会回滚 (一个方 ...
- Promise实现简易AMD加载器
在最新的Chrome和FF中已经 实现了Promise.有了Promise我们用数行代码即可实现一个简易AMD模式的加载器 var registry = { promises: { }, resolv ...
- silverlight PopupWindow Resizeable兼容问题
下方第一段代码,在ie11中Resizeable无法生效,而在chrome中运行正常. HtmlPopupWindowOptions options = new HtmlPopupWindowOpti ...
- ASP.NET程序从IIS6移植到IIS7时出现500.22错误(转)
最可能的原因: • 此应用程序在 system.web/httpModules 节中定义配置. 可尝试的操作: • 将配置迁移到 system.webServer/modules 节.也可 ...
- 阿里云PolarDB及其共享存储PolarFS技术实现分析(上)
PolarDB是阿里云基于MySQL推出的云原生数据库(Cloud Native Database)产品,通过将数据库中计算和存储分离,多个计算节点访问同一份存储数据的方式来解决目前MySQL数据库存 ...
- leetcode 33 Search in Rotated Sorted Array JAVA
题目: 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 搜索一个给定的目标值,如果数组中存在这个 ...
- 10分钟教你用VS2017将代码上传到GitHub
前言 关于微软的Visual Studio系列,真可谓是宇宙最强IDE了.不过,像小编这样的菜鸟级别也用不到几个功能.今天给大家介绍一个比较实用的功能吧,把Visual Studio 2017里面写好 ...