美文网首页
vue 直播弹幕功能

vue 直播弹幕功能

作者: 小蝴蝶_037a | 来源:发表于2020-03-13 14:56 被阅读0次

    需求是不重叠弹幕
    公司使用的是云信接发消息,我的思路是给直播间四条弹幕跑道,接收到弹幕之后往跑道内插入弹幕
    以下是其中一条弹幕跑道的html:

            <div class="trumpet-box box1" v-for="(item,index) in list[0]">
                    <div class="trumpet-item">
                        <div class="top">
                            <i v-show="item.barrageType === 64"></i>
                            <span v-show="item.grade>5"></span>
                            <p>{{item.userNickname}}</p>
                        </div>
                        <div class="bottom">
                            <img :src="item.userAvatar">
                            <p>{{item.content}}</p>
                            <i v-show="item.barrageType === 64"></i>
                        </div>
                    </div>
            </div>
    

    trumpet-item 为弹幕 如果接收到消息就往list里push,push前先判断当前跑道能不能插入弹幕,确保当前弹幕不会和上一条弹幕重叠,如果不能(即当前跑道有一条弹幕正在移动),则把当前弹幕插入下一条弹幕跑道里(我这里一共设了四条跑道),下面是方法:
    注:我这边定义的是弹幕在屏幕上移动的时长是6s,弹幕移动动画用的是translate
    首先是先把从云信接收的消息体obj整理成自己想要的格式

            formatObj(obj){
                let width = this.textSize(14,obj.content)>70?this.textSize(14,obj.content)+70:140//元素的宽度
                let boxWidth = document.getElementsByClassName('center')[0].offsetWidth //屏幕宽度
                let speed = (width+boxWidth)/6   //弹幕速度
                obj.second = (width/speed).toFixed(1)*1000 //元素插入需要的时间
                obj.startTime =  Date.now() //元素动画开始的时间
                obj.endTime =  obj.startTime+6000+obj.second //元素动画结束的时间
                obj.trumpetItemShow = true
                let style = document.styleSheets[0];
                let transformWidth = width+boxWidth;
                transformWidth = (Math.ceil(transformWidth))%2 ==0 ? Math.ceil(transformWidth) :Math.ceil(transformWidth)+1 //防止字体模糊
                style.insertRule("@keyframes bounce-in{from{ transform: translateX(0); }to{ transform: translateX(-"+transformWidth+"px);}}",1);//写入样式
                return obj
            },
            textSize(fontSize, text) { //根据文本内容获取元素宽度
                let span = document.createElement("span");
                span.innerText = text
                span.style.visibility = "hidden";
                span.style.fontSize = fontSize+'px';
                span.style.id = 'fakeSpan';
                document.getElementsByClassName("center")[0].appendChild(span);
                let newWidth = span.offsetWidth;
                document.getElementsByClassName("center")[0].removeChild(span);
                return newWidth;
            }
    

    上面的方法的作用是
    计算出并记录下
    1.弹幕从屏幕左边出现到完全出现需要的时间
    2.弹幕插入动画开始的时间(在屏幕左边出现前一秒)
    3弹幕动画结束的时间(在屏幕右边消失的后一秒)
    用于判断↓

     //判断当前弹幕是否能插入跑道
            getTrack(val){   //val是格式化好的弹幕
                let obj =  _.cloneDeep(val)
                for(let i = 0;i<this.list.length;i++){ //遍历四条跑道
                    let lastChatData = this.list[i][this.list[i].length-1]  //当前跑道最后一条数据
                    if(this.list[i].length==0){  //如果是当前跑道的第一条数据 直接插入跑道
                        this.list[i].push(this.formatObj(obj))
                        return
                    }
                    if(Date.now()-lastChatData.startTime>lastChatData.second){  //如果不是当前跑道的第一条数据 过了等待时间插入跑道
                        this.list[i].push(this.formatObj(obj))
                        return
                    } 
                }
                //都不是则进入等待队列
                this.toWait.push(obj)   
            },
    

    this.toWait 是等待队列 ,在所有跑道都有弹幕在插入的时候,新的弹幕消息进入等待队列,然后定时询问是否有跑道能插入数据 ,我这里使用的是watch

        watch:{
            YxLiveRoomBarrageVo(val){ //接收到消息的时候判断是否能插入弹幕
                this.getTrack(val)
            },
            toWait(newVal){ //等待队列有值得时候定时询问是否能插入弹幕
                if(newVal.length>0){
                    this.timer = setInterval(()=>{
                        for(let item of newVal){
                            this.getTrack(item)
                            this.toWait.shift()
                        }
                    }, 500)
                }else{
                    this.timer && clearInterval(this.timer)
                }
            },
            list(newVal){
                this.list.forEach((val,index)=>{
                    if(val.length>=20){
                        this.delectItem(val)
                    }
                })
            }
        },
    

    list是我的四条跑道

    data(){
            return {
                list:[[],[],[],[]],
                timer:null,
                timer1:null,
                toWait:[] //等待插入的弹幕
            }
        },
    

    监听它是为了定时清理数据 ,每条跑道的弹幕数据大于20的时候清理一次,
    清理的方法是

            delectItem(arr){ //定时清除
                for(let i = 0;i<arr.length;i++){
                    if(arr[i].endTime<Date.now()){
                        arr[i].trumpetItemShow = false
                    }
                }
            },
    

    所以上面的endTime是用来判断但是是不是已经完成动画了,完成了的弹幕就能清理掉,最后

        destroyed() {
            this.timer && clearInterval(this.timer)
            this.timer1 && clearInterval(this.timer1)
        }
    

    清一下定时器


    成果.jpg

    相关文章

      网友评论

          本文标题:vue 直播弹幕功能

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