vue+node.js+mysql实现视频弹幕功能

主体html结构

<div class="vedio-container">
  <div class="barrage-container-wrap" ref="barWrapper">
    <div class="video-wrapper">
      <common-video @watchCurrent="watchCurrentTime"></common-video>
    </div>
    <div class="barrage-container" ref="bar"></div>
  </div>
  <div class="input-wrapper">
    <input v-model="barrageInnerText" type="text">
    <div @click="send">发送弹幕</div>
  </div>
</div>

主要是:

<div class="barrage-container-wrap" ref="barWrapper">
  <div class="video-wrapper">
    <common-video @watchCurrent="watchCurrentTime"></common-video>
  </div>
  <div class="barrage-container" ref="bar"></div>
</div>

解释:

  1. barrage-container-wrap是最外层容器,包裹着video和弹幕层div
  2. video-wrapper是包裹着一个基础组件,commonVideo,监听一个watchCurrent的事件,这个事件主要用于,基础组件的video传送当前视频播放时间给父组件,父组件根据当前时间看是否有相应的弹幕,选择性发出
  3. barrage-container是弹幕层div,弹幕主要在这里显示
  4. 注意:由于弹幕层div的z-index必须要大于video才可以显示弹幕,所以commonVideo组件的video的controls组件要自己根据API设定,z-index大小:controls浮层>弹幕层>video,这样才可以既能控制,又能看弹幕,又能看视频

commonVideo组件html

<template>
  <div class="common-video">
    <video id="mycommonVideo" src="./../../assets/test.mp4"></video>
    <div class="bottom-controls">
      <span @click="changeStatus">{{playText}}</span>
      <div class="progress-bar" ref="progressBar" @click="setBar">
        <div class="progress-bar-active" ref="progressBarInner"></div>
      </div>
      <span class="time">{{currentT}} / {{allTime}}</span>
      <span @click="addYinliang">音量+</span>
      <span @click="decreaseYinliang">音量-</span>
      <span @click="allScreen">全屏</span>
    </div>
  </div> 
</template>

主要CSS

.vedio-container {
  width: 80%;
  height: 7rem;
  margin: 0 auto;
  font-size: 0.14rem;
  margin-bottom: 1rem;
}
.barrage-container-wrap {
  width: 100%;
  height: 7rem;
  position: relative;
  overflow: hidden;
}
.barrage-container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1;
  bottom: 30px;
  cursor: default;
  user-select: none;
}
.barrage-item {
  position: absolute;
  top: 0;
  left: 100%;
  white-space: nowrap;
  cursor: pointer;
  color: #fff;
}

解释:
1rem = 100px(自己设定),barrage-item是弹幕item


主要数据段

data() {
    return {
      barrageArray: [   //假弹幕数据
        {
          time: '5',
          text: '秋天爱美丽'
        },
        {
          time: '3',
          text: '2'
        },
        {
          time: '11',
          text: 'winter has come'
        }
      ],
      barrageColorArray: [   //弹幕颜色
        '#0099CC', '#333333', '#009966', '#FFFF66', '#9933FF', '#FFFF99', '#CCCCFF', '#CC9933', '#FFFF66'
      ],
      barrageInnerText: '',   //弹幕内容
      currentTime: 0,   //弹幕时间
    }
  }

主要js代码

初始化dom

this.barrageBoxWrap = this.$refs.barWrapper
this.barrageBox = this.$refs.bar
this.barrageWidth = parseInt(this.barrageBoxWrap.offsetWidth)
this.barrageHeight = parseInt(this.barrageBoxWrap.offsetHeight)

父组件中:按下按钮发送input框中的内容 send方法

send() {
  this.createBarrage(this.barrageInnerText, true)   //制作弹幕
  usersModel.sendBarrage({   //向后台发送post请求,发送当前视频时间,弹幕内容,存储到数据库中
    time: this.currentTime,
    content: this.barrageInnerText
  }).then((res)=>{
    console.log(res)
  })
  this.barrageInnerText = ''
}

createBarrage方法,参数1:弹幕内容,参数2:是否随机距离

createBarrage(msg, isSendMsg) {
   //生产dom结点
   let divNode = document.createElement('div')
   divNode.innerHTML = msg
   divNode.classList.add('barrage-item')
   this.$refs.barWrapper.appendChild(divNode)
   //设置偏离距离
   var barrageOffsetLeft = this.getRandom(this.barrageWidth, this.barrageWidth * 2)  //可设置随机距离
   barrageOffsetLeft = isSendMsg ? this.barrageWidth : barrageOffsetLeft  //不随机则就在距右边0px
   var barrageOffsetTop = this.getRandom(10, this.barrageHeight - 40)  //弹幕的高度
   var barrageColor = this.barrageColorArray[Math.floor(Math.random() * (this.barrageColorArray.length))]  //弹幕的颜色
   //执行并初始化滚动
   this.initBarrage(divNode, {
     left: barrageOffsetLeft,
     top: barrageOffsetTop,
     color: barrageColor
   })
 }

getRandom方法

getRandom(start, end) {
  return start + (Math.random() * (end - start))
}

initBarrage方法,初始化弹幕元素,参数1:弹幕dom,参数2:距离参数对象

initBarrage(el, obj) {
     obj.top = obj.top || 0
     obj.class = obj.color || '#fff'
     el.style.left = obj.left + 'px'
     el.style.top = obj.top + 'px'
     el.style.color = obj.color
     //添加属性
     el.distance = 0
     el.width = ~~window.getComputedStyle(el).width.replace('px','')
     el.timer = null
     this.barrageAnimation(el)
}

barrageAnimation方法,让弹幕动起来

barrageAnimation(el) {
  let that = this
  this.move(el)
  if (Math.abs(el.distance) < el.width + el.offsetLeft) {  //如果移动的距离小于弹幕的长度+外部div的宽度,则不断移动
    el.timer = requestAnimationFrame(function () {
      that.barrageAnimation(el);
    })
  } else {
    cancelAnimationFrame(el.timer);
    //删除节点
    el.parentNode.removeChild(el);
  }
}

解释:
cancelAnimationFrame,requestAnimationFrame是h5的请求动画帧的方法,递归实现平滑节能的动画效果

move方法

move(el) {
  el.distance--;
  el.style.transform = 'translateX('+el.distance+'px)';   //设置transform,平滑实现
  el.style.webkitTransform = 'translateX('+el.distance+'px)';
}

这样基本可以实现发送弹幕的效果了


从后台接受数据,并在相应的时间进行发送
首先设计的数据库表很简单:
id字段,time字段,content字段
简单的sql语句和node.js代码不贴出来了,如有需要则私聊我,这里主要介绍思想

思想是:子组件的video,向父组件emit事件,每一秒就传递当前video的currentTime,父组件根据函数看时间是否吻合,如果吻合,则用send方法发送弹幕

子组件JS代码:

//每个一秒设置进度条并设置时间
    setProgressBar() {
      let outerBarWidth = this.$refs.progressBar.offsetWidth
      this.oneTimer = setInterval(()=>{
        this.setTimes(this.videoDom.currentTime,'current')
        this.currentTimeNumber = this.videoDom.currentTime
        let baifenbi = this.currentTimeNumber / this.allTimeNumber
        let innerWidth = baifenbi * outerBarWidth
        this.$refs.progressBarInner.style.width = innerWidth + 'px'
        this.$emit('watchCurrent',Math.floor(this.currentTimeNumber))  //主要的代码是这一行,上面是设置conrtols的代码
      },1000)
    }

父组件代码:

<!-父组件监听-->
<common-video @watchCurrent="watchCurrentTime"></common-video>
 barrageArray: [   //假弹幕数据,在created的时候连接后台,把数据覆盖上去
        {
          time: '5',
          text: '秋天爱美丽'
        }
 ]
//子组件传递的每一秒,看这一秒是否有弹幕
    watchCurrentTime(seconds) {
      this.currentTime = seconds
      let barlen = this.barrageArray.length
      let that = this
      for(let i =0;i<barlen;i++){
        let item = this.barrageArray[i]
        if(!item){
          continue
        }
        if(item.time == seconds){
          that.createBarrage(item.text,true)
          // that.barrageArray.splice(i,1)
        }
      }     
    }
//不知道这个算法写的好不好,有想过把已经显示的弹幕splice掉,但是这样重新点击到相应的时间就看不到弹幕了,如果有更好的方法也可以改为你自己的算法哦

至此大概的功能的实现了,自己做的video controls的功能没有贴出来


源代码:
请私聊我要哦


参考
https://blog.csdn.net/qq_32849999/article/details/81031234
里面更加详细,如有需要可以进去看看

猜你喜欢

转载自blog.csdn.net/qq_40816360/article/details/84850657