canvas实现弹幕效果
看到弹幕突然想着自己实现一个,查资料之后自己写了一个简单版的用画布实现弹幕效果
以下是代码,纯前端实现,代码里有注释
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .centainer{ /* text-align: center; */ } .video{ position: relative; } #video{ position: absolute; } #canvas{ position: absolute; /* 画布穿透 */ pointer-events:none; } .footer{ position: absolute; margin-top: 430px; } </style> </head> <body> <div class="centainer"> <div class="body"> <div class="video"> <video src="./1.mp4" id="video" width="640px" height="400px" controls></video> <canvas id="canvas"></canvas> </div> </div> </div> <div class="footer"> <input type="text" id="text"/><button id="btn">发送弹幕</button> <input type="color" id="color"/><input type="range" id="range"/> </div> <script> const $ = document.querySelector.bind(document); const video = $('#video'); const canvas = $('#canvas'); const text = $('#text'); const color = $('#color'); const range = $('#range'); const btn = $('#btn'); //弹幕数据 const data = [ {value:'弹幕1',time:0,color:'green',speed:'3',fondSize:40}, {value:'弹幕2',time:2,color:'green'}, {value:'弹幕3',time:2,color:'red'}, {value:'弹幕4',time:2,color:'orign'}, {value:'弹幕5',time:2,color:'blue'}, {value:'弹幕6',time:2,color:'green'}, {value:'弹幕7',time:2,color:'green'}, {value:'弹幕8',time:2,color:'green'}, {value:'弹幕9',time:2,color:'green'}, ] class Barrage{ constructor(obj,ctx){ this.value = obj.value; this.time = obj.time; this.obj = obj; this.ctx = ctx; } //初始化,弹幕出现的位置,速度颜色等 init(){ this.color = this.obj.color || this.ctx.color; this.speed = this.obj.speed || this.ctx.speed; this.fondSize = this.obj.fondSize || this.ctx.fondSize; //获取文本在画布中的宽度和高度 let span = document.createElement('span'); //添加内容 span.innerText = this.value; //设置字体 span.style.font = this.fondSize+'px "Microsoft Yahei" '; //加入到body document.body.appendChild(span); //此时获取该span标签的宽高 this.spanWidth = span.clientWidth; this.spanHeight = span.clientHeight; //移除该元素 document.body.removeChild(span); //随机出现弹幕的位置 this.x = this.ctx.canvas.clientWidth; this.y = this.ctx.canvas.height*Math.random(); //判断y值是否超出边界 if(this.y<this.fondSize){ //上边界出去了 this.y = this.fondSize; } if(this.y>this.ctx.canvas.height-this.fondSize){ this.y = this.ctx.canvas.height-this.fondSize; } } rander(){ this.ctx.context.font = this.fondSize+'px "Microsoft Yahei"'; this.ctx.context.fillStyle = this.color; this.ctx.context.fillText(this.value,this.x,this.y); } } class CanvasBarrage{ constructor(video,canvas,options){ if(!video&&!canvas) return; this.video = video; this.canvas = canvas; const defaultData = { color:'yellow', speed:2, fondSize:20, data:[] } //对象合并 Object.assign(this,defaultData,options); this.isPaused = true; //获取画布,设置宽度和video一样 this.context= canvas.getContext('2d'); this.canvas.width = video.clientWidth; this.canvas.height = video.clientHeight; //每一条数据渲染成一个弹幕实例 this.barrages = this.data.map(e=>{ return new Barrage(e,this) }) this.render(); } renderBarrage(){ //获取当前视频时间 const time = this.video.currentTime; this.barrages.forEach(barrage=>{ //如果没有渲染过,先初始化 if(!barrage.flag && time>=barrage.time){ if(!barrage.initRender){ barrage.init(); barrage.initRender = true; } //y轴不变,x轴递减移动 barrage.x -= barrage.speed; barrage.rander();//渲染 } if(barrage.x<=barrage.width){ barrage.flag = true;//停止渲染标记 } }) } render(){ //清空画布 this.context.clearRect(0,0,this.canvas.width,this.canvas.height); //渲染 this.renderBarrage(); //递归渲染 if(!this.isPaused){ requestAnimationFrame(this.render.bind(this)); } } reset(){ //清空画布 this.context.clearRect(0,0,this.canvas.width,this.canvas.height); //获取当前时间 const time = this.video.currentTime; this.barrages.forEach(barrage=>{ if(time<=barrage.time){ //移动进度条之后的时间如果还在渲染时间之前,重新初始化渲染 barrage.initRender = false; } }) } add(obj){ this.barrages.push(new Barrage(obj,this)); } }
//初始化实例,入口 const canvasBarrage = new CanvasBarrage(video,canvas,{data}); //播放监听事件 video.addEventListener('play',function(){ canvasBarrage.isPaused = false; canvasBarrage.render() }) //暂停监听事件 video.addEventListener('pause',function(){ canvasBarrage.isPaused = true; }) //滚动条移动监听事件 video.addEventListener('seeked',function(){ //重置 canvasBarrage.reset(); }) //添加弹幕 $('#btn').addEventListener('click',function(){ const value = $('#text').value; const color = $('#color').value; const time = video.currentTime; const fondSize = $('#range').value; const obj = {value,color,time,fondSize}; canvasBarrage.add(obj); }) </script> </body> </html>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通