question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

loadPaths() is laggy for data coming in from onUpdate() via socket-io (Collaborative canvas)

See original GitHub issue

Describe the bug Hello. Awesome work on the canvas!! I am trying to sync two whiteboards with each other. But the two keep getting laggy till it’s barely usable

To Reproduce When something is drawn on the first whiteboard, I get the CanvasPath[ ] from onUpdate() and send to the second whiteboard via socketio. On the second whiteboard, socketio captures the sent CanvasPath[ ] and I use loadPath() to display it on the screen. It works okay for the first few seconds and what is drawn on one end reflects quickly on the other. Then gets keeps getting laggy to the point where there is a huge delay. There is even a lag when drawing on the original whiteboard, leave alone reflecting on the other one. I also noticed that loadPaths() was trigerring onUpdate(), so I disabled socketio sending the paths until data is loaded

Expected behavior loadPaths() should not cause a lag

Screenshots If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: Kubuntu 20.04
  • Browser : Chrome
  • Version 86.0.4240.183

My code ` import React,{ useState, useEffect } from “react”; import { ReactSketchCanvas } from “react-sketch-canvas”; import ReactTooltip from ‘react-tooltip’; import { getsocketIoInstance } from ‘…/utils/socketio-client’;

export default class Whiteboard extends React.Component { constructor(props) { super(props); this.styles = { border: “0.0625rem solid #9c9c9c”, borderRadius: “0.25rem”, }; this.canvas = React.createRef(); this.state = { eraseMode: false } this.pauseSync = false; this.WhiteBoardMsgType = { canvas_draw: 1, canvas_undo: 2, canvas_redo: 3, canvas_clear: 4, } this.roomName = sessionStorage.getItem(‘roomName’); this.socketIo = getsocketIoInstance(this.roomName, ‘Whiteboard’); }

componentDidMount() { this.socketIo.on(‘whiteboard’, (changes) => { const { type, drawUpdates } = changes; if (type === this.WhiteBoardMsgType.canvas_draw) { this.pauseSync = true; this.canvas.current.loadPaths(drawUpdates); this.pauseSync = false; // setTimeout(() => { // this.pauseSync = false; // }, 50); } }); }

whiteBoardUpdated = (drawUpdates) => { if (!this.pauseSync) { const changes = { roomName: this.roomName, type: this.WhiteBoardMsgType.canvas_draw, drawUpdates } this.socketIo.emit(‘whiteboard’, changes); } // console.log(‘pause sync’, this.pauseSync); } toggleEraseMode = () => { this.canvas.current.eraseMode(!this.state.eraseMode); this.setState({ eraseMode: !this.state.eraseMode }) } undoCanvas = () => { this.canvas.current.undo(); // no need to send this change as they are already captured in the drawUpdates // const changes = { roomName: this.roomName, type: this.WhiteBoardMsgType.canvas_undo } // this.socketIo.emit(‘whiteboard’, changes); } redoCanvas = () => { this.canvas.current.redo(); // no need to send this change as they are already captured in the drawUpdates // const changes = { roomName: this.roomName, type: this.WhiteBoardMsgType.canvas_redo } // this.socketIo.emit(‘whiteboard’, changes); } clearCanvas = () => { this.canvas.current.clearCanvas(); // no need to send this change as they are already captured in the drawUpdates // const changes = { roomName: this.roomName, type: this.WhiteBoardMsgType.canvas_clear } // this.socketIo.emit(‘whiteboard’, changes); }

render() { return ( <div className="whiteboard">

Whiteboard

<ReactTooltip place="top" type="info" effect="float"/> <div className="whiteboard-icons"> <i className=“fas fa-eraser” data-tip={this.state.eraseMode ? ‘Stop Erase’: ‘Erase’} onClick={this.toggleEraseMode}> <i className="fas fa-broom"data-tip=‘Clear’ onClick={this.clearCanvas}> </div> <ReactSketchCanvas ref={this.canvas} style={this.styles} // width=“600” // height=“400” strokeWidth={4} strokeColor=“red” eraserWidth={20} onUpdate={this.whiteBoardUpdated} /> </div>

);

} } `

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
AllanMwirigicommented, Jan 3, 2021

Hello, again So after doing some digging, I realized that using Immutable.Js as per the example you had given was the problem I can’t believe I never realized that using ImmutableJs was resulting in different types of the data in curentPaths, than what was expected on the Canvas and Paths.tsx I first attempted to rework Paths.tsx to fit the types from ImmutableJs (mostly Maps). I was able to get it all done without any errors but still nothing would display on the Canvas. Next, I attempted to remove the ImmutableJs stuff from the server. I’m glad to say that this is working well !! I tried to replace the ImmutableJs code as best as I could. You can let me know if there is any serious downside of not using ImmutableJs. Here is my code below. Thanks a lot for all the help!!!

Server

"use strict";

const logger = require('./utils/winston');

const workspaces = {};

exports.initSync = (server) => {
  const socketio = require('socket.io')(server, {
    cors: {
      // NOTE!!!: in case domain is changed, ensure to replace/update these; local and prod domains
      origin: ["http://localhost:3000"],
      // if using socket.io v3, then these two are needed; had to downgrade to v2.3 because ngx-socket-io client in Angular didn't seem to be comaptible, was giving 400 errors
      methods: ["GET", "POST"],
      // credentials: true
    }
  });
  
  // sockets for real time data
  socketio.on('connection', (socket) => {
    socket.on('join-room', ({ roomName, userName }) => {
      // each user in a workspace will join a room identified by the room name
      // create a new entry in workspaces if none exists
      if (workspaces[roomName] == null) {
        workspaces[roomName] = {
          drawMode: true,
          isDrawing: false,
          // currentPaths: new _immutable.List(),
          currentPaths: [],
          canvasColor: "white",
          strokeColor: "red",
          strokeWidth: 4,
          eraserWidth: 20,
        }
      }
      socket.join(roomName);
      logger.debug(`socket ${socket.id} joined room ${roomName}`);
      socket.to(roomName).emit('join-room', userName);
      socketio.to(roomName).emit('whiteboard-paths', { 
        currentPaths: workspaces[roomName].currentPaths,
        isDrawing: workspaces[roomName].isDrawing
      });
    });
  
    socket.on('chat-msg', (data) => {
      const { roomName, txt, senderName } = data;
      socket.to(roomName).emit('chat-msg', { txt, senderName });
    });

    // Pointer down event
    socket.on("sketchPointerDown", function ({ roomName, point }) {
      // logger.debug(`pointerDown ${JSON.stringify(point)}`)
      if (workspaces[roomName] == null) {
        workspaces[roomName] = {
          drawMode: true,
          isDrawing: false,
          currentPaths: [],
          canvasColor: "white",
          strokeColor: "red",
          strokeWidth: 4,
          eraserWidth: 20,
        }
      }
      workspaces[roomName].isDrawing = true;
      const { drawMode, strokeColor, canvasColor, strokeWidth, eraserWidth } = workspaces[roomName];
      const cp = workspaces[roomName].currentPaths.slice();
      cp.push({
        drawMode: drawMode,
        strokeColor: drawMode ? strokeColor : canvasColor,
        strokeWidth: drawMode ? strokeWidth : eraserWidth,
        paths: [point]
      });
      workspaces[roomName].currentPaths = cp;

      socketio.to(roomName).emit('whiteboard-paths', { 
        currentPaths: workspaces[roomName].currentPaths,
        isDrawing: workspaces[roomName].isDrawing
      });
    });

    // Pointer move event
    socket.on("sketchPointerMove", function ({ roomName, point }) {
      // logger.debug(`pointerMove ${JSON.stringify(point)}`)
      if (workspaces[roomName] == null) {
        workspaces[roomName] = {
          drawMode: true,
          isDrawing: false,
          currentPaths: [],
          canvasColor: "white",
          strokeColor: "red",
          strokeWidth: 4,
          eraserWidth: 20,
        }
      }
      if (!workspaces[roomName].isDrawing) return;

      const cp = workspaces[roomName].currentPaths.slice();
      cp[workspaces[roomName].currentPaths.length - 1].paths.push(point);
      workspaces[roomName].currentPaths = cp;

      socketio.to(roomName).emit('whiteboard-paths', { 
        currentPaths: workspaces[roomName].currentPaths,
        isDrawing: workspaces[roomName].isDrawing
      });
    });

    // Pointer up event
    socket.on("sketchPointerUp", function ({roomName}) {
      if (workspaces[roomName] == null) {
        workspaces[roomName] = {
          drawMode: true,
          isDrawing: false,
          currentPaths: [],
          canvasColor: "white",
          strokeColor: "red",
          strokeWidth: 4,
          eraserWidth: 20,
        }
      }
      workspaces[roomName].isDrawing = false;
    });

  
  });
}
0reactions
vinothpandiancommented, Jan 3, 2021

You don’t have to use Immutable. You can do by writing pure functions in your code or try immer library.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Discussions · vinothpandian/react-sketch-canvas - GitHub
Freehand vector drawing component for React using SVG as canvas 🖌️ - Discussions ... loadPaths() is laggy for data coming in from onUpdate()...
Read more >
Laggy movement/synchronization between clients using JS ...
I've been trying to make a program using nodejs, socketio, js, where i can move a circle in a Canvas, and all other...
Read more >
react-sketch-canvas from vinothpandian - Coder Social
loadPaths() is laggy for data coming in from onUpdate() via socket-io (Collaborative canvas). Describe the bug. Hello. Awesome work on the canvas!!
Read more >
React component to draw using SVG - BestofReactjs
loadPaths() is laggy for data coming in from onUpdate() via socket-io (Collaborative canvas). Describe the bug Hello.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found