美文网首页
html5 canvas+video实时弹幕&添加弹幕功能

html5 canvas+video实时弹幕&添加弹幕功能

作者: Peter_2B | 来源:发表于2021-03-24 16:27 被阅读0次
    没有做全屏的实时弹幕

    html:

    <div id="container">
        <div id="content">
            <canvas id="canvas"></canvas>
            <video id="video" src="./x.mp4" width="640" height="380" controls></video>
        </div>
    
        <input type="text" id="text"> <button id="add">添加弹幕</button>
        <input type="color" id="color"> 
        <input type="range" id="range" max="40" min="20">
    </div>
    

    css:

    <style>
        #container{
            text-align: center;
        }
        #content{
            width: 640px;
            margin: auto;
            position: relative;
        }
        #canvas{
            position: absolute;
        }
        input{
            vertical-align: middle;
        }
    </style>
    

    js:

    var canvas = document.getElementById('canvas');
    var video = document.getElementById('video');
    var inp = document.getElementById('text');
    var add = document.getElementById('add');
    var coLor = document.getElementById('color');
    var range = document.getElementById('range');
    
    class CanvasBarrage{
            constructor(canvas, video, options={}){
    
                    if(!canvas || !video)return;
    
                    this.video = video;
                    this.isPaused = true;
    
                    this.canvas = canvas;
                    this.ctx = canvas.getContext('2d');     //获取画笔
                    
                    this.canvas.width = video.clientWidth;  //js设置canvas同video元素等高;
                    this.canvas.height = video.clientHeight;
    
                    let defaultOptions = {
                        fontSize: 20, color:'#000', speed: 2, opacity: 0.9,
                        getData:[]
                    };
                    
                    //合并对象: 1参是合并到的目标对象,后面的都是来源对象,合并到this实例对象中;
                            //合并对象到实例对象中; option={},没有传就是为空,合并的就是defaultOptions
                    Object.assign(this, defaultOptions, options);   
                    
                    //存放所有弹幕
                    this.barrages = this.getData.map(currentObj=>new Barrage(currentObj,this) );
    
                    console.log(this);
                    this.render(); 
            }
    
            render = ()=>{      //渲染画布
                                //第一次先进行清空操作:( x,y, width,height)
                this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
                this.renderBarrage();//渲染所有弹幕
    
                if(this.isPaused === false){
                    //递归渲染                  //回调函数的this必须用bind,否者指向window;
                    requestAnimationFrame( this.render.bind(this) ); //<--必须传一个回调
                    //这里用requestAni函数比用setInterval还渲染流畅很多
                }
            }
    
            renderBarrage = ()=>{
                //取出每个弹幕,判断时间和视频的事件是否符合,符合就执行渲染此弹幕
                let time = this.video.currentTime;
                this.barrages.forEach(currentBarrage=>{
    
                    if(!currentBarrage.flag && time >= currentBarrage.time){    //当视频播放时间等于或大于当前弹幕时间
                       
                            if(!currentBarrage.isInited){       //初始化,再进行绘制
                                currentBarrage.init();
                            }
    
                            currentBarrage.x -= currentBarrage.speed;
                            currentBarrage.currentRender();     //渲染此条弹幕
    
                            if(currentBarrage.x <= currentBarrage.width*-1){
                                currentBarrage.flag = true;     //当此条弹幕的x位置小于等于弹幕宽度
                            };
                    };
                });
            }
    
            addBarrage = (obj)=>{   
                this.barrages.push( new Barrage(obj, this) );
            }
    
            reset(){
                this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    
                let time = this.video.currentTime;
                this.barrages.forEach(currentBarrage=>{
    
                    currentBarrage.flag = false;
    
                    if(time <= currentBarrage.time){
                        currentBarrage.isInited = false;    //重新初始化
                    }else{
                        currentBarrage.flag = true;         //其他项目部渲染
                    };
                })
            }
    }
    

    class Barrage{
    
            constructor(currentObj, contextObj){
                
                this.value = currentObj.value;
                this.time = currentObj.time;    //value & time 是弹幕必传值
                this.contextObj = contextObj;
                this.isInited = false;
                this.flag = false;
                                                //如果没有传opacity就取defaultOptions的opacity;
                this.opacity = currentObj.opacity || this.contextObj.opacity;
                this.color = currentObj.color || this.contextObj.color;
                this.speed = currentObj.speed || this.contextObj.speed;
                this.fontSize = currentObj.fontSize || this.contextObj.fontSize;
            }
    
            init = ()=>{  //初始化此条弹幕: 宽高,位置;
                
                                //求此条弹幕的宽度,目的是用来检验当前是否还需要继续绘制
                let span = document.createElement('span');
                    span.innerText = this.value;
                    span.style.fontSize = this.fontSize +'px "Microsoft Yahei" ';
                    span.style.position = 'absolute';
                document.body.appendChild(span);
    
                this.width = span.clientWidth;
                this.height = span.clientHeight;  //span元素高度就是fontSize高度。span没有padding,剩下内容就只有fontSize高度支撑。 
                document.body.removeChild(span);  //获得此条弹幕高&宽,再从页面中删除。
    
                this.x = this.contextObj.canvas.width;  //此条弹幕出现在画布的x,y位置
                this.y = this.contextObj.canvas.height * Math.random(); //随机高度
    
                if(this.y < this.fontSize){
                    this.y = this.fontSize;
                }
                if(this.y > this.contextObj.canvas.height - this.fontSize){
                    this.y = this.contextObj.canvas.height - this.fontSize;
                }
    
                this.isInited = true;
            }
    
            currentRender = ()=>{ //渲染此条弹幕,画在画布上
                this.contextObj.ctx.font = this.fontSize + 'px "Microsoft Yahei" ';
                this.contextObj.ctx.fillStyle = this.color;
                this.contextObj.ctx.fillText(this.value, this.x, this.y);
            }
    }
    

    var getData = [
        //value & time 是必传值
        {value:'爷青回',speed:2,time:1,color:'#000',fontSize:20, opacity:0.8},
        {value:'爷的青春回来了',time:2},
    ];
    
    let ccc = new CanvasBarrage(canvas, video, {getData});
    video.addEventListener('play',function(){
        ccc.isPaused = false;
        ccc.render();
    });
    video.addEventListener('pause',function(){
        ccc.isPaused = true;
    });
    video.addEventListener('seeked',function(){  //拖动滚动条事件
        ccc.reset();
    });
    add.addEventListener('click',function(){
        let value = inp.value;
        let time = video.currentTime;
        let color = coLor.value;
        let fontSize = range.value;
        let obj = {value,time,color,fontSize};
        ccc.addBarrage(obj); 
    })
    

    相关文章

      网友评论

          本文标题:html5 canvas+video实时弹幕&添加弹幕功能

          本文链接:https://www.haomeiwen.com/subject/ceshhltx.html