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>
复制代码

 

posted @   lijun12138  阅读(259)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示