I have made a realtime collaborative drawing web app using node js, react js and socket.io. It works fine locally. When I open multiple tabs of localhost url and draw in one of the canvas, it shows in the other canvas a few seconds later. However, i've deployed it on AWS and it is not doing this behavior. I try to draw on the canvas but I wait for a long time and it doesn't show up in the other canvas.
My Server :
const express = require('express')
const path = require('path')
const app = express()
app.use(express.static(path.join(__dirname + '/public')))
const PORT = process.env.PORT || 3000
let server = app.listen(PORT);
const io = require('socket.io')(server);
var savedData;
io.on('connection', (socket)=>{
console.log('a user connected');
socket.on('canvas-data', (data) => {
savedData=data
socket.broadcast.emit('canvas-data', data);
console.log("canvas-data saved and emitted");
});
socket.on('saved-canvas-data', (data)=>{
io.to(socket.id).emit("saved-canvas-data", savedData);
});
})
My Client :
import React from 'react';
import io from 'socket.io-client';
import './style.css';
class Board extends React.Component {
timeout;
socket = io.connect("http://192.168.1.72:3000",{'force new connection': true});
isDrawing = false;
strokeStyle = "black";
constructor(props){
super(props);
this.socket.emit('saved-canvas-data', null);
this.socket.on('saved-canvas-data', function(data){
var canvas = document.querySelector('#board');
var ctx = canvas.getContext('2d');
var image = new Image();
image.onload = function(){
ctx.drawImage(image, 0, 0);
};
image.src = data;
});
this.socket.on("canvas-data", function(data){
var root = this;
var interval = setInterval(function(){
if(root.isDrawing) return;
root.isDrawing = true;
clearInterval(interval);
var canvas = document.querySelector('#board');
var ctx = canvas.getContext('2d');
var image = new Image();
image.onload = function(){
ctx.drawImage(image, 0, 0);
root.isDrawing = false;
};
image.src = data;
});
})
}
componentDidMount(){
this.drawOnCanvas();
}
drawOnCanvas(){
document.getElementById("black").style.border = "10px solid #ffbf00";
var canvas = document.querySelector('#board');
var ctx = canvas.getContext('2d');
var sketch = document.querySelector('#sketch');
var sketch_style = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width'));
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
var mouse = {x: 0, y: 0};
var last_mouse = {x: 0, y: 0};
/* Mouse Capturing Work */
canvas.addEventListener('mousemove', function(e) {
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
mouse.x = e.pageX;
mouse.y = e.pageY;
}, false);
/* Drawing on Paint App */
ctx.lineWidth = 2;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = this.strokeStyle;
canvas.addEventListener("mouseleave", function(event){
var mouseEvent = new MouseEvent("mouseup", {});
event.preventDefault();
canvas.dispatchEvent(mouseEvent);
});
canvas.addEventListener('mousedown', function(e) {
canvas.addEventListener('mousemove', onPaint, false);
}, false);
canvas.addEventListener('mouseup', function() {
canvas.removeEventListener('mousemove', onPaint, false);
}, false);
var lastTouchY;
canvas.addEventListener("touchstart", function (e) {
mouse = {x: e.touches[0].pageX ,y: e.touches[0].pageY };
var touch = e.touches[0];
if (e.touches.length < 2){
e.preventDefault();
}
var mouseEvent = new MouseEvent("mousedown", {
clientX: touch.pageX,
clientY: touch.pageY
});
canvas.dispatchEvent(mouseEvent);
}, false);
canvas.addEventListener("touchend", function (e) {
var mouseEvent = new MouseEvent("mouseup", {});
if (e.touches.length < 2){
e.preventDefault();
}
canvas.dispatchEvent(mouseEvent);
}, false);
canvas.addEventListener("touchmove", function (e) {
var touch = e.touches[0];
if (e.touches.length < 2){
e.preventDefault();
}
var mouseEvent = new MouseEvent("mousemove", {
clientX: touch.pageX,
clientY: touch.pageY
});
canvas.dispatchEvent(mouseEvent);
}, false);
var root = this;
var onPaint = function() {
ctx.beginPath();
var rect = canvas.getBoundingClientRect();
// var scrollY = document.body.scrollTop || document.documentElement.scrollTop;
// var scrollX = document.body.scrollLeft || document.documentElement.scrollLeft;
var scrollX = window.scrollX;
var scrollY = window.scrollY;
ctx.moveTo(last_mouse.x - rect.left - scrollX, last_mouse.y - rect.top - scrollY);
ctx.lineTo(mouse.x - rect.left - scrollX, mouse.y - rect.top - scrollY);
ctx.closePath();
ctx.stroke();
if(root.timeout != undefined) {
clearTimeout(root.timeout);
}
root.timeout = setTimeout(function(){
var base64ImageData = canvas.toDataURL("image/png");
root.socket.emit("canvas-data", base64ImageData);
}, 1000)
};
}
changeColor(string){
var canvas = document.querySelector('#board');
var ctx = canvas.getContext('2d');
ctx.strokeStyle = string;
if (string == "white") {
document.getElementById("white").style.border = "10px solid #ffbf00";
document.getElementById("black").style.border = "2px solid black";
}
else {
document.getElementById("black").style.border = "10px solid #ffbf00";
document.getElementById("white").style.border = "2px solid black";
}
}
render(){
return(
<div className="sketch" id="sketch">
<div className="heading" id="heading">
<h1>The Board</h1>
<p><i>Anyone can draw</i></p>
</div>
<div className="colors">
<div onClick={() => this.changeColor("black")} className="black" id="black"></div>
<div onClick={() => this.changeColor("white")} className="white" id="white"></div>
</div>
<canvas className="board" id="board"></canvas>
</div>
)
}
}
export default Board
I compile my react app and put the build inside my server folder with index.js and etc. The build command is set to npm install and start command is node index.js.
I have tried deploying on multiple sites like render.com, cloudflare, google cloud functions and etc. Render.com was the only one I could deploy and the others I couldn't figure out how to do. I was still facing the same issue though.
In client side, you just need to change :
socket = io.connect("http://192.168.1.72:3000",{'force new connection': true});
to :
socket = io.connect();