您可以使用带有控制点的 cmets 中建议的贝塞尔曲线,但是,这些可能会变得难以控制(没有双关语),因为它们不会通过您定义的点并且您总是需要定义两个控制点。
为了使用实际点实现通过点的线,您需要使用基本样条线。
没有对这些的内置支持,但不久前我为 JavaScript 和画布实现了这个(代码可以下载from here,MIT 许可证)。
有了这个,您可以简单地将三个点定义为最小值(以获得简单的曲线),该函数将负责在具有设定张力值的点之间绘制平滑曲线。
如果你例如定义了以下三点:
var pts = [10,100, 200,50, 390,100];
如果我们想说明要点(用于比较),您显然只会得到simple poly-line like this:
使用具有相同三个点的基数样条 would give you this:
以下代码生成上述曲线(没有红点表示点坐标):
ctx.beginPath();
ctx.curve(pts);
ctx.stroke();
现在只需移动点(特别是中心点),曲线就会采用。为用户添加张力滑块可能是一个优势:
Increasing the tension 到例如 0.8 给你这个结果:
ctx.curve(pts, 0.8);
和lowering it 到例如 0.3 会降低平滑度:
ctx.curve(pts, 0.3);
还有其他参数(参见文档顶部的链接),如果您想添加超精细控制,您可以在点数组中拥有“无限”数量的点。
该实现扩展了画布上下文,但如果您胆怯,您可以提取该方法并单独使用它。 :-)
为一个圈子实现这个
我希望我在这里正确解释了您的绘图...要将上述内容用于圆圈,您只需执行以下操作:
- 定义范围,即您希望将线条绘制到的圆的一侧。
- 定义步骤,即。每行之间的间距。
假设您想在 -70° 和 70° 之间画线,最多可以画 5 条线,您可以这样做:
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();
}
这会导致:
Fiddle here
现在只需使用值(例如 delta)并计算水平行 - 我将把它作为 OP 的练习:
使用椭圆边画线
话虽这么说 - 如果您希望地球更圆,更圆:-S,您也可以使用函数来计算椭圆的一部分并将其绘制为线条。 If 将与上面的实现大致相同,但带有一个子函数,使用直线和中点之间的差作为半径来计算左侧和右侧之间的椭圆。
例如:
/// 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();
}
然后在方法中(仅垂直显示):
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);
}
}
}
导致这一点(我在最终图纸中作弊,以更清楚地说明(没有双关语)如果你继续沿着这些线(也没有双关语,我被诅咒)最终结果将是什么这里):
Fiddle here
我的代码强迫症开始了 :-P 但你至少应该有一些选择。研究代码以了解如何计算垂直线并将其用于水平线。
希望这会有所帮助!