1. 程式人生 > >vue+node.js+mysql實現視訊彈幕功能

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
裡面更加詳細,如有需要可以進去看看