skip to Main Content

This canvas have been creating an error since I created it and I haven’t found a solution after all my research. so I need a hand. I need the canvas to create a straight line but it end up creating multiple straight lines from the starting point when the mouse is moving instead of one straight line.

<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]>      <html class="no-js"> <!--<![endif]-->
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title></title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="">
    </head>

    <style>
        *{
            box-sizing: border-box;
        }

        body,html{
            padding: 0;
            margin: 0;
            height: 100vh;
            width: 100vw;
        }


    </style>
    <body>
       
        <canvas>
            
        </canvas>
        
        <script>
            var colorLine = 'black'
            var fillLine = 'none'
            var prevX = ''
            var prevY = ''
            var isDrawing = false
            var snapshot;
            var canvas = document.querySelector('canvas');
            var ctx = canvas.getContext('2d');

            
            let backgroundSetUp = () => {
                
                canvas.height = window.innerHeight - 50; 
                canvas.width = window.innerWidth - 50;
                ctx.fillStyle = colorLine;
                ctx.fillStyle = fillLine
            };

            let startDrawing = e => {
                isDrawing = true
                
                prevX = e.offsetX 
                prevY = e.offsetY 
                ctx.beginPath();
                ctx.strokeStyle  = 'black'
                ctx.fillStyle = '#fff'
           
            }

            let Drawing = e => {
                if(isDrawing === false)
                return
                ctx.beginPath();
                ctx.moveTo(prevX, prevY);
                ctx.lineTo(e.offsetX, e.offsetY);
                ctx.stroke()

                
            }

            let stopDrawing = e => {
                isDrawing = false
            }






            canvas.onmousedown = ( e => {
                startDrawing(e)
            })
            canvas.onmousemove = ( e => {
                Drawing(e)
            })
            canvas.onmouseup = ( e => {
                stopDrawing(e)
            })

            backgroundSetUp()


        </script>
    </body>
</html>

2

Answers


  1. You should clear your canvas drawing before you do another draw:

    ctx.clearRect( 0, 0, canvas.width, canvas.height );
    

    This will reset the canvas before you draw your ‘corrected’ line to the new mouse position. If you want to draw further lines you will need another canvas with a copy of the previous drawing, and draw it back into the output canvas instead of using clear.

    var colorLine = 'black'
    var fillLine = 'none'
    var prevX = ''
    var prevY = ''
    var isDrawing = false
    var snapshot;
    var canvas = document.querySelector('canvas');
    var ctx = canvas.getContext('2d');
    
    
    let backgroundSetUp = () => {
        canvas.height = window.innerHeight - 50; 
        canvas.width = window.innerWidth - 50;
        ctx.fillStyle = colorLine;
        ctx.fillStyle = fillLine
    };
    
    let startDrawing = e => {
        isDrawing = true
        prevX = e.offsetX 
        prevY = e.offsetY;
        ctx.beginPath();
        ctx.strokeStyle  = 'black'
        ctx.fillStyle = '#fff'
    
    }
    
    let Drawing = e => {
        if(isDrawing === false) return
        // This is where you redraw or clear the previous canvas
        ctx.clearRect( 0, 0, canvas.width, canvas.height );
        ctx.beginPath();
        ctx.moveTo(prevX, prevY);
        ctx.lineTo(e.offsetX, e.offsetY);
        ctx.stroke()
    }
    
    let stopDrawing = e => {
        isDrawing = false
    }
    
    
    
    
    
    
    canvas.onmousedown = ( e => {
        startDrawing(e)
    })
    canvas.onmousemove = ( e => {
        Drawing(e)
    })
    canvas.onmouseup = ( e => {
        stopDrawing(e)
    })
    
    backgroundSetUp()
    <canvas></canvas>

    Here is one with a buffer instead, allowing you to continue drawing after the first line. I have also cleaned up some of your definitions (correctly using const and let, ignoring var and using function where it makes sense to create a little more concise and clear code):

    function onResize(){
    
        canvas.height = window.innerHeight - 50; 
        canvas.width = window.innerWidth - 50;
        
        ctx.fillStyle = colorLine;
        ctx.fillStyle = fillLine;
        
    }
    function onPointerStart( e ){
      
        // Write the current state into the buffer when we start
        buffer.width = canvas.width;
        buffer.height = canvas.height;
        bufferCtx.drawImage( canvas, 0, 0 );
        
        isDrawing = true;
        prevX = e.offsetX;
        prevY = e.offsetY;
        ctx.beginPath();
        ctx.strokeStyle  = 'black';
        ctx.fillStyle = '#fff';
    
    }
    function onPointerMove( e ){
    
        if( isDrawing === false ) return;
        
        // Clear the screen, redraw the buffer into it
        ctx.clearRect( 0, 0, canvas.width, canvas.height );
        ctx.drawImage( buffer, 0, 0 );
        
        // Draw your path
        ctx.beginPath();
        ctx.moveTo(prevX, prevY);
        ctx.lineTo(e.offsetX, e.offsetY);
        ctx.stroke();
        
    }
    function onPointerEnd( e ){
    
        isDrawing = false;
        
    }
    
    const canvas = document.querySelector('canvas');
    const ctx = canvas.getContext('2d');
    
    // This will be our buffer
    const buffer = document.createElement('canvas');
    const bufferCtx = buffer.getContext('2d');
    
    let colorLine = 'black';
    let fillLine = 'none';
    let prevX = 0;
    let prevY = 0;
    let isDrawing = false;
    let snapshot;
    
    window.addEventListener( 'mousedown', onPointerStart );
    window.addEventListener( 'mousemove', onPointerMove );
    window.addEventListener( 'mouseup', onPointerEnd );
    window.addEventListener( 'resize', onResize );
    
    onResize()
    <canvas></canvas>
    Login or Signup to reply.
  2. Store the position of existing line, so you can erase it

    Disconcertingly, I needed to make the erasing-line a little thicker (3px) than the drawing line (1px) to get full erasure. Even 2px of erasing was not quite enough. I am open to explanations why this might be – it surprised me.

    var colorLine = 'black'
    var fillLine = 'none'
    var prevX = ''
    var prevY = ''
    var currX = ''
    var currY = ''
    var isDrawing = false
    var snapshot;
    var canvas = document.querySelector('canvas');
    var ctx = canvas.getContext('2d');
    
    
    let backgroundSetUp = () => {
      canvas.height = window.innerHeight - 50;
      canvas.width = window.innerWidth - 50;
      ctx.fillStyle = colorLine;
      ctx.fillStyle = fillLine
    };
    
    let startDrawing = e => {
      isDrawing = true
      prevX = e.offsetX
      prevY = e.offsetY
      currX = prevX
      currY = prevY
    
    }
    
    let Drawing = e => {
      if (isDrawing === false)
        return
    
    
      ctx.strokeStyle = 'white'
      ctx.lineWidth = 3;
      ctx.beginPath();
      ctx.moveTo(prevX, prevY);
      ctx.lineTo(currX, currY);
      ctx.stroke()
    
      currX = e.offsetX
      currY = e.offsetY
      ctx.strokeStyle = 'black'
      ctx.lineWidth = 1;
      ctx.beginPath();
      ctx.moveTo(prevX, prevY);
      ctx.lineTo(currX, currY);
      ctx.stroke()
    }
    
    let stopDrawing = e => {
      isDrawing = false
    }
    
    
    canvas.onmousedown = (e => {
      startDrawing(e)
    })
    canvas.onmousemove = (e => {
      Drawing(e)
    })
    canvas.onmouseup = (e => {
      stopDrawing(e)
    })
    
    backgroundSetUp()
    * {
      box-sizing: border-box;
    }
    
    body,
    html {
      padding: 0;
      margin: 0;
      height: 100vh;
      width: 100vw;
    }
    <canvas>
    
    </canvas>

    Alternatively, clear the canvas before drawing

    If there was anything else on the canvas, you will lose it unless you store the information and re-draw it.

    Why we need to erase with thicker lines and why XORing won’t work

    I have found this StackOverflow answer explaining why the erasing line needs to be wider. It also explains why we can’t do a clever trick with XOR to allow us to draw and remove a line, without disturbing anything that was already on the canvas.

    https://stackoverflow.com/a/16740592/7549483

    In short, when we draw, the browser does anti-aliasing, which is a complex process that is not easily reversible. That requires the erasing line to be wider, and prevents XORing doing what we hope it would.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search