创建一个html,在body部分写入:
<h1></h1>
<div id="container">
<div id="content">
<canvas id="canvas" ></canvas>
<video width="640px" height="360px" src="0001.mp4" controls id="video"></video>
</div>
<input type="text" id="text"> <button id="add">发射弹幕</button>
<input type="color" id="color"> <input type="range" max="40" min="20" id="range">
</div>
0001.mp4是测试用视频。
下面实现弹幕功能,首先先写一个数组,数组中存放一些已经写好的弹幕:
let data = [
{
value : "哈哈哈哈哈哈哈",//内容
speed : 2,//速度
color : 'red',
time : 0,//出现时间
fontSize : 20
},
{
value : "哈哈哈哈哈哈哈哈哈哈",//内容
speed : 5,//速度
time : 10,//出现时间
fontSize : 20
}
]
创建一个CanvasBarrage对象,用于把弹幕渲染到canvas画布上:
class CanvasBarrage{
constructor(canvas,video,options={}){
if(!canvas||!video) return;
this.canvas = canvas;
this.video = video;
let defaultOptions = {
fontSize:20,
color:"gold",
speed:2,
opacity:0.9,
data:[]
}
Object.assign(this,defaultOptions,options);
//获取画布
this.context = canvas.getContext('2d')
//设置登高
this.canvas.width = video.clientWidth
this.canvas.height = video.clientHeight
//是否暂停
this.isPause = true;
//Barrage是弹幕的实例类
this.barrages = this.data.map(obj=>new Barrage(obj,this));
//渲染弹幕
this.render();
}
renderBarrage(){
//将数组中的弹幕一一取出,判断时间和视频的时间是否符合,符合就执行渲染
let time = this.video.currentTime
this.barrages.forEach(barrage => {
if(!barrage.flag && time>=barrage.time){
if(!barrage.isInited){
barrage.init();
barrage.isInited = true
}
barrage.x-=barrage.speed;
barrage.render();
if(barrage.x<=barrage.width*-1){
barrage.flag = true
}
}
})
}
render(){
//先清空
this.context.clearRect(0,0,this.canvas.width,this.canvas.height)
//渲染
this.renderBarrage();
if(this.isPause===false){
requestAnimationFrame(this.render.bind(this))
}
}
}
为每一个弹幕创建一个barrage对象,两个方法,一个确定这个弹幕长啥样,主要是要求宽和高,另一个将其添加到画布上:
class Barrage{
constructor(obj,ctx){
this.value = obj.value
this.time = obj.time
this.obj = obj
this.ctx = ctx
}
init(){
this.opacity = this.obj.opacity||this.ctx.opacity;
this.color = this.obj.color||this.ctx.color;
this.fontSize = this.obj.fontSize||this.ctx.fontSize;
this.speed = this.obj.speed||this.ctx.speed;
//求自己的宽度
let span = document.createElement('span')
span.innerText = this.value;
span.style.font = this.fontSize + 'px "Microsoft YaHei"';
span.style.position = 'absolute'
document.body.appendChild(span)
//记录弹幕有多宽
this.width = span.clientWidth
document.body.removeChild(span)
this.x = this.ctx.canvas.width
this.y = this.ctx.canvas.height * Math.random();
if(this.y < this.fontSize){
this.y = this.fontSize
}
if(this.y > this.ctx.canvas.height-this.fontSize){
this.y = this.ctx.canvas.height-this.fontSize
}
}
render(){
this.ctx.context.font = this.fontSize + 'px "Microsoft YaHei"';
this.ctx.context.fillStyle = this.color;
this.ctx.context.fillText(this.value,this.x,this.y);
}
}
确认视频是否在播放:
let Canvasbarrage = new CanvasBarrage(canvas,video,{
data
});
video.addEventListener('play',function(){
Canvasbarrage.isPause = false;
Canvasbarrage.render()
})
video.addEventListener('pause',function(){
Canvasbarrage.isPause = true;
})
即:判断视频是否暂停,如果没有暂停就反复清空并渲染,对于每一条弹幕,如果发现该时刻有该弹幕出现,就把它渲染到页面上的随机高度,并且不断向左移动,如果完全跃出屏幕就将flag置为true,今后不再渲染。
然后实现发射弹幕功能,点击发射弹幕时执行函数Canvasbarrage.add:
add.addEventListener('click',function(){
let value = document.getElementById("text").value
let time = video.currentTime
let color = document.getElementById("color").value
let fontsize = document.getElementById("range").value
let obj = {
time,value,color,fontsize
}
Canvasbarrage.add(obj)
})
为Canvasbarrage添加add方法:
add(obj){
this.barrages.push(new Barrage(obj,this));
}
然而如果把视频倒回去,原先该出现的弹幕就不再出现了,因此需要对此进行改进:
video.addEventListener("seeked",function(){
Canvasbarrage.reset()
})
为Canvasbarrage添加reset方法:
reset(){
this.context.clearRect(0,0,this.canvas.width,this.canvas.height)
let time = this.video.currentTime
this.barrages.forEach(barrage => {
barrage.flag = false;
if(time<=barrage.time){
barrage.isInited = false;
}else{
barrage.flag = true;
}
})
}
如果往后调,无妨,往前调原先出现过的弹幕还要出现。