Skip to content
Advertisement

Weird shape when drawing line on canvas (js)

I’m trying to make a very simple paint in JS but I have a problem when I increase the line width it create weird shape at the end of shape but not when I use a lineWidth = 1 and I don’t know where the problem come from furthermore it create space between lines while with a lineWidth=1 I don’t have that problem

without problem :
lineWidth = 1

with problem :
lineWidth = 15

this is my code :

class Board{
constructor(canvas) {
    this.canvas = canvas;
    this.ctx = this.canvas.getContext("2d");
    this.isDrawing = false;
    this.mousePosition = {
        x: 0,
        y: 0
    };
    this.color = "black";
    this.rect = this.canvas.getBoundingClientRect();
    this.lineWidth = 15;
    this.height = 0;
    this.width = 0;

    this.initBoard();
    // this.test();
    // this.checkIfFill = this.checkIfFill.bind(this)
}

initBoard() {
    this.renderCanvas();

    window.addEventListener("resize", () => {
        this.renderCanvas();
    });

    this.canvas.addEventListener("mousedown", (e) => {
        this.mousePosition = {
            x: e.clientX - this.rect.left,
            y: e.clientY - this.rect.top
        };

        this.isDrawing = true;
    });

    this.canvas.addEventListener("mousemove", (e) => {
        if (this.isDrawing) {
            this.draw((e.clientX - this.rect.left), (e.clientY - this.rect.top));
            this.mousePosition = {
                x: e.clientX - this.rect.left,
                y: e.clientY - this.rect.top
            };
        }
    });

    this.canvas.addEventListener("mouseup", () => {
        this.isDrawing = false;
    });
}

renderCanvas() {
    const drawingTools = document.querySelector(".drawing-tools");
    const dpr = window.devicePixelRatio;
    this.height = document.body.offsetHeight - drawingTools.offsetHeight;
   this.width = document.body.offsetWidth;

    this.canvas.style.height = this.height + "px";
    this.canvas.style.width = this.width + "px";
    this.canvas.setAttribute("width", (this.width*dpr) + "px");
    this.canvas.setAttribute("height", (this.height*dpr) + "px");

    this.ctx.scale(dpr, dpr);
}

draw(x, y) {
    this.ctx.strokeStyle = this.color;
    this.ctx.lineWidth = this.lineWidth;
    this.ctx.beginPath();
    this.ctx.moveTo(this.mousePosition.x, this.mousePosition.y);
    this.ctx.lineTo(x, y);
    this.ctx.stroke()
    // this.ctx.closePath();
}

setColor(color) {
    this.color = color;
}

setLineWidth(width) {
    this.lineWidth = width;
}

reset() {
    this.ctx.clearRect(0,0, this.canvas.width, this.canvas.height)
}

test() {
    this.ctx.strokeStyle = this.color;
    this.ctx.lineWidth = this.lineWidth;
    this.ctx.beginPath();
    this.ctx.moveTo(100, 100);
    this.ctx.lineTo(150, 100);
    this.ctx.stroke()
}

checkIfFill() {
    
    this.canvas.addEventListener("mousemove", (e) => {
        const x = e.clientX - this.rect.left;
        const y = e.clientY - this.rect.top;

        console.log(this.ctx.getImageData(x, y, 1, 1).data)
    })
}

}

Advertisement

Answer

This happens because of two things:

  1. Inside your draw() function you’re setting the starting point and the end point with every call. Usually you determine the start as soon as the user pushes the mousebutton – once.
  2. even with #1 fixed, the line end might still look a bit ‘fuzzy’. This can be fixed by setting the context’s lineCap style to round instead of it’s default butt – which squares of line endpoints.

Here’s an example based on your code (just click ‘Run code snippet’):

class Board {
  constructor(canvas) {
    this.canvas = canvas;
    this.ctx = this.canvas.getContext("2d");
    this.isDrawing = false;
    this.mousePosition = {
      x: 0,
      y: 0
    };
    this.color = "black";
    this.rect = this.canvas.getBoundingClientRect();
    this.lineWidth = 16;
    this.height = 0;
    this.width = 0;

    this.initBoard();
    // this.test();
    // this.checkIfFill = this.checkIfFill.bind(this)
  }

  initBoard() {
    this.renderCanvas();

    window.addEventListener("resize", () => {
      this.renderCanvas();
    });

    this.canvas.addEventListener("mousedown", (e) => {
      this.mousePosition = {
        x: e.clientX - this.rect.left,
        y: e.clientY - this.rect.top
      };
      this.ctx.beginPath();
      this.ctx.moveTo(this.mousePosition.x, this.mousePosition.y);
      this.isDrawing = true;
    });

    this.canvas.addEventListener("mousemove", (e) => {
      if (this.isDrawing) {
        this.draw((e.clientX - this.rect.left), (e.clientY - this.rect.top));
        this.mousePosition = {
          x: e.clientX - this.rect.left,
          y: e.clientY - this.rect.top
        };
      }
    });

    this.canvas.addEventListener("mouseup", () => {
      this.isDrawing = false;
    });
  }

  renderCanvas() {
    //    const drawingTools = document.querySelector(".drawing-tools");
    const dpr = window.devicePixelRatio;
    this.height = document.body.offsetHeight;
    this.width = document.body.offsetWidth;

    this.canvas.style.height = this.height + "px";
    this.canvas.style.width = this.width + "px";
    this.canvas.setAttribute("width", (this.width * dpr) + "px");
    this.canvas.setAttribute("height", (this.height * dpr) + "px");

    this.ctx.scale(dpr, dpr);
  }

  draw(x, y) {
    this.ctx.strokeStyle = this.color;
    this.ctx.lineWidth = this.lineWidth;
    this.ctx.lineCap = 'round';

    this.ctx.lineTo(x, y);
    this.ctx.stroke()

  }

  setColor(color) {
    this.color = color;
  }

  setLineWidth(width) {
    this.lineWidth = width;
  }

  reset() {
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
  }

  test() {
    this.ctx.strokeStyle = this.color;
    this.ctx.lineWidth = this.lineWidth;
    this.ctx.beginPath();
    this.ctx.moveTo(100, 100);
    this.ctx.lineTo(150, 100);
    this.ctx.stroke()
  }

  checkIfFill() {

    this.canvas.addEventListener("mousemove", (e) => {
      const x = e.clientX - this.rect.left;
      const y = e.clientY - this.rect.top;
    });
  }
}
var b = new Board(document.getElementById("canvas"));
<canvas id="canvas" width=400 height=300></canvas>
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement