项目起始原因

源于数据库课设和以前的一次突发奇想。其实还有其他微信公众号的弹幕系统,但是我发现使用体验不佳,因为那种弹幕系统都是私用,并且只支持同时进行一个房间的使用。所以便萌生了自己写一个的想法。(第一次写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地址

Ranger
11 声望0 粉丝

前端菜鸡一枚,努力学习成为大佬--