pixicon


Paint++

The code below is from the recreation of a paintchat application where people can draw and chat online together. Although the code is pretty primitively written, this project was also my introduction into using websockets for realtime updates and I was able to tackle to main problems that I did not expect:

1. Visually showing shifts when changing tools. In order to both visually show which tool was un/selected/on-hover and limit functionality to their respective tools, switching states resulted in a lot of repeat code. This was circumvented through the use of 2 helper methods and specific file naming standards.

   changeImage(element, imageLink){
        let img = document.getElementById(`${element}`);
        img.setAttribute('src', imageLink);
    }

    setMode(mode){
        this.resetSelect();
        this.props.changeState(mode);
        this.setState({ mode: `${mode}` });
        this.changeImage(`tool_${mode}`, `/icons/tools_${mode}_select.png`);
    }

    <div >
        <img id='tool_draw' className='toolIcon' src='/icons/tools_draw.png'
            alt='brush'
            onMouseOver={(this.state.mode !== 'draw') ?
                () => this.changeImage('tool_draw', '/icons/tools_draw_hover.png')
                : function () {}}
            onMouseOut={
                (this.state.mode !== 'draw') ? () => this.changeImage('tool_draw', '/icons/tools_draw.png') : this.changeImage('tool_draw', '/icons/tools_draw_select.png')}
            onClick={() => {
                this.setMode('draw');
                this.props.returnToBrush()}} />
    </div>

2. Finding out a way to send appropriate data to be sent through websockets for images. Messages could be sent in a simple string/ text format, but images were a bit more complicated. In order to convert the canvas brush strokes into a form that could be sent using sockets, canvas's built in .toDataURL was used to save and convert the image on the canvas into a string form, saved to the state, and emitted to the server. On mounting, each client then received the broadcast image data, assigned as a new Image(), and drawn to the size of the canvas.

    draw(e){
        if (this.state.mode === 'erase') this.setState({strokeStyle: '#fff'});

        if ((this.state.mode ==='erase' || this.state.mode === 'draw') 
        && this.state.isDrawing && this.drawArea.current.contains(e.target)) {
            let canvas = document.getElementById('canvas');
            let ctx = canvas.getContext('2d');
            ctx.strokeStyle = this.state.strokeStyle;
            ctx.lineJoin = 'round';
            ctx.lineCap = 'round';
            ctx.lineWidth = this.state.lineWidth;
            ctx.beginPath();
            ctx.moveTo(this.lastX, this.lastY);
            ctx.lineTo(e.offsetX, e.offsetY);
            ctx.stroke();
            [this.lastX, this.lastY] = [e.offsetX, e.offsetY];
            this.sendImage();
        }
    }

    componentDidMount(){
        document.addEventListener('mousedown', this.handleMouseDown);
        document.addEventListener('mousemove', this.draw);
        document.addEventListener('click', this.eyedropper);

        this.socket.on('sketch update', (image) => {
            let canvas = document.getElementById('canvas');
            let ctx = canvas.getContext('2d');
            var myImg = new Image();
            let imgsrc = image;
            myImg.onload = function () {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.drawImage(myImg, 0, 0);
            };
            myImg.src = imgsrc;
        });
    }
    
    
    sendImage(){
      let canvas = document.getElementById('canvas');
      var dataURL = canvas.toDataURL();
      this.setState({ saveState: dataURL });

      this.socket.emit('sketch update', this.state.saveState);
    }





Resources I referenced if you also want to make your own :)!
  • Websockets: https://socket.io/get-started/chat/
  • HTML Canvas (Day 8): https://javascript30.com/