javascripthtmlhtml5-canvas

When using ctx.moveTo() with Html Canvas Tag ctx.arc(), moveTo() draws a line


For the purpose of improving myself and because I was bored, I was learning the canvas tags in html by following the doc of mozilla and we were drawing a face there. I tried to make the face as given by mozilla, but lines appeared on my face due to the values and I don't know why.

const canvas = document.querySelector(".canvas");
const ctx = canvas.getContext("2d")

ctx.beginPath()
ctx.arc(75, 75, 60, 0, Math.PI * 2, true)
ctx.moveTo(40, 40)
ctx.arc(50, 50, 10, 0, Math.PI * 2, true)
ctx.moveTo(60, 50)
ctx.arc(100, 50, 10, 0, Math.PI * 2, true)
ctx.moveTo(110, 110)
ctx.arc(80, 110, 30, 0, Math.PI, true)
ctx.stroke()
<canvas class="canvas" width="150" height="150"></canvas>

Expected face:

expected face screenshot

Result face:

result face

As you can see, I'm talking about unwanted lines on the face.

All I know and discovered is that for some reason the x value of the moveTo should be as much as the radius of the circle and the y values should be the same - but I don't know why. I wanted to ask you why this is the case.

In summary, I haven't figured out why, but for some reason, when I play with the values or when I make the values incompatible, a line appears.


Solution

  • So to draw the face, you need to remove the moveTo() calls and wrap each arc() in a beginPath() and stroke() call. See this first snippet.

    const canvas = document.querySelector(".canvas");
    const ctx = canvas.getContext("2d")
    
    ctx.beginPath()
    ctx.arc(75, 75, 60, 0, Math.PI * 2, true)
    ctx.stroke()
    
    ctx.beginPath()
    ctx.arc(50, 50, 10, 0, Math.PI * 2, true)
    ctx.stroke()
    
    ctx.beginPath()
    ctx.arc(100, 50, 10, 0, Math.PI * 2, true)
    ctx.stroke()
    
    ctx.beginPath()
    // EDIT Change x and y slightly
    ctx.arc(75, 90, 30, 0, Math.PI, false)
    ctx.stroke()
    canvas {
      box-shadow: 2px 2px 5px salmon;
    }
    <canvas class="canvas" width="300" height="180"></canvas>

    In this second snippet is your code with the moveTo() coordinates circled in red. You can see that lines are drawn from the moveTo() coordinates to the beginning of the arc's path segment.

    const canvas = document.querySelector(".canvas");
    const ctx    = canvas.getContext("2d");
    
    ctx.beginPath()
    ctx.arc(75, 75, 60, 0, Math.PI * 2, true)
    ctx.moveTo(40, 40)
    ctx.arc(50, 50, 10, 0, Math.PI * 2, true)
    ctx.moveTo(60, 50)
    ctx.arc(100, 50, 10, 0, Math.PI * 2, true)
    ctx.moveTo(110, 110)
    ctx.arc(80, 110, 30, 0, Math.PI, true)
    ctx.stroke()
    
    // Draw a dot at all of the moveTo() coordinates
    dot(40, 40);
    dot(60, 50);
    dot(110, 110);
    
    
    function dot(x, y) {
      ctx.strokeStyle = "red"
      ctx.beginPath();
      ctx.arc(x, y, 4, 0, Math.PI * 2)
      ctx.stroke();
    }
    <canvas class="canvas" width="300" height="180"></canvas>

    In this third snippet the original mozilla coordinates are used with moveTo() coordinates circled in red. You don't see any straight lines because the "lines" are 0 length, since the moveTo() coordinates are the same as the location where the arc's start their path segment.

    const canvas = document.querySelector(".canvas");
    const ctx = canvas.getContext("2d");
    
    ctx.beginPath();
    ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // Outer circle
    ctx.moveTo(110, 75);
    ctx.arc(75, 75, 35, 0, Math.PI, false); // Mouth (clockwise)
    ctx.moveTo(65, 65);
    ctx.arc(60, 65, 5, 0, Math.PI * 2, true); // Left eye
    ctx.moveTo(95, 65);
    ctx.arc(90, 65, 5, 0, Math.PI * 2, true); // Right eye
    ctx.stroke();
    
    // Draw a dot at all of the moveTo() coordinates
    dot(110, 75);
    dot(65, 65);
    dot(95, 65);
    
    function dot(x, y) {
      ctx.strokeStyle = "red"
      ctx.beginPath();
      ctx.arc(x, y, 4, 0, Math.PI * 2)
      ctx.stroke();
    }
    <canvas class="canvas" width="300" height="180"></canvas>