2015-01-03 2 views
0

Я пытаюсь запрограммировать некоторые анимации в холсте html5. Мне нужно, чтобы анимация была воспроизведена на любом другом клиенте, подключенном к моему серверу. Поэтому я посылаю вызываемую функцию и аргументы в виде строки и вызываю eval() на стороне клиента. Таким образом, анимационная логика должна выполняться только на одном холсте, а вызовы функций, которые фактически делают вещи, выполняются всеми клиентами.Nodejs - предотвратить сокет.io от падения кадров

Однако, когда я это делаю, частота кадров падает резко. Я использую 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 не блокирует. Отправка строк, которые воспроизводят кадр, является самым легким способом выполнить то, что я хочу. Отправка пикселей еще более тяжелая. Отправка строк работает отлично, когда фрейм легко рисовать - например, простой круг, перемещающийся вверх и вниз, отлично отображает.

+0

Вы пытались использовать один 'emit' сигнал отправить все данные? – Aroll605

+0

Нет. Но это поможет? Я попробую это. Его правда, что 4 испускания будут уменьшены до 1. Но это все равно приведет к снижению fps? Есть ли способ сделать неблокирующий вызов? – lonesword

+1

Звонки в первую очередь не блокируются. Это просто означает, что вы можете сделать другой звонок, не дожидаясь результата этого. Вы увеличиваете накладные расходы путем передачи нескольких сигналов «emit», вот и все. И добраться до места назначения занимает больше времени. Поэтому, если вы отправляете его как один из «emit», все они будут поступать в одно и то же время. – Aroll605

ответ

1

Я был вниз по дороге вы едете прямо сейчас. Это интересная дорога &, но иногда это расстраивающая дорога. Веселитесь и будьте терпеливы!

ОК ... вот несколько советов:

  1. Все ваши команды рисования должны быть атомарными. Каждая команда, отправленная любому клиенту, должна определить полную операцию рисования (полная операция пути от beginPath до штриха).

  2. Сделайте своих клиентов умными. Дайте каждому клиенту хотя бы достаточно javascript smarts, чтобы нарисовать каждую примитивную форму после получения примитивного определения. Боковые функции клиента: DrawLine(), DrawRect(), DrawCircle(), drawBCurve(), drawQCurve(), DrawText() и т.д.

  3. Издает могут быть потеряны. Вот почему не рекомендуется отправлять сырые контекстные команды. Включите серийный индекс # в каждый объект команды и после обработки каждого массива команд добавьте этот новый массив в главный массив (главный массив содержит все полученные массивы). Таким образом, клиент может распознавать и запрашивать замены отсутствующих пакетов. Подсказка: вы можете даже используйте этот мастер-массив для воспроизведения всей анимации ;-)

Вот несколько примеров фрагментов, которые расширяют свои подсказки (не полные или испытано!):

На компьютере, выдающего рисунок команды

// 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(); 
    } 

} 
+0

Спасибо большое! Вы сделали аналогичный проект? Это открытый источник? Написание пользовательских функций, таких как drawLine(), - это одна вещь, которую я хотел избежать. Если я оставлю это до существующих HTML-команд, я могу однажды написать сценарий, чтобы преобразовать любой пример HTML-холста, чтобы он совместим с моей программой. – lonesword

+0

Добро пожаловать! Да, я был лидером по аналогичному проекту, который позволил инженерам по контролю качества сотрудничать, одновременно отмечая проблемы с продуктом. Приложение является проприетарным. О рисовании на клиенте таких функций, как drawLine(): эти функции представляют собой минимальные команды рисования, необходимые для успешного выполнения рендеринга фигуры на холсте. Даже если вы отправляете отдельные команды с вашего сервера, вы все равно должны выдать тот же набор команд (beginPath, moveTo, lineTo, stroke). Я нашел, что умеренно умный клиент уменьшает многие проблемы, возникающие при удалении. Удачи с вашим проектом! – markE

+0

Я звонил в socket.emit из цикла и в моих усилиях, чтобы повысить эффективность сигнала, я упустил этот глупый факт. Как вы предположили, я перебросил все мои команды в массив и отправил массив, и FPS, похоже, вверх. – lonesword

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 
Смежные вопросы