Nodejs - 阻止socket.io降低帧速率

时间:2015-01-03 17:56:14

标签: javascript html5 node.js animation html5-canvas

我正在尝试在html5画布中编写一些动画。我需要在连接到我的服务器的任何其他客户端上重现动画。所以我要做的是将要调用的函数和参数作为字符串发送,并在客户端调用eval()。这样,动画逻辑只需要在一个画布上完成,而函数调用实际渲染的东西由所有客户端执行。

但是,当我这样做时,我的帧速率急剧下降。我使用socket.emit()向服务器发送信号,然后服务器调用socket.broadcast.emit()将字符串发送到所有客户端。这是渲染循环:

var rf = function()
{
   // clear background
   context.fillStyle = "#000";
   context.fillRect(0, 0, width, height);
   socket.emit('action', 'clearScreen', "{}");
   // mouse position to head towards
   var cx = (mousex - width / 2) + (width / 2),
       cy = (mousey - height / 2) + (height / 2);

   // update all stars
   var sat = Floor(Z * 500);       // Z range 0.01 -> 0.5
   if (sat > 100) sat = 100;
   for (var i=0; i<units; i++)
   {
      var n = stars[i],            // the star
          xx = n.x / n.z,          // star position
          yy = n.y / n.z,
          e = (1.0 / n.z + 1) * 2;   // size i.e. z

      if (n.px !== 0)
      {
         // hsl colour from a sine wave
         context.strokeStyle = "hsl(" + ((cycle * i) % 360) + "," + sat + "%,80%)";
         context.lineWidth = e;
         context.beginPath();
         socket.emit('action', 'context.beginPath', "{}");
         context.moveTo(xx + cx, yy + cy);
         socket.emit('action', 'context.moveTo', "{\"a\": [" + (xx + cx) + "," + (yy + cy) + "]}");
         context.lineTo(n.px + cx, n.py + cy);
         socket.emit('action', 'context.lineTo', "{\"a\": [" + (n.px + cx) + "," + (n.py + cy) + "]}");
         context.stroke();
         socket.emit('action', 'context.stroke', "{}");
      }

      // update star position values with new settings
      n.px = xx;
      n.py = yy;
      n.z -= Z;

      // reset when star is out of the view field
      if (n.z < Z || n.px > width || n.py > height)
      {
         // reset star
         resetstar(n);
      }
   }

   // colour cycle sinewave rotation
   cycle += 0.01;

   requestAnimFrame(rf);
};
requestAnimFrame(rf);

以上代码段取自here

你能建议一些防止帧率下降的方法吗?我想如果socket.emit是非阻塞的,可以这样做。 发送再现帧的字符串是实现我想要的最轻的方法。发送像素更加沉重。当框架易于绘制时,发送字符串工作正常 - 例如,上下移动的简单圆圈可以很好地呈现。

2 个答案:

答案 0 :(得分:1)

我已经走了你现在旅行的路。这是一个有趣的&amp;有趣的道路,但有时令人沮丧的道路。 玩得开心,耐心等待!

好的......这里有一些提示:

  1. 所有绘图命令必须是原子的。发送到任何客户端的每个命令都必须定义一个完整的绘图操作(从beginPath到stroke的完整路径操作)。

  2. 让您的客户变得聪明。在给定基本定义后,为每个客户提供至少足够的javascript智能来绘制每个基本形状。客户端函数:drawLine(),drawRect(),drawCircle(),drawBCurve(),drawQCurve(),drawText()等。

  3. 可能会丢失。这就是为什么发送原始上下文命令不是一个好主意。在每个命令对象中包含一个串行索引#,并在处理完每个命令数组后,将新数组附加到主数组(主数组包含所有已接收的数组)。这样,客户端可以识别并请求丢失数据包的替换。提示:您甚至可以使用此主数组来重放整个动画; - )

  4. 以下是一些扩展我的提示(不完整或经过测试!)的示例片段:

    在发出绘图命令的计算机上

    // emits can be lost
    // include an index# with each command so clients
    // can request replacements for lost commands
    var nextCommand=1;
    
    // an array to hold all commands to be sent with the next emit
    var commands=[];
    
    // Example: push 1 atomic line command into the commands array
    commands.push({
            op:'line',
            x0:xx+cx,
            y0:yy+cy,
            x1:n.px+cx,
            y1:n.py+cy,
            stroke:"hsl("+((cycle*i)%360)+","+sat+"%,80%)",
            fill:null,
            index:(nextCommand++),   
    });
    
    // Continue adding commands to be sent in this emit
    // Push any more atomic drawing commands into commands[]
    // You will probably generate many commands through a loop
    
    // serialize the commands array and emit it
    socket.emit(JSON.stringify(commands));
    

    在每个客户端

    // context color changes are expensive
    // Don't change the color if the context is already that color
    // These vars hold the current context color for comparison
    var currentStroke='';
    var currentFill='';
    
    // deserialize the incoming commands back into an array of JS command objects
    var commands=JSON.parse(receivedCommands);
    
    // process each command in the array
    for(var i=0;i<commands.length;i++){
    
        var cmd=commands[i];
    
        // execute each command's atomic drawing based on op-type
        switch(cmd.op){
            case 'line':
                drawLine(cmd);
                break;
            // ... also 'circle','rect',etc.
        }
    
    }
    
    // draw a line defined by an atomic line drawing command
    function drawLine(cmd){
    
        if(stroke){
    
            // execute primitive line commands
            context.beginPath();
            context.moveTo(cmd.x0,cmd.y0);
            context.lineTo(cmd.x1,cmd.y1);
    
            // Don't change the color if the context is already that color
            if(stroke!==currentStroke){
                context.strokeStyle=stroke;
                currentStroke=stroke;
            }
    
            // stroke the line (this completes an atomic line draw--beginPath thru stroke)
            context.stroke();
        } 
    
    }
    

答案 1 :(得分:0)

每帧仅发出一次,发送包含所有信息的对象actions

var rf = function()
{
   var actions = {};//prepare the object to be sent
   // clear background
   context.fillStyle = "#000";
   context.fillRect(0, 0, width, height);
   //socket.emit('action', 'clearScreen', "{}");
   actions['clearScreen'] = "{}";
   // mouse position to head towards
   var cx = (mousex - width / 2) + (width / 2),
       cy = (mousey - height / 2) + (height / 2);

   // update all stars
   var sat = Floor(Z * 500);       // Z range 0.01 -> 0.5
   if (sat > 100) sat = 100;
   actions['stars'] = [];//push all the star related actions in this array
   for (var i=0; i<units; i++)
   {
      var n = stars[i],            // the star
          xx = n.x / n.z,          // star position
          yy = n.y / n.z,
          e = (1.0 / n.z + 1) * 2;   // size i.e. z

      if (n.px !== 0)
      {
         // hsl colour from a sine wave
         context.strokeStyle = "hsl(" + ((cycle * i) % 360) + "," + sat + "%,80%)";
         context.lineWidth = e;
         context.beginPath();
         //socket.emit('action', 'context.beginPath', "{}");
         actions['stars'].push({
            'context.beginPath' : "{}"
         });
         context.moveTo(xx + cx, yy + cy);
         //socket.emit('action', 'context.moveTo', "{\"a\": [" + (xx + cx) + "," + (yy + cy) + "]}");
         actions['stars'].push({
            'context.moveTo' : "{\"a\": [" + (xx + cx) + "," + (yy + cy) + "]}"
         });
         context.lineTo(n.px + cx, n.py + cy);
         //socket.emit('action', 'context.lineTo', "{\"a\": [" + (n.px + cx) + "," + (n.py + cy) + "]}");
         actions['stars'].push({
            'context.lineTo' : "{\"a\": [" + (n.px + cx) + "," + (n.py + cy) + "]}"
         });
         context.stroke();
         //socket.emit('action', 'context.stroke', "{}");
         actions['stars'].push({
            'context.stroke' : "{}"
         });
      }

      // update star position values with new settings
      n.px = xx;
      n.py = yy;
      n.z -= Z;

      // reset when star is out of the view field
      if (n.z < Z || n.px > width || n.py > height)
      {
         // reset star
         resetstar(n);
      }
   }

   // colour cycle sinewave rotation
   cycle += 0.01;

   //emit only once
   socket.emit('actions',actions);

   requestAnimFrame(rf);
};
//requestAnimFrame(rf);
rf();//call directly