maudulus
maudulus

Reputation: 11035

Draw a line with two different sized ends

How can I have a line be two different sizes with canvas?

I have a line I am drawing with canvas that I would like to start out with a width of 30 and gradually(proportionally) reduce down to a size of 15, so that it reaches 15 right at the end of the line.

I though that perhaps if I set context.lineWidth in two places (start and end) it would work.

<!DOCTYPE HTML>
<html>
  <head>
    <style>
      body {
        margin: 0px;
        padding: 0px;
      }
    </style>
  </head>
  <body>
    <canvas id="myCanvas" width="578" height="200"></canvas>
    <script>
      var canvas = document.getElementById('myCanvas');
      var context = canvas.getContext('2d');

      context.beginPath();
      context.moveTo(100, 150);
      context.lineWidth = 30;
      context.lineTo(450, 50);
      context.lineWidth = 15;
      context.stroke();
    </script>
  </body>
</html>

Upvotes: 2

Views: 207

Answers (1)

GameAlchemist
GameAlchemist

Reputation: 19294

I once wondered about building such a variable-width line, and i ended building my own solution, and wrote a blog post out of it.
I'll copy the first part of it here, the rounded version can also be found here : https://gamealchemist.wordpress.com/2013/08/28/variable-width-lines-in-html5-canvas/


Variable width lines in html5

Drawing such a [variable width] line is quite easy once we realize that what we need to draw is not a line : in fact it is a polygon.

If the line segment we want to draw is (A,B), the situation looks like this :

enter image description here

What we want to draw in fact is the A1,A2,B2,B1 polygon.

If we call N the normal vector (drawn on the scheme), and w1 and w2 the width in A and B respectively, we have :
A1 = A + N * w1/2
A2 = A – N * w1/2
B1 = B + N * w2/2
B2 = B – N * w2/2

So how do we find this normal vector N ?
Maths says that if (x,y) defines a vector V , its normal vector coordinates are (-y, x).
N, the vector normal to AB will hence have ( – ( yB – yA ) , ( xB – xA ) ) as coordinates.
But there is an annoying thing about this vector : it depends on AB length, which is not what we want : we need to normalize this vector, i.e. have it to a standard length of 1, so when we later multiply this vector by w1/2, we get the right length vector added.

Vector normalisation is done by dividing the x and y of the vector by the vector length. Since the length is found using phytagore’s theorem, that makes 2 squares, one square root, and finally 2 divides to find the normalized vector N :

// computing the normalized vector normal to AB
length = Math.sqrt( sq (xB-xA) + sq (yB - yA) ) ;
Nx     =  -  (yB - yA) / length ;
Ny     =     (xB - xA) / length ;  

So now that we can compute the four points, let us link them by a poly-line, and fill the resulting shape : here comes our variable width segment !

Here is the javascript code :

// varLine : draws a line from A(x1,y1) to B(x2,y2)
// that starts with a w1 width and ends with a w2 width.
// relies on fillStyle for its color.
// ctx is a valid canvas's context2d.
function varLine(ctx, x1, y1, x2, y2, w1, w2) {
    var dx = (x2 - x1);
    var dy = (y2 - y1);
    w1 /= 2;  w2 /= 2; // we only use w1/2 and w2/2 for computations.
    // length of the AB vector
    var length = Math.sqrt(sq(dx) + sq(dy));
    if (!length) return; // exit if zero length
    dx /= length ;    dy /= length ;
    var shiftx = - dy * w1   // compute AA1 vector's x
    var shifty =   dx * w1   // compute AA1 vector's y
    ctx.beginPath();
    ctx.moveTo(x1 + shiftx, y1 + shifty);
    ctx.lineTo(x1 - shiftx, y1 - shifty); // draw A1A2
    shiftx =  - dy * w2 ;   // compute BB1 vector's x
    shifty =    dx * w2 ;   // compute BB1 vector's y
    ctx.lineTo(x2 - shiftx, y2 - shifty); // draw A2B1
    ctx.lineTo(x2 + shiftx, y2 + shifty); // draw B1B2
    ctx.closePath(); // draw B2A1
    ctx.fill();    
}

So let us see the result on a small example : drawing variable width segments within a circle with nice hsl colors :

enter image description here

(About @MarkE's (interesting) remark on chaining line segments, i fear this is a quite difficult goal, since there are many specific cases depending on line length/ w1 /w2 / angle in between segments. I quite solved it using force fields and marching cubes, but i fear this is completely off-topic !! :-) )

Upvotes: 4

Related Questions