jdogdvr
jdogdvr

Reputation: 369

HTML 5 Canvas drawing a straight smooth line example?

I got this canvas drawing app example and I need help understanding certain parts of the code. The app draws a really smooth line just like a pen drawing tool.

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

var radius = 10;
var dragging = false;


canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context.lineWidth = radius*2;

var putPoint = function(e){
  if(dragging){
    context.lineTo(e.clientX, e.clientY);
    context.stroke();
    context.beginPath();
    context.arc(e.clientX, e.clientY, radius, 0, Math.PI*2);
    context.fill();
    context.beginPath();
    context.moveTo(e.clientX, e.clientY);
  }
}

var engage = function(e){
  dragging = true;
  putPoint(e);
}

var disengage = function(){
  dragging = false;
  context.beginPath();
}

canvas.addEventListener('mousedown', engage);
canvas.addEventListener('mousemove', putPoint);
canvas.addEventListener('mouseup', disengage);
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Canvas</title>
</head>
<body style="margin:0">
  <canvas style="display:block;" id="canvas">
    sorry this browser does not support HTML 5 Canvas!
  </canvas>
  <script src="main.js"></script>
</body>
</html>

I would like to draw attention to this part of the JS code which I am having trouble understanding:

var putPoint = function(e){
  if(dragging){
    context.lineTo(e.clientX, e.clientY);
    context.stroke();
    context.beginPath();
    context.arc(e.clientX, e.clientY, radius, 0, Math.PI*2);
    context.fill();
    context.beginPath();
    context.moveTo(e.clientX, e.clientY);
  }
}

I just don't understand how parts of that code relate to each other and ultimately creates a really smooth line.

Breaking that code down:

context.lineTo(e.clientX, e.clientY);
context.stroke();

This piece of code is straightforward as it connects the previous coordinate with a line. However the line is not smooth and looks rough.

context.beginPath();
context.arc(e.clientX, e.clientY, radius, 0, Math.PI*2);
context.fill();

This part just prints a circle but if I move my mouse really quickly the circles don't connect to each other.

context.beginPath();
context.moveTo(e.clientX, e.clientY);

This is the final part of the code that makes drawing a line really smooth and efficient. This part of the code I don't understand and how it relates in any way.

Please explain the code logic behind the whole function and how it relates to each.

Thanks

Upvotes: 0

Views: 1292

Answers (1)

Marco Bonelli
Marco Bonelli

Reputation: 69276

The concept in that function is simple: to draw a "smooth" line you basically have to first draw a normal "rough" line, and then end it with a circle.

Breaking it down, you are doing this:

  1. Draw a "rough" line:

    context.lineTo(e.clientX, e.clientY);
    context.stroke();
    

    Which would look like this:

    line

  2. Add a circle to make the line "smooth":

    context.beginPath();
    context.arc(e.clientX, e.clientY, radius, 0, Math.PI*2);
    context.fill();
    

    Which transforms the previous line in something like this:

    smooth line

    Here you might wonder: you said "end it with a circle", so why is there a circle at the beginning of the line? Well, actually there are way more circles. Every time the function is called, the line is drawn from the previous known position of the mouse to the next one, so at the beginning the line is drawn with a length of zero, and a circle is made. Then the mouse continues moving, and more very small segments are added with circles at the end of them. Drawing down all those (almost infinite) small segments ending with circles creates a nice straight line.

    This also means that the smoothness of the line only depends on the resolution at which it's drawn. The more times per second the function is called and the slower the mouse goes, the smoother the line will be.

  3. Move the current position of the path to the mouse position

    context.beginPath();
    context.moveTo(e.clientX, e.clientY);
    

    These two lines just make sure the next time the function is called, the beginning point will already be set.

Upvotes: 1

Related Questions