Javascript 如何在html5画布中绘制椭圆形?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2172798/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to draw an oval in html5 canvas?
提问by Tammo
There doesnt seem to be a native function to draw an oval-like shape. Also i am not looking for the egg-shape.
似乎没有绘制椭圆形形状的本机函数。我也不是在寻找鸡蛋形状。
Is it possible to draw an oval with 2 bezier curves? Somebody expierenced with that?
是否可以用两条贝塞尔曲线绘制椭圆?有人经历过吗?
My purpose is to draw some eyes and actually im just using arcs. Thanks in advance.
我的目的是画一些眼睛,实际上我只是使用弧线。提前致谢。
Solution
解决方案
So scale() changes the scaling for all next shapes. Save() saves the settings before and restore is used to restore the settings to draw new shapes without scaling.
所以 scale() 改变了所有下一个形状的缩放比例。Save() 保存之前的设置,restore 用于恢复设置以在不缩放的情况下绘制新形状。
Thanks to Jani
感谢贾尼
ctx.save();
ctx.scale(0.75, 1);
ctx.beginPath();
ctx.arc(20, 21, 10, 0, Math.PI*2, false);
ctx.stroke();
ctx.closePath();
ctx.restore();
回答by Steve Tranby
updates:
更新:
- scaling method can affect stroke width appearance
- scaling method done right can keep stroke width intact
- canvas has ellipse method that Chrome now supports
- added updated tests to JSBin
- 缩放方法会影响笔画宽度外观
- 正确的缩放方法可以保持笔画宽度不变
- 画布具有 Chrome 现在支持的椭圆方法
- 向 JSBin 添加了更新的测试
JSBin Testing Example(updated to test other's answers for comparison)
JSBin 测试示例(更新以测试其他人的答案以进行比较)
- Bezier - draw based on top left containing rect and width/height
- Bezier with Center - draw based on center and width/height
- Arcs and Scaling - draw based on drawing circle and scaling
- see Deven Kalra'sanswer
- Quadratic Curves - draw with quadratics
- test appears to not draw quite the same, may be implementation
- see oyophant'sanswer
- Canvas Ellipse - using W3C standard ellipse() method
- test appears to not draw quite the same, may be implementation
- see Loktar'sanswer
- Bezier - 基于包含矩形和宽度/高度的左上角绘制
- Bezier with Center - 基于中心和宽度/高度绘制
- Arcs and Scaling - 基于绘制圆和缩放比例绘制
- 二次曲线 - 用二次曲线绘制
- 测试似乎不太一样,可能是实现
- 见oyophant的回答
- Canvas Ellipse - 使用 W3C 标准 ellipse() 方法
- 测试似乎不太一样,可能是实现
- 看Loktar 的回答
Original:
原来的:
If you want a symmetrical oval you could always create a circle of radius width, and then scale it to the height you want (edit:notice this will affect stroke width appearance - see acdameli's answer), but if you want full control of the ellipse here's one way using bezier curves.
如果你想要一个对称的椭圆,你总是可以创建一个半径宽度的圆,然后将它缩放到你想要的高度(编辑:注意这会影响笔画宽度的外观 - 请参阅 acdameli 的答案),但如果你想完全控制椭圆这是使用贝塞尔曲线的一种方法。
<canvas id="thecanvas" width="400" height="400"></canvas>
<script>
var canvas = document.getElementById('thecanvas');
if(canvas.getContext)
{
var ctx = canvas.getContext('2d');
drawEllipse(ctx, 10, 10, 100, 60);
drawEllipseByCenter(ctx, 60,40,20,10);
}
function drawEllipseByCenter(ctx, cx, cy, w, h) {
drawEllipse(ctx, cx - w/2.0, cy - h/2.0, w, h);
}
function drawEllipse(ctx, x, y, w, h) {
var kappa = .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(); // not used correctly, see comments (use to close off open path)
ctx.stroke();
}
</script>
回答by Deven Kalra
Here is a simplified version of solutions elsewhere. I draw a canonical circle, translate and scale and then stroke.
这是其他地方解决方案的简化版本。我画了一个规范圆,翻译和缩放,然后描边。
function ellipse(context, cx, cy, rx, ry){
context.save(); // save state
context.beginPath();
context.translate(cx-rx, cy-ry);
context.scale(rx, ry);
context.arc(1, 1, 1, 0, 2 * Math.PI, false);
context.restore(); // restore to original state
context.stroke();
}
回答by Andrew Staroscik
The bezier curve approach is great for simple ovals. For more control, you can use a loop to draw an ellipse with different values for the x and y radius (radiuses, radii?).
贝塞尔曲线方法非常适合简单的椭圆。为了获得更多控制,您可以使用循环绘制具有不同 x 和 y 半径值(半径、半径?)的椭圆。
Adding a rotationAngle parameter allows the oval to be rotated around its center by any angle. Partial ovals can be drawn by changing the range (var i) over which the loop runs.
添加 rotationAngle 参数允许椭圆围绕其中心旋转任意角度。可以通过更改循环运行的范围 (var i) 来绘制部分椭圆。
Rendering the oval this way allows you to determine the exact x,y location of all points on the line. This is useful if the postion of other objects depend on the location and orientation of the oval.
以这种方式渲染椭圆可以让您确定线上所有点的确切 x,y 位置。如果其他对象的位置取决于椭圆的位置和方向,这将很有用。
Here is an example of the code:
以下是代码示例:
for (var i = 0 * Math.PI; i < 2 * Math.PI; i += 0.01 ) {
xPos = centerX - (radiusX * Math.sin(i)) * Math.sin(rotationAngle * Math.PI) + (radiusY * Math.cos(i)) * Math.cos(rotationAngle * Math.PI);
yPos = centerY + (radiusY * Math.cos(i)) * Math.sin(rotationAngle * Math.PI) + (radiusX * Math.sin(i)) * Math.cos(rotationAngle * Math.PI);
if (i == 0) {
cxt.moveTo(xPos, yPos);
} else {
cxt.lineTo(xPos, yPos);
}
}
See an interactive example here: http://www.scienceprimer.com/draw-oval-html5-canvas
在此处查看交互式示例:http: //www.scienceprimer.com/draw-oval-html5-canvas
回答by Loktar
There is now a native ellipsefunction for canvas, very similar to the arc function although now we have two radius values and a rotation which is awesome.
现在有一个用于画布的原生椭圆函数,与 arc 函数非常相似,尽管现在我们有两个半径值和一个很棒的旋转。
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
椭圆(x,y,radiusX,radiusY,旋转,startAngle,endAngle,逆时针)
ctx.ellipse(100, 100, 10, 15, 0, 0, Math.PI*2);
ctx.fill();
Only seems to work in Chrome currently
目前似乎只能在 Chrome 中使用
回答by Alankar Misra
You need 4 bezier curves (and a magic number) to reliably reproduce an ellipse. See here:
您需要 4 条贝塞尔曲线(和一个幻数)才能可靠地再现椭圆。看这里:
www.tinaja.com/glib/ellipse4.pdf
www.tinaja.com/glib/ellipse4.pdf
Two beziers don't accurately reproduce an ellipse. To prove this, try some of the 2 bezier solutions above with equal height and width - they should ideally approximate a circle but they won't. They'll still look oval which goes to prove they aren't doing what they are supposed to.
两个贝塞尔曲线不能准确地再现椭圆。为了证明这一点,请尝试上面两个具有相同高度和宽度的贝塞尔解决方案中的一些 - 理想情况下它们应该近似于一个圆,但它们不会。他们仍然会看起来是椭圆形的,这证明他们没有做他们应该做的。
Here's something that should work:
这是应该起作用的东西:
Here's the code:
这是代码:
function ellipse(cx, cy, w, h){
var ctx = canvas.getContext('2d');
ctx.beginPath();
var lx = cx - w/2,
rx = cx + w/2,
ty = cy - h/2,
by = cy + h/2;
var magic = 0.551784;
var xmagic = magic*w/2;
var ymagic = h*magic/2;
ctx.moveTo(cx,ty);
ctx.bezierCurveTo(cx+xmagic,ty,rx,cy-ymagic,rx,cy);
ctx.bezierCurveTo(rx,cy+ymagic,cx+xmagic,by,cx,by);
ctx.bezierCurveTo(cx-xmagic,by,lx,cy+ymagic,lx,cy);
ctx.bezierCurveTo(lx,cy-ymagic,cx-xmagic,ty,cx,ty);
ctx.stroke();
}
回答by Jani Hartikainen
You could also try using non-uniform scaling. You can provide X and Y scaling, so simply set X or Y scaling larger than the other, and draw a circle, and you have an ellipse.
您也可以尝试使用非均匀缩放。您可以提供 X 和 Y 缩放,因此只需将 X 或 Y 缩放设置为比另一个更大,然后绘制一个圆,您就会得到一个椭圆。
回答by Girardi
I did a little adaptation of this code(partially presented by Andrew Staroscik) for peoplo who do not want a so general ellipse and who have only the greater semi-axis and the excentricity data of the ellipse (good for astronomical javascript toys to plot orbits, for instance).
我对这段代码做了一些改编(部分由 Andrew Staroscik 提供),适用于那些不想要一个如此一般的椭圆并且只有更大的半轴和椭圆的偏心数据的人(对于天文 javascript 玩具绘制轨道很有用) , 例如)。
Here you go, remembering that one can adapt the steps in ito have a greater precision in the drawing:
在这里,请记住,可以调整步骤i以在绘图中具有更高的精度:
/* draw ellipse
* x0,y0 = center of the ellipse
* a = greater semi-axis
* exc = ellipse excentricity (exc = 0 for circle, 0 < exc < 1 for ellipse, exc > 1 for hyperbole)
*/
function drawEllipse(ctx, x0, y0, a, exc, lineWidth, color)
{
x0 += a * exc;
var r = a * (1 - exc*exc)/(1 + exc),
x = x0 + r,
y = y0;
ctx.beginPath();
ctx.moveTo(x, y);
var i = 0.01 * Math.PI;
var twoPi = 2 * Math.PI;
while (i < twoPi) {
r = a * (1 - exc*exc)/(1 + exc * Math.cos(i));
x = x0 + r * Math.cos(i);
y = y0 + r * Math.sin(i);
ctx.lineTo(x, y);
i += 0.01;
}
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.closePath();
ctx.stroke();
}
回答by jaredwilli
My solution is a bit different than all of these. Closest I think is the most voted answer above though, but I think this way is a bit cleaner and easier to comprehend.
我的解决方案与所有这些都有些不同。我认为最接近的是上面投票最多的答案,但我认为这种方式更清晰,更容易理解。
http://jsfiddle.net/jaredwilli/CZeEG/4/
http://jsfiddle.net/jaredwilli/CZeEG/4/
function bezierCurve(centerX, centerY, width, height) {
con.beginPath();
con.moveTo(centerX, centerY - height / 2);
con.bezierCurveTo(
centerX + width / 2, centerY - height / 2,
centerX + width / 2, centerY + height / 2,
centerX, centerY + height / 2
);
con.bezierCurveTo(
centerX - width / 2, centerY + height / 2,
centerX - width / 2, centerY - height / 2,
centerX, centerY - height / 2
);
con.fillStyle = 'white';
con.fill();
con.closePath();
}
And then use it like this:
然后像这样使用它:
bezierCurve(x + 60, y + 75, 80, 130);
There are a couple use examples in the fiddle, along with a failed attempt to make one using quadraticCurveTo.
小提琴中有几个使用示例,以及使用 quadraticCurveTo 制作一个失败的尝试。
回答by acdameli
I like the Bezier curves solution above. I noticed the scale also affects the line width so if you're trying to draw an ellipse that is wider than it is tall, your top and bottom "sides" will appear thinner than your left and right "sides"...
我喜欢上面的贝塞尔曲线解决方案。我注意到比例也会影响线宽,所以如果你试图画一个比它高的椭圆,你的顶部和底部“边”会比你的左右“边”看起来更薄......
a good example would be:
一个很好的例子是:
ctx.lineWidth = 4;
ctx.scale(1, 0.5);
ctx.beginPath();
ctx.arc(20, 20, 10, 0, Math.PI * 2, false);
ctx.stroke();
you should notice the width of the line at the peak and valley of the ellipse are half as wide as at the left and right apexes (apices?).
您应该注意到椭圆峰和谷线的宽度是左右顶点(顶点?)的一半。
回答by tagawa
Yes, it is possible with two bezier curves - here's a brief tutorial/example: http://www.williammalone.com/briefs/how-to-draw-ellipse-html5-canvas/
是的,可以使用两条贝塞尔曲线 - 这是一个简短的教程/示例:http: //www.williammalone.com/briefs/how-to-draw-ellipse-html5-canvas/

