How to draw lines on canvas
I want to draw some lines inside a circle on canvas in the following way.
I don't know how to draw lines like below. But I have basic knowledge of drawing lines and arcs on canvas. how to proceed?
You could use a bezier curve with control points as suggested in the comments, however, since those control points cannot pass through the points you define, and you always need to define two control points, it's hard to control (no pun intended).
In order to implement a line through points using actual points , you need to use a cardinal spline .
They don't have built-in support, but for a while I implemented JavaScript and canvas ( code can be downloaded from here under the MIT license ).
This way you can simply define three points as the minimum value (to get a simple curve) and the function will take care of drawing a smooth curve between the points where the tension value is set.
For example, if you define the following three points:
var pts = [10,100, 200,50, 390,100];
If we want to illustrate the points (for comparison), obviously you get a simple polyline :
Use a cardinal spline with the same three pointswill provide you with :
The following code produces the above curve (without the red dots showing the point coordinates):
ctx.beginPath();
ctx.curve(pts);
ctx.stroke();
Now it is simply a matter of moving the points around (in particular the center point) and the curve will adopt. Adding a tension slider for the user can be an advantage:
Increasing the tension to for example 0.8 give you this result:
ctx.curve(pts, 0.8);
and lowering it to for example 0.3 will reduce the smoothness to this:
ctx.curve(pts, 0.3);
There are also other parameters (see the link at top for documentation) and you can have "unlimited" number of points in the point array in case you want to add super-fine control.
The implementation extends the canvas context but you can extract the method and use it separately if you are faint at heart. :-)
Implementing this for a circle
I hope I am interpreting your drawing correctly here... to use the above for a circle you would simply need to do the following:
- Define the range, the side of the circle you want the lines to be drawn into.
- Define the steps, ie. the space between each line.
Lets say you wanted to draw lines between -70° and 70° and maximum 5 lines you could do something like this:
var ctx = canvas.getContext('2d'),
cx = canvas.width * 0.5,
cy = canvas.height * 0.5,
pts,
startAngle = -70,
endAngle = 70,
lines = 5,
angle,
range,
steps,
radius = 90,
delta = 15,
x, y,
i;
ctx.lineWidth = 3;
ctx.strokeStyle = '#059';
/// draw arc
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, 2 * Math.PI);
ctx.stroke();
/// calculate angle range normalized to 0 degrees
startAngle = startAngle * Math.PI / 180;
endAngle = endAngle * Math.PI / 180;
range = endAngle - startAngle;
steps = range / (lines + 1);
/// calculate point at circle (vertical only)
for(i = 1; i <= lines; i++) {
pts = [];
/// right side
x = cx + radius * Math.cos(startAngle + steps * i);
y = cy + radius * Math.sin(startAngle + steps * i);
pts.push(x, y);
/// center
pts.push(cx, y + delta * ((y - cy)/ cy));
/// flip for left side
x = cx - (x - cx);
pts.push(x, y);
/// draw curve
ctx.beginPath();
ctx.curve(pts, 0.8);
ctx.stroke();
}
Which would result in this:
It's now just a matter of playing around with the values (delta for example) and to calculate the horizontal row - I'll leave that as an exercise for the OP:
Using ellipse side to draw the lines
That being said - if you intended the globe to be more um, circular :-S, you could also have used a function to calculate part of an ellipse and draw that as lines instead. If would be about the same implementation as above here but with a sub function to calculate the ellipse between left and right side using the difference between the line and middle point as radius.
For example:
/// calculate point at circle (vertical only)
for(i = 1; i <= lines; i++) {
pts = [];
/// right side
x = cx + radius * Math.cos(startAngle + steps * i);
y = cy + radius * Math.sin(startAngle + steps * i);
pts.push(cx - radius, cy);
pts.push(cx, y);
pts.push(cx + radius, cy);
/// draw ellipse side
ctx.beginPath();
drawEllipseSide(pts, true);
ctx.stroke();
}
Then in the method (only vertical shown):
function drawEllipseSide(pts, horizontal) {
var radiusX,
radiusY,
cx, cy,
x, y,
startAngle,
endAngle,
steps = Math.PI * 0.01,
i = 0;
if (horizontal) {
radiusX = Math.abs(pts[4] - pts[0]) * 0.5;
radiusY = pts[3] - pts[1];
cx = pts[2];
cy = pts[1];
startAngle = 0;
endAngle = Math.PI;
x = cx + radiusX * Math.cos(startAngle);
y = cy + radiusY * Math.sin(startAngle);
ctx.moveTo(x, y);
for(i = startAngle + steps; i < endAngle; i += steps) {
x = cx + radiusX * Math.cos(i);
y = cy + radiusY * Math.sin(i);
ctx.lineTo(x, y);
}
}
}
Resulting in this (I cheat a bit in the final drawing to give a more clear picture (no pun) of what the end result will be if you continue down these lines (no pun either, I am pun-cursed) given here):
My code-OCD kicked in :-P but you should at least have a few options here to go with. Study the code to see how the vertical lines are calculated and adopt that for horizontal ones.
Hope this helps!