效果图:
左侧视频画面上显示弹幕,右侧评论区显示各用户发表过的评论,自己发表的评论也可显示在弹幕上。
基本布局
HTML:
<div id="myvideo">
<!-- 视频区 -->
<div id="video_area">
<!-- 视频窗口 -->
<div id="video_window">
<!-- 视频 -->
<video id="video" src="media/海草舞.mp4" width="500" preload="metadata"></video>
<!-- 弹幕区,覆盖在视频窗口上 -->
<div id="barrage_area" class="barrage_area"></div>
</div>
<!-- 进度条 -->
<div class="myprogress"></div>
<!-- 播放控件 -->
<div id="mycontrols" class="mycontrols icon">
<!-- 播放按钮 -->
<div id="play" class="play">播放</div>
<!-- 停止按钮 -->
<div id="stop" class="stop">停止</div>
<!-- 弹幕开关 -->
<div id="on_off" class="barrage_icon">
<div>弹幕</div>
</div>
<!-- 时间 -->
<div class="time_area">
<span id="curTime"></span> / <span id="durTime"></span>
</div>
<!-- 屏宽 -->
<div class="range_area"></div>
<!-- 音量图标 -->
<div id="horn" class="horn"></div>
</div>
</div>
<!-- 评论区 -->
<div id="comment_area" class="comment_area">
<!-- 用户评论 -->
<div id="comment" class="comment">
<div id="comment_content"></div>
</div>
<!-- 发表评论 -->
<div>
<div><textarea id="mycomment" class="mycomment" rows="5" onfocus="select()"></textarea></div>
<div style="text-align: right"><input id="comment_submit" type="button" value="评论"></div>
</div>
</div>
</div>
主要CSS:
#myvideo{
display: flex;/*实例总体布局方式*/
}
/**** 视频区 ****/
#video_area{
width: 500px;
color: white;
background-color: #000000;
font-size: 20px;
padding: 40px 40px 20px 40px;/*黑边大小*/
flex: none;
}
#video_window{
position: relative;
}
/**** 播放控制面板 ****/
.mycontrols{
display: flex;
justify-content: space-between;
margin-top: 10px;
}
/** 弹幕 **/
@keyframes barrageAnimation{/*弹幕移动动画*/
0%{
right: 0;
transform: translate(100%, 0);
}
100%{
right: 100%;
transform: translate(0, 0);
}
}
.barrage_area{/*弹幕区*/
position: absolute;/*叠放在视频窗口上*/
top: 0;
left: 0;
width: 500px;/*初始大小*/
height: 250px;
overflow: hidden;
background-color: RGBA(250,10,10,0.3);/*此颜色在调试时使用,完成后要删掉*/
}
.barrage_area div{/*弹幕文字样式*/
position: absolute;
right: 100%;/*从右侧进入*/
font-size: 20px; /*弹幕文字大小*/
color: #fefcc9;
white-space: nowrap;
text-shadow: 0 0 2px #999;
animation: barrageAnimation ease-in 10s;
}
/**** 评论区 ****/
.comment_area{
width: 280px;
margin-left: 5px;
flex: none;
display: flex;
flex-direction: column;/*内部为垂直布局*/
}
.comment{/*用户评论*/
border: #66aaEE solid 1px;
font-size: 14px;
line-height: 20px;
padding: 5px;
overflow-y: auto;
flex: auto;/*高度可自动扩展*/
}
.comment div{
margin-top: 5px;/*评论间距*/
}
.comment span{
color: #cd4606;/*用户名颜色*/
}
.mycomment{/*发表评论文本框*/
width: 275px;
margin-top: 5px;
}
/****弹幕开关图标****/
/*用例:
<div class="barrage_icon">
<div>弹幕</div>
</div>
关闭状态需向class中添加off,即:class="barrage_icon off"
*/
.barrage_icon{/*圆角矩形*/
position: relative;
width: 40px;
height: 20px;
line-height: 20px;
font-size: 12px;
border-radius: 10px;
background-color: darkcyan;
padding: 0 5px;
cursor: default;
}
.barrage_icon::after{/*圆形按钮*/
content: "";
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 20px;
border-radius: 100%;
background-color: #aaaaaa;
background: radial-gradient(circle,#c0c0c0,#f1f1f1);
}
.barrage_icon.off{/*关闭状态*/
background-color: #999999;
text-align: right;
}
.barrage_icon.off::after{
right: auto;
left: 0;
}
效果图上的各种控件都是用CSS画的,量很大,这里只给出了与弹幕相关的。
弹幕数据
弹幕数据应来自于后台数据库,此处我们用一个JSON文件模拟从数据库中读取到的数据,结构如下:
barrage.json:
{
"data":[
{
"time": 3.5,
"username": "自强奋斗者协会",
"text": "又一神曲"
},
{
"time": 88.6,
"username": "请叫我天才369号",
"text": "现在才知道萧全是唱斗龙战士1主题曲的人"
},
{
"time": 5.1,
"username": "葫芦小紫",
"text": "海草海草海草海草。。。"
},
{
"time": 76.9,
"username": "NONGZHENG",
"text": "抛开成见,还是挺好听的"
},
{
"time": 8,
"username": "神兽驴蛋蛋儿",
"text": "继社会摇之后又一神曲,萧全有毒!!!"
},
{
"time": 20,
"username": "fanfan",
"text": "好听"
},
{
"time": 20,
"username": "小米",
"text": "嗨起来,哈哈哈。。。"
}
]
}
time是发布时间,是用户在观看视频时发布的,以秒数为单位,比如"time": 3.5表示用户在看到视频播到3.5秒时发布了此条评论。
username是用户名。
text是评论文本。
弹幕控制
几个要点:
- 弹幕区要覆盖在视频上,大小与视频相同(调试时加了个红色背景,调试完记得要删掉)
- 播放时用Ajax加载弹幕数据
- 监听视频的timeupdate事件,保证弹幕出现的时间与视频播放时间同步
- 弹幕出现的位置和移动的速度是随机的
- 关闭弹幕不能用隐藏,它会导致再打开时积累的弹幕一起涌出,所以用修改不透明度的方式隐藏弹幕
JS代码:(需使用jQuery)
/**** 弹幕控制 ****/
var barrage = {
data: [], //弹幕文本
area_id: "#barrage_area", //弹幕区id
area_width: 0, //弹幕区宽度
area_height: 0, //弹幕区高度
isLoad: false, //是否已加载弹幕数据
isOn: true, //是否开启弹幕
on_off_id: "#on_off", //弹幕开关id
curTime: 0, //当前时间
setBarrageArea: function (width,height) {//设置弹幕区大小
this.area_width = width;
this.area_height = height;
$(this.area_id).css({"width":width+"px","height":height+"px"});//设置弹幕区大小
},
getData: function () { //获取弹幕文本
var that = this;
$.ajax({
type: "GET",
url: "js/barrage.json",//弹幕数据地址
data: {},
dataType: "json",
success: function(res) {
that.data = res.data;
that.data.sort(function (a, b) {//按时间顺序排序
return a.time-b.time;
});
that.isLoad = true;
}
});
},
showBarrage: function () { //显示弹幕
if(this.data.length==0) return;
if(this.data[0].time>this.curTime) return;
var text = this.data[0].text; //取出首条弹幕
var top = Math.random()*(this.area_height-20);//随机生成弹幕文本位置
var dt = 6;//速度基点
if(this.area_width>600){
dt += 2;
}
if(this.area_width>800){
dt += 2;
}
if(this.area_width>1000){//大屏幕增加时间
dt += 2;
}
var dmtime = Math.random()*10+dt;//随机生成弹幕过渡时间
if(text.length>10){//长文本增加时间
dmtime+=5;
}
if(text.length>20){
dmtime+=4;
}
if(text.length>30){
dmtime+=3;
}
var div = $("<div></div>").text(text).css({"top":top,"animation-duration":dmtime});
$(this.area_id).prepend(div);//显示弹幕
this.data.shift(); //从数组中删除已输出的弹幕
},
on_off: function () { //弹幕开关处理函数
$(this.on_off_id).toggleClass("off");//切换开关状态
if(this.isOn){
$(this.area_id).css("opacity",0);//关闭弹幕
}else {
$(this.area_id).css("opacity",1);//打开弹幕
}
this.isOn = !this.isOn;
}
};
barrage.setBarrageArea(500,280);//设置弹幕区大小
事件监听:
- 单击播放按钮时加载弹幕数据
- 单击停止按钮时清除弹幕数据,以便再次播放时可从头开始
- 视频正常结束时也要清除弹幕数据
- 单击弹幕开关时切换弹幕显示状态
- 根据视频播放进度显示弹幕
JS代码:
//视频标签
var video = document.getElementById("video");
/** 单击播放按钮 **/
$("#play").click(function () {
video.play();
if(!barrage.isLoad){
barrage.getData();//加载弹幕数据
comment.clear();//清除评论区内容
}
});
/** 单击停止按钮 **/
$("#stop").click(function () {
video.pause();
video.currentTime = 0;
barrage.isLoad = false;
barrage.curTime = 0;
});
/* 单击弹幕开关按钮 */
$("#on_off").click(function () {
barrage.on_off();
});
/** 监听视频的ended事件 **/
document.getElementById("video").onended = function () {
barrage.isLoad = false;
barrage.curTime = 0;
};
/** 监听视频的timeupdate事件 **/
document.getElementById("video").ontimeupdate = function () {
barrage.curTime = this.currentTime; //同步视频时间
comment.append(); //添加到评论区
barrage.showBarrage(); //显示弹幕
};
评论控制
评论与弹幕是同步的,而且评论带有用户名。
要点:
- 加载弹幕时要清空评论区,以便再次播放时不会与上次重叠(见上面的单击播放按钮代码)
- 评论显示与弹幕是同步的(见上面的timeupdate事件)
- 评论区填满后要产生垂直滚动条,且要保证滚动条在底部,这样可以让评论向上滚动
- 自己发布的评论在弹幕上显示(已实现),还要写入后台数据库,这样别人才能看到你发表的评论(未实现)
JS代码:
/**** 评论控制 ****/
var comment = {
comment_area_id: "#comment_area",//评论区id
comment_id: "#comment", //评论框id
content_id: "#comment_content", //评论内容区id
sub_id: "#mycomment", //评论文本框id
subButton_id: "#comment_submit", //发布按钮id
setCommentArea: function (width,height) {//设置评论区大小
$(this.comment_area_id).css({"width":width,"height":height+115+"px"});
},
append: function () { //显示已有评论(数据在弹幕控制中)
if(barrage.data.length==0) return;
if(barrage.data[0].time>barrage.curTime) return;
var div = $("<div></div>");//生成评论标签
var span = $("<span></span>").text(barrage.data[0].username+":");
div.append(span,barrage.data[0].text);
$(this.content_id).append(div);//添加到评论内容区
$(this.comment_id)[0].scrollTop = $(this.comment_id)[0].scrollHeight;//保持滚动条在最底部
},
subComment: function () { //发表评论
if(!barrage.isLoad) return;
var text = $(this.sub_id).val();//评论内容
if(text.length==0) return;
barrage.data.unshift({ //添加到弹幕数组中
time: 0,//如果写入后台,需设置为视频当前时间video.currentTime
username: "我",
text: text
});
},
clear: function () { //清空评论区
$(this.content_id).html("");
}
};
/*单击评论发布按钮*/
$(comment.subButton_id).click(function () {
comment.subComment();
});
//设置评论区初始大小
comment.setCommentArea(280,250);
调试时要注意视频文件路径和json文件路径,弹幕和评论都必须在播放视频时才可用。