I am situation where i need to copy canvas on another page, both those pages are connected with socket.io so i\'ve communication channel.
i as far as i my research s
Transporting the full canvas content with each emit is resource intensive & inefficient.
Imagine sending a full 300x300px image to convey a single 1x1px rect drawn onto the canvas!
Instead, serialize each set of drawing commands (==each beginPath
through its ending fill
/ stroke
). You can use tokens to represent each drawing command.
For example...
ctx.beginPath();
ctx.moveTo(100,50);
ctx.lineTo(150,100);
ctx.lineTo(50,100);
ctx.closePath();
ctx.fillStyle='red';
ctx.fill();
... can be tokenized as:
var set1=[
['m',100,50],
['l',150,100],
['l',50,100],
['z'],
['fs','red'],
['f'],
['sequence',myOrder++],
['timestamp',performance.now()]
];
Of course, you can custom tokenize any complex command sets that are specific to your design. For example, you might have a single token that moves an entire 20x20 pixel block.
You can JSON.toString
this tokenized command set and broadcast it to other clients (==other pages).
When a client receives a message they can rehydrate the command set with JSON.parse
and process it on their own canvas.
A few notes to get you started:
Emits can be lost so the sequence
and timestamp
fields can be used to request missing packets and to properly order packets. You can do peer-to-peer packet management, but it's easier to have the server handle packet management.
Some canvas actions are not easily serializable. In particular, pixel manipulations with getImageData
are not easily serializable (including 3rd party image filters using getImageData).
In practice, you will sometimes need to "hard reset" all canvases to a "last known good" content-state. You could do this by having all canvases clear themselves and reissuing all command sets since the-beginning-of-time. To be more efficient, have the server occasionally cache an image of the last-known-good canvas (and any command sets issued after the known good). This way you can hard-reset if needed.
Depending on how big your canvas is and how often you are drawing, it might be cheaper to just use canvas.toBlob
or canvas.toDataURL
and send the entire canvas.
Checking your entire canvas for changes using getImageData
can be quite expensive since you have to iterate over every pixel. If you're changing most of the canvas each time, I'd at least test sending the entire canvas using canvas.toDataURL
to send a base64 encoded version.