基於微信公衆號的多房間實時彈幕消息系統

項目起始原因

源於數據庫課設和以前的一次突發奇想。其實還有其他微信公衆號的彈幕系統,但是我發現使用體驗不佳,因爲那種彈幕系統都是私用,並且只支持同時進行一個房間的使用。所以便萌生了自己寫一個的想法。(第一次寫md,有點不會,希望諒解--)

主要技術點

  • Redis(結合socket實現在非socket中主動發送socket消息)
  • MySQL(數據持久化)
  • socket.io(實現消息實時推送)
  • Express(後端主要使用框架)

實現的主要技術點和難點

  • 接受微信服務器的消息推送

微信服務器推送的消息,我們在Express中通過data數據流的形式接受,然後xml轉爲json格式。即得到我們需要的數據。部分代碼如下(getXml爲封裝的函數用於處理數據):

//  處理上傳消息請求
let promise = new Promise((resolve,reject) => {
  let buffer = [];
  //監聽 data 事件 用於接收數據
  req.on('data',chunk => {
      buffer.push(chunk);
  });
  //監聽 end 事件 用於處理接收完成的數據
  req.on('end',() => {
      let msgXml = Buffer.concat(buffer).toString('utf-8');
      xml.xmltool.getXml(msgXml).then(datas => {
          resolve(JSON.stringify(datas.xml));
      }).catch(e => {
          reject(JSON.stringify(e));
      })
  })
});
  • websocket(socket.io)

socket.io是已經封裝好的一個庫。我們只需要安裝之後,就可方便的使用。同時藉助Redis實現在非socket接口中推送socket消息流。需要使用到的模塊主要是:

  1. socket.io
  2. socket.io-redis
  3. socket.io-emitter

部分核心代碼如下(作爲一個模塊導出直接使用):

const redis = require('redis');
const ioRedis = require('socket.io-redis');

// var roomInfo = {};

let ioCreater = function(server) {
    const io = require('socket.io')(server);
    io.on('connection', function (socket) {

        var url = socket.request.headers.referer;
        var splited = url.split('/');
        var roomID = splited[splited.length - 1];   // 獲取房間ID
        // var user = '';
        console.log(socket.request.headers);
        console.log(roomID);
        socket.join(roomID);    // 加入房間

       
        socket.on('disconnect', function(){    //斷開socket連接的時候觸發
            console.log('user disconnected');
        });
        socket.on('message', function(){  //接收socket連接消息的時候觸發
            console.log('received a message');
        });
        socket.on('connect', function(){  //建立socket連接時候觸發
            console.log('connect a socket client');
        });

    });

    io.adapter(ioRedis({host:"127.0.0.1", port:"6379" })); //使用socket.io-adapter設置緩存依賴
    return io; 
};
 module.exports = ioCreater;
  • 多房間

在使用socket.io的時候,我們很方便的就可以創建socket.io多房間。我們只需要在socket連接的時候帶上參數即可。所以在初始化的時候,我們需要將房間號發到前端,前端拿到之後,創建相應的socket連接即可。同時前端頁面可以直接使用node_modules裏面的socket包。

    <script src="/socket.io/socket.io.js"></script>
    <script>
              var socket = io.connect('http://localhost');
    </script>
  • 彈幕實現

因爲課設有時間限制的原因,所以沒有自己去手動實現彈幕效果,我直接在github上面找了一個基於jQuery的彈幕插件。jQuery.danmu.js(體驗還不錯)部分核心代碼如下:

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
    <script src="/javascripts/jquery.min.js"></script>
    <script src="/javascripts/jquery.danmu.min.js"></script>
    <!--jquery.danmu.js (//github.com/chiruom/danmu/) - Licensed under the MIT license-->
  </head>
  <style>
    *{
      margin: 0;
      padding: 0;
    }
    html{
      width: 100%;
      height: 100%;
    }
    body{
      height: 100%;
      margin: 0;
    }
    #danmu{
      height: 100%;
      width: 100%;
    }
    #father{
      height: 100%;
      width: 100%;
    }
  </style>
  <body>
  <div id="father">
    <div id="danmu"></div>
  </div>
  <!--<button type="button" onclick="zanting()">暫停</button>-->
    <script src="/socket.io/socket.io.js"></script>
    <script>
        function bg3(){
            var r=Math.floor(Math.random()*256);
            var g=Math.floor(Math.random()*256);
            var b=Math.floor(Math.random()*256);
            return "rgb("+r+','+g+','+b+")";//所有方法的拼接都可以用ES6新特性`其他字符串{$變量名}`替換
        }

      $('body').css('margin','0');
      $('body').css('padding','0');
      var width = document.body.scrollWidth;
      var height = document.body.scrollHeight-$('#father').scrollTop;
      $("#danmu").danmu({
            height: height,
            width: width,
            zindex :100,   //彈幕區域z-index屬性
            speed:7000,      //滾動彈幕的默認速度,這是數值值得是彈幕滾過每672像素所需要的時間(毫秒)
            sumTime:65535,   //彈幕流的總時間
            danmuLoop:true,   //是否循環播放彈幕
            defaultFontColor:"#FFFFFF",   //彈幕的默認顏色
            fontSizeSmall:24,     //小彈幕的字號大小
            FontSizeBig:32,       //大彈幕的字號大小
            opacity:"0.9",            //默認彈幕透明度
            topBottonDanmuTime:6000,   // 頂部底部彈幕持續時間(毫秒)
            SubtitleProtection:true,     //是否字幕保護
            positionOptimize:false,         //是否位置優化,位置優化是指像AB站那樣彈幕主要漂浮於區域上半部分

            maxCountInScreen: 40000,   //屏幕上的最大的顯示彈幕數目,彈幕數量過多時,優先加載最新的。
            maxCountPerSec: 10000      //每分秒鐘最多的彈幕數目,彈幕數量過多時,優先加載最新的。
        });

        $('#danmu').danmu('danmuStart');
      function zanting() {
          $('#danmu').danmu('danmuPause');
      }
      var socket = io.connect('http://localhost');

      socket.on('news', function (data) {
          console.log(data.data);
          var time = $('#danmu').data("nowTime") +1;
          var color = bg3(),size = '1';
          // var position = Math.floor(Math.random()*3);
          var position = '0';
          var text_obj = '{ text:"' + data.data + '",color:"' + color + '",size:"' + size + '",position:"' + position + '",time:' + time + '}';   //構造加上了innew屬性的字符串danmu對象
          var new_obj = eval('(' + text_obj + ')');
          $("#danmu").danmu("addDanmu",new_obj);
          socket.emit('my other event', { my: 'data' });
      });
    </script>
  </body>
</html>

寫在最後的話

代碼很粗糙,如果有參考價值的話,希望多多支持。如果有不懂得地方,請提出來,我會盡我全力爲您解答的。附上github地址,如果對您有幫助的話,希望給我一個小小的star,這是對我最大的鼓勵和支持,前端的路很長,我希望自己能堅持下去!!共勉

github地址
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章