Nodejs - 防止 socket.io 降低帧速率
Nodejs - prevent socket.io from dropping the frame rates
我正在尝试在 html5 canvas 中编写一些动画。我需要在连接到我的服务器的任何其他客户端上复制动画。所以我所做的是将要调用的函数和参数作为字符串发送,然后在客户端调用 eval()。这样动画逻辑只需要在一个 canvas 上完成,而实际渲染事物的函数调用则由所有客户端执行。
但是,当我这样做时,我的帧率急剧下降。我使用 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 是非阻塞的,就可以做到这一点。
发送重现框架的字符串是完成我想要的最轻松的方法。发送像素更重。当框架易于绘制时,发送字符串效果很好 - 例如,一个简单的上下移动的圆圈可以很好地渲染。
每帧只发射一次,发送一个包含所有信息的对象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
我一直在你现在走过的路上。这是一条有趣而有趣的道路,但有时也是一条令人沮丧的道路。 玩得开心,耐心等待!
好的...这里有一些提示:
你所有的绘图命令都必须是原子的。发送到任何客户端的每个命令都必须定义一个完整的绘图操作(从 beginPath 到 stroke 的完整路径操作) .
让您的客户变得聪明。 给每个客户至少足够的 javascript 智慧,在给出原始定义后绘制每个原始形状。客户端函数:drawLine()、drawRect()、drawCircle()、drawBCurve()、drawQCurve()、drawText() 等。
发射可以丢失。这就是为什么只发送原始上下文命令不是一个好主意的原因。在每个命令对象中包含一个序列索引#,并在处理每个命令数组后,将该新数组附加到主数组(主数组包含曾经收到的所有数组)。这样客户端可以识别并请求替换丢失的数据包。提示:您甚至可以使用这个主数组来重播整个动画 ;-)
以下是一些扩展我的提示的示例片段(未完成或测试!):
在发出绘图命令的计算机上
// 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();
}
}
我正在尝试在 html5 canvas 中编写一些动画。我需要在连接到我的服务器的任何其他客户端上复制动画。所以我所做的是将要调用的函数和参数作为字符串发送,然后在客户端调用 eval()。这样动画逻辑只需要在一个 canvas 上完成,而实际渲染事物的函数调用则由所有客户端执行。
但是,当我这样做时,我的帧率急剧下降。我使用 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 是非阻塞的,就可以做到这一点。 发送重现框架的字符串是完成我想要的最轻松的方法。发送像素更重。当框架易于绘制时,发送字符串效果很好 - 例如,一个简单的上下移动的圆圈可以很好地渲染。
每帧只发射一次,发送一个包含所有信息的对象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
我一直在你现在走过的路上。这是一条有趣而有趣的道路,但有时也是一条令人沮丧的道路。 玩得开心,耐心等待!
好的...这里有一些提示:
你所有的绘图命令都必须是原子的。发送到任何客户端的每个命令都必须定义一个完整的绘图操作(从 beginPath 到 stroke 的完整路径操作) .
让您的客户变得聪明。 给每个客户至少足够的 javascript 智慧,在给出原始定义后绘制每个原始形状。客户端函数:drawLine()、drawRect()、drawCircle()、drawBCurve()、drawQCurve()、drawText() 等。
发射可以丢失。这就是为什么只发送原始上下文命令不是一个好主意的原因。在每个命令对象中包含一个序列索引#,并在处理每个命令数组后,将该新数组附加到主数组(主数组包含曾经收到的所有数组)。这样客户端可以识别并请求替换丢失的数据包。提示:您甚至可以使用这个主数组来重播整个动画 ;-)
以下是一些扩展我的提示的示例片段(未完成或测试!):
在发出绘图命令的计算机上
// 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();
}
}