使用canvas實現一個vue彈幕組件功能

看B站時,對彈幕的實現產生了興趣,一開始想到用css3動畫去實現,後來感受這樣性能不是很好,查了下資料,發現能夠用canvas實現,因而就摸索着寫了一個簡單的彈幕。css

彈幕功能前端

支持動態添加彈幕
彈幕不重疊
自定義彈幕顏色
效果圖
vue

前端框架選了比較熟悉的vuejscss3

彈幕滾動的基本思路就是經過定時器不斷地改變彈幕的位置,時時重繪畫布。程序員

實現步驟canvas

先加入一個canvas標籤,這裏有個注意點,關於設備像素比對canvas的影響,會出現繪圖模糊。數組

<canvas width="600" height="600"></canvas> // 若是單純這樣寫,canvas會出現模糊
<canvas width="600" height="600" style="width: 300px;height: 300px">
</canvas>
//爲了避免出現模糊,須要設置canvas的css寬高爲上下文寬高的1/devicePixelRatio,
本文是對於devicePixelRatio:2的設備設置的,該值可從window.devicePixelRatio取得。
<canvas ref="hiddenCanvas" width="0" height="0" style="display: none">
</canvas> 
// 後面會用到

咱們先定義一個數組來存放彈幕數據,一條彈幕信息,包括文本內容,x,y座標位置,顏色,速度(能夠是隨機或者固定,爲了計算簡單,咱們這裏採用了固定的速度)前端框架

var dmArr = [];
var gap = 80; // 彈幕的上下間距
var hiddenCanvas = this.$refs.hiddenCanvas;
// 增長彈幕的方法
function pushDm(text, color) {
  let y = getY(); // 先肯定跑道
  let x = 600; // 初始x座標爲canvas的右邊界
  let delayWidth = 0; // 同跑道
  for (let i = 0, len = dmArr.length; i < len; i++) {
    let dm = dmArr[i];
    if (y === dm.y) { // 若是是同跑道,則日後排,設置必定的間隔,保證彈幕不會重疊;
      delayWidth += Math.floor(hiddenCanvas.getContext('2d').measureText(dm.text).width * 4 + 50);
    }  }
  dmArr.push({
    text: text,
    x: x + delayWidth,
    y: y,
    speed: 8,
    color: color || getColor()
  });
}
// 隨機得到y座標
function getY() {
  let range = Math.floor(600 / gap); // 跑道數量
  return Math.floor(Math.random() * range + 1) * gap;
}
// 隨機得到顏色
function getColor() {
  return `${Math.floor(Math.random() * 16777215).toString(16)}`;
}
// 寫一個for循環,初始化30條彈幕
for (let i = 0; i < 30; i++) {
  pushDm(`It's barrage ${i}`);
}

接下來設置一個20ms的定時器,實現彈幕滾動效果css3動畫

var timer = null;
var ctx = this.$refs.canvas.getContext('2d');
function start(){
 timer = setInterval(() => {
  ctx.clearRect(0, 0, 600, 600); // 每次須要清空畫布
  ctx.save();
  ctx.font = '30px Microsoft YaHei'; // 這裏須要把字體大小設爲須要顯示的css大小的2倍(devicePixelRatio爲2時)
  if (!dmArr.length) stop(); // 若是沒有新彈幕了,就中止計時器
  for (let i = 0, len = this.dmArr.length; i < len; i++) {
    let dm = dmArr[i];
    let overRange = -ctx.measureText(dm.text).width * 2;
    dm.x -= dm.speed;
    if (dm.x < overRange) {
      dmArr.splice(i, 1); // 彈幕在畫布中不可見時,從數組中移除該項
      continue;
    }
    ctx.fillStyle = `#${dm.color}`;
    ctx.fillText(dm.text, dm.x, dm.y);
  }
  ctx.restore();
 }, 20);
}
function stop() {
  clearInterval(timer);
  ctx.clearRect(0, 0, 600, 600);
}

咱們還須要一個輸入框,來實現手動添加彈幕功能框架

<input type="text" @keyup.enter="sent" v-model="dmInput" maxlength="20">
 
<button type="button" @click="sent">發表</button>
var dmInput = '';
var color = ''; // 可自定義彈幕的顏色
function sent() {
  if (!dmInput) return;
  stop();
  pushDm(dmInput, color);
  start();
  dmInput = '';
}

有待改進的地方和疑問?速度不恆定時,怎麼保持彈幕不重疊視頻彈幕是根據彈幕發送時間點來定位到視頻的每一幀?如何實現?

爲了學習工做與休閒娛樂互不衝突,現新建圈【碼農茶水鋪】用於程序員生活,愛好,交友,求職招聘,吐槽等話題交流,但願各位大神工做之餘到茶水鋪來喝茶聊天。羣號:582735936

相關文章
相關標籤/搜索