我想在html5画布中绘制一个椭圆,我在stackoverflow找到了一个很好的方法。但我有另一个问题。
function drawEllipse(ctx, x, y, w, h) {
var kappa = 0.5522848;
ox = (w / 2) * kappa, // control point offset horizontal
oy = (h / 2) * kappa, // control point offset vertical
xe = x + w, // x-end
ye = y + h, // y-end
xm = x + w / 2, // x-middle
ym = y + h / 2; // y-middle
ctx.beginPath();
ctx.moveTo(x, ym);
ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
ctx.closePath();
ctx.stroke();
}
上面链接中的方法使用bezierCurveTo绘制一个椭圆,但它绘制了bezierCurveTo 4 次。但我认为只需 2 bezierCurve就可以绘制一个椭圆形。就像这样:
但是我的数学很弱,有人能告诉我“控制点”和“椭圆点”的关系吗?或者我们必须绘制四条贝塞尔曲线来画椭圆形?
谢谢大家
答案 0 :(得分:4)
我的背景不是数学,所以如果我错了,我确定有人会纠正我,但根据我的理解,我们可以画出相当不错的近似只有两条立方贝塞尔曲线的椭圆但坐标会有点棘手。
要回答关于椭圆点和控制点之间关系的问题,我认为最好通过观察 this video 从我选择的点开始,如果您&# 39;熟悉插值或从头开始,如果不是。不要担心它很短。
我们可能遇到的一个问题是,当我们从顶部开始并做一个bezierCurve到椭圆的底部时,矩形的角(宽度和高度相同)与控制点相同,椭圆宽度将小于矩形。 .75倍我们想要的尺寸。所以我们可以相应地缩放控制点。
我们的控制点的x会像这样调整(假设宽度是椭圆的宽度,我们除以2得到它与原点的偏移量)
var cpx = (width / .75) / 2;
Put together a visualization您可以在其中播放宽度和高度,并查看绘制的椭圆。
红色椭圆是我们想要绘制它的方式,如果我们没有重新定位控制点,那么内部的椭圆是如何绘制的。这些线条说明了视频中显示的De Casteljau算法。
这是可视化的屏幕截图
答案 1 :(得分:2)
您只需要两条三次贝塞尔曲线来绘制椭圆。这是DerekR代码的简化版本,它使用您提供的原始函数参数 - 假设您希望x和y成为椭圆的中心:
function drawEllipse(ctx, x, y, w, h) {
var width_over_2 = w / 2;
var width_two_thirds = w * 2 / 3;
var height_over_2 = h / 2;
ctx.beginPath();
ctx.moveTo(x, y - height_over_2);
ctx.bezierCurveTo(x + width_two_thirds, y - height_over_2, x + width_two_thirds, y + height_over_2, x, y + height_over_2);
ctx.bezierCurveTo(x - width_two_thirds, y + height_over_2, x - width_two_thirds, y - height_over_2, x, y -height_over_2);
ctx.closePath();
ctx.stroke();
}
答案 2 :(得分:2)
非常感谢BKH。 我使用他的代码和两条贝塞尔曲线完成我的椭圆绘图与任何旋转角度。另外,我在贝塞尔曲线和原始椭圆()函数绘制的椭圆之间创建了一个比较demo(现在仅在Chrome中实现)。
function drawEllipseByBezierCurves(ctx, x, y, radiusX, radiusY, rotationAngle) {
var width_two_thirds = radiusX * 4 / 3;
var dx1 = Math.sin(rotationAngle) * radiusY;
var dy1 = Math.cos(rotationAngle) * radiusY;
var dx2 = Math.cos(rotationAngle) * width_two_thirds;
var dy2 = Math.sin(rotationAngle) * width_two_thirds;
var topCenterX = x - dx1;
var topCenterY = y + dy1;
var topRightX = topCenterX + dx2;
var topRightY = topCenterY + dy2;
var topLeftX = topCenterX - dx2;
var topLeftY = topCenterY - dy2;
var bottomCenterX = x + dx1;
var bottomCenterY = y - dy1;
var bottomRightX = bottomCenterX + dx2;
var bottomRightY = bottomCenterY + dy2;
var bottomLeftX = bottomCenterX - dx2;
var bottomLeftY = bottomCenterY - dy2;
ctx.beginPath();
ctx.moveTo(bottomCenterX, bottomCenterY);
ctx.bezierCurveTo(bottomRightX, bottomRightY, topRightX, topRightY, topCenterX, topCenterY);
ctx.bezierCurveTo(topLeftX, topLeftY, bottomLeftX, bottomLeftY, bottomCenterX, bottomCenterY);
ctx.closePath();
ctx.stroke();
}
答案 3 :(得分:1)
你会发现这在http://pomax.github.io/bezierinfo/#circles_cubic中稍微更基于数学解释,但要点是使用立方贝塞尔曲线超过四分之一转通常不是一个好主意。值得庆幸的是,使用四条曲线可以轻松找到所需的控制点。从圆开始,在这种情况下,每个四分之一圆为(1,0) - (1,0.55228) - (0.55228,1) - (0,1),椭圆的缩放坐标。绘制四次,交换+/-符号以实现整圆,缩放尺寸以获得椭圆,并完成。
如果使用两条曲线,坐标将变为(1,0) - (1,4 / 3) - ( - 1,4 / 3) - ( - 1,0),缩放为椭圆。在你的应用程序中它可能仍然看起来不错,它取决于你的绘图最终有多大。
答案 4 :(得分:0)
可以在数学上证明,不能用任何程度的贝塞尔曲线制作圆。您可以通过近似来制作“几乎圆”。
假设你想在[0,0]周围绘制四分之一圆。立方贝塞尔坐标是:
[0 , 1 ]
[0.55, 1 ]
[1 , 0.55]
[1 , 0 ]
这是一个非常好的近似值。线性转换以获得椭圆形。