HTML5キャンバスで楕円を描く方法は?


81

楕円形を描く本来の機能はないようです。また、私は卵形を探していません。

2つのベジェ曲線で楕円を描くことは可能ですか?誰かがそれを経験しましたか?

私の目的は、いくつかの目を描き、実際には円弧を使用することです。前もって感謝します。

解決

したがって、scale()は、次のすべてのシェイプのスケーリングを変更します。Save()は前に設定を保存し、restoreを使用して設定を復元し、スケーリングせずに新しい形状を描画します。

ジャニに感謝します

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();

7
以下の回答の一部とは異なり、ベジェ曲線を使用して楕円を描くことはできません。楕円を多項式曲線で近似することはできますが、正確に再現することはできません。
ジョー

+1を付けましたが、これにより、円に沿って線が歪んでしまい、うまくいきません。でも良い情報。
mwilcox 2012年

1
@ mwilcox-restore()stroke()に置いても線が歪まない場合は、JohnathanHebertが以下のSteveTranbyの回答へのコメントで述べています。また、これは不要であり、の誤った使用ですclosePath()。このような誤解方法... stackoverflow.com/questions/10807230/...
ブライアンMcCutchon

回答:


114

更新:

  • スケーリング方法は、ストローク幅の外観に影響を与える可能性があります
  • 正しく行われたスケーリング方法は、ストローク幅をそのまま維持できます
  • キャンバスには、Chromeでサポートされるようになった楕円法があります
  • JSBinに更新されたテストを追加しました

JSBinテスト例(比較のために他の回答をテストするために更新)

  • ベジェ-長方形と幅/高さを含む左上に基づいて描画します
  • 中心のあるベジェ-中心と幅/高さに基づいて描画
  • 円弧とスケーリング-描画円とスケーリングに基づいて描画します
  • 二次曲線-二次で描く
    • テストはまったく同じようには描かれていないようです、実装かもしれません
    • oyophantの答えを参照してください
  • Canvas Ellipse-W3C標準のellipse()メソッドを使用
    • テストはまったく同じようには描かれていないようです、実装かもしれません
    • Loktarの答えを参照してください

元の:

対称的な楕円が必要な場合は、いつでも半径幅の円を作成し、それを必要な高さにスケーリングできます(編集:これはストローク幅の外観に影響することに注意してください-acdameliの回答を参照してください)が、楕円を完全に制御したい場合ベジェ曲線を使用する1つの方法があります。

<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>

1
これは、scaleストロークを歪めるのと同様に、最も一般的な解決策です(acdameliの回答を参照)。
トレバーバーナム

4
ctx.stroke()をctx.fill()に置き換えると、塗りつぶされた形状になります。
h3xStream 2011

13
規模が歪むストロークではないでしょうあなたがちょうど第1のスケール場合、は、元に縮小、アークまたは何を使用してパスに追加し、その後、ストローク、既存のパス
ジョナサンエベール

1
4 *(sqrt(2)-1)/ 3の概算で、Googleの計算で0.55228475となる多くの場所が見つかりました。すばやく検索すると、次の優れたリソースが得られます。whizkidtech.redprince.net
Steve

1
愚かな質問ではありません。楕円を囲む長方形を想像すると、x、yは左上隅であり、中心は点{x + w / 2.0、y + h / 2.0}になります
Steve Tranby 2013

45

これは、他の場所にあるソリューションの簡略版です。正規の円を描き、平行移動して拡大縮小してからストロークします。

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();
}

2
これはかなりエレガントです。でcontext重い物を持ち上げましょうscale
adu 2013年

1
ストロークの非常に重要な復元注意してください(そうしないと、ストローク幅が歪んでしまいます)
Jason S

まさに私が探していたもの、ありがとう!ベジェ法以外でも可能だとは思っていましたが、自分でスケールを動かすことができませんでした。

@JasonSストローク後に復元すると、なぜストローク幅が歪むのでしょうか。
OneMoreQuestion

17

キャンバスのネイティブ楕円関数があります。これはアーク関数と非常によく似ていますが、2つの半径値と素晴らしい回転があります。

ellipse(x、y、radiusX、radiusY、rotation、startAngle、endAngle、反時計回り)

ライブデモ

ctx.ellipse(100, 100, 10, 15, 0, 0, Math.PI*2);
ctx.fill();

現在Chromeでのみ動作するようです


2
ええ、これの実装は遅いです。

@Epistemex同意しました。私はそれが時間とともに速くなることを想像/願っています。
Loktar 2014年

developer.mozilla.org/en-US/docs/Web/API/…によると、Operaでもサポートされています
jazzpi 2014

16

ベジェ曲線アプローチは、単純な楕円に最適です。さらに制御するために、ループを使用して、x半径とy半径(半径、半径?)の値が異なる楕円を描画できます。

rotationAngleパラメータを追加すると、楕円をその中心を中心に任意の角度で回転できます。ループが実行される範囲(var i)を変更することにより、部分的な楕円を描画できます。

このように楕円をレンダリングすると、ライン上のすべてのポイントの正確なx、y位置を特定できます。これは、他のオブジェクトの位置が楕円の位置と方向に依存する場合に役立ちます。

コードの例を次に示します。

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);
    }
}

ここでインタラクティブな例を参照してください:http//www.scienceprimer.com/draw-oval-html5-canvas


この方法では、数学的に最も正しい楕円が生成されます。計算コストの点で他と比べてどうなのか気になります。
エリック

@Eric頭のてっぺんから、(この方法:12の乗算、4の加算、8の三角関数)と(4つのキュービックベジエ:24の乗算と24の加算)の違いのように見えます。そして、それは、キャンバスがDe Casteljauの反復アプローチを使用していることを前提としていますが、さまざまなブラウザーのキャンバスに実際にどのように実装されているかはわかりません。
DerekR 2013年

Chromeでこの2つのアプローチをプロファイリングしようとしました:4キュービックベジエ:約0.3ミリ秒この方法:ステップ= 0.1で約1.5ミリ秒ほぼ同じ0.3ミリ秒のようです
sol0mka

どうやらCanvasRenderingContext2D.ellipse()はすべてのブラウザでサポートされているわけではありません。このルーチンは、ワークショップテンプレートに十分な、優れた完璧な精度で機能します。 Internet Explorerでも動作します、ため息をつきます。
zipzit 2016

12

楕円を確実に再現するには、4つのベジェ曲線(およびマジックナンバー)が必要です。ここを参照してください:

www.tinaja.com/glib/ellipse4.pdf

2つのベジエは、楕円を正確に再現しません。これを証明するために、同じ高さと幅で上記の2つのベジェソリューションのいくつかを試してください。理想的には円を近似する必要がありますが、そうではありません。彼らはまだ楕円形に見えますが、これは彼らが想定されていることをしていないことを証明するためのものです。

動作するはずのものは次のとおりです。

http://jsfiddle.net/BsPsj/

コードは次のとおりです。

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();


}

このソリューションは私にとって最適です。4つのパラメータ。クイック。簡単。
オリバー

こんにちは、私もこのソリューションが好きでしたが、コードをいじっていて、パラメーター値の左側にゼロを追加すると、円が歪んでいることに気付きました。これは、円のサイズが大きくなるにつれて強調されますが、なぜこれが発生するのか知っていますか? jsfiddle.net/BsPsj/187
alexcons

1
@alexconsの前に0を付けると、8進整数リテラルになります。
Alankar Misra 2017

11

不均一なスケーリングを使用することもできます。XとYのスケーリングを提供できるので、XまたはYのスケーリングを他より大きく設定し、円を描くだけで、楕円ができます。


9

それほど一般的な楕円を必要とせず、楕円の半軸と離心率のデータが大きい(天文学的なJavaScriptのおもちゃが軌道をプロットするのに適している)peoploに対して、このコード(Andrew Staroscikによって部分的に提示された)を少し変更しました。 、 例えば)。

ここでは、手順を調整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();
}

5

私の解決策は、これらすべてとは少し異なります。上記の中で最も投票された回答は最も近いと思いますが、この方法の方が少しクリーンで理解しやすいと思います。

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();
}

そして、次のように使用します。

bezierCurve(x + 60, y + 75, 80, 130);

quadraticCurveToを使用して作成しようとして失敗したことに加えて、フィドルにはいくつかの使用例があります。


コードは見栄えがしますが、これを使用して同じ高さと幅の値を渡して円を描くと、角が非常に丸い正方形に似た形状になります。そのような場合に本物のサークルを取得する方法はありますか?
ドリューノアケス2012年

その場合の最良のオプションは、arc()メソッドを使用することだと思います。このメソッドを使用すると、ここでの上位投票の回答と同様に、円弧の開始角度と終了角度の位置を定義できます。全体的にもベストアンサーだと思います。
jaredwilli 2012年

4

上記のベジェ曲線ソリューションが好きです。縮尺も線幅に影響するので、高さよりも幅の広い楕円を描画しようとすると、上下の「側面」が左右の「側面」よりも薄く表示されます...

良い例は次のとおりです。

ctx.lineWidth = 4;
ctx.scale(1, 0.5);
ctx.beginPath();
ctx.arc(20, 20, 10, 0, Math.PI * 2, false);
ctx.stroke();

楕円の山と谷の線の幅は、左右の頂点(頂点?)の半分の幅であることに注意してください。


5
ストローク操作の直前にスケールを逆にすると、ストロークのスケーリングを回避できます...したがって、最後から2番目の行として反対のスケーリングを追加しますctx.scale(0.5, 1);
Johnathan Hebert 2012


3

ChromeとOperaはキャンバス2Dコンテキストの楕円法をサポートしていますが、IE、Edge、Firefox、Safariはサポートしていません。

JSで楕円法を実装することも、サードパーティのポリフィルを使用することもできます。

ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)

使用例:

ctx.ellipse(20, 21, 10, 10, 0, 0, Math.PI*2, true);

canvas-5-polyfillを使用して、楕円法を提供できます。

または、いくつかのjsコードを貼り付けて、楕円メソッドを提供します。

if (CanvasRenderingContext2D.prototype.ellipse == undefined) {
  CanvasRenderingContext2D.prototype.ellipse = function(x, y, radiusX, radiusY,
        rotation, startAngle, endAngle, antiClockwise) {
    this.save();
    this.translate(x, y);
    this.rotate(rotation);
    this.scale(radiusX, radiusY);
    this.arc(0, 0, 1, startAngle, endAngle, antiClockwise);
    this.restore();
  }
}

楕円1

楕円2

楕円3

楕円4


今日のベストアンサー。ネイティブEclipse関数はまだ実験的であるため、フォールバックがあることは絶対に素晴らしいことです。ありがとう!
walv 2017

2

これは、楕円のような形状を作成する別の方法ですが、「fillRect()」関数を使用しますが、これを使用して、fillRect()関数の引数を変更できます。

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Sine and cosine functions</title>
</head>
<body>
<canvas id="trigCan" width="400" height="400"></canvas>

<script type="text/javascript">
var canvas = document.getElementById("trigCan"), ctx = canvas.getContext('2d');
for (var i = 0; i < 360; i++) {
    var x = Math.sin(i), y = Math.cos(i);
    ctx.stroke();
    ctx.fillRect(50 * 2 * x * 2 / 5 + 200, 40 * 2 * y / 4 + 200, 10, 10, true);
}
</script>
</body>
</html>

2

これを使用すると、楕円のセグメントを描画することもできます。

function ellipse(color, lineWidth, x, y, stretchX, stretchY, startAngle, endAngle) {
    for (var angle = startAngle; angle < endAngle; angle += Math.PI / 180) {
        ctx.beginPath()
        ctx.moveTo(x, y)
        ctx.lineTo(x + Math.cos(angle) * stretchX, y + Math.sin(angle) * stretchY)
        ctx.lineWidth = lineWidth
        ctx.strokeStyle = color
        ctx.stroke()
        ctx.closePath()
    }
}

http://jsfiddle.net/FazAe/1/


2

誰もより単純なアプローチを思い付かなかったので、quadraticCurveTo私はその解決策を追加しています。@StevebezierCurveTo回答の呼び出しを次のように置き換えるだけです。

  ctx.quadraticCurveTo(x,y,xm,y);
  ctx.quadraticCurveTo(xe,y,xe,ym);
  ctx.quadraticCurveTo(xe,ye,xm,ye);
  ctx.quadraticCurveTo(x,ye,x,ym);

を削除することもできclosePathます。ただし、楕円形は少し異なって見えます。


0

これは、SVGの楕円弧と同じ値を使用する私が作成した関数です。X1とY1は最後の座標、X2とY2は終了座標、半径は数値、時計回りはブール値です。また、キャンバスコンテキストがすでに定義されていることも前提としています。

function ellipse(x1, y1, x2, y2, radius, clockwise) {

var cBx = (x1 + x2) / 2;    //get point between xy1 and xy2
var cBy = (y1 + y2) / 2;
var aB = Math.atan2(y1 - y2, x1 - x2);  //get angle to bulge point in radians
if (clockwise) { aB += (90 * (Math.PI / 180)); }
else { aB -= (90 * (Math.PI / 180)); }
var op_side = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) / 2;
var adj_side = Math.sqrt(Math.pow(radius, 2) - Math.pow(op_side, 2));

if (isNaN(adj_side)) {
    adj_side = Math.sqrt(Math.pow(op_side, 2) - Math.pow(radius, 2));
}

var Cx = cBx + (adj_side * Math.cos(aB));            
var Cy = cBy + (adj_side * Math.sin(aB));
var startA = Math.atan2(y1 - Cy, x1 - Cx);       //get start/end angles in radians
var endA = Math.atan2(y2 - Cy, x2 - Cx);
var mid = (startA + endA) / 2;
var Mx = Cx + (radius * Math.cos(mid));
var My = Cy + (radius * Math.sin(mid));
context.arc(Cx, Cy, radius, startA, endA, clockwise);
}

0

楕円を長方形の中に完全に収めたい場合は、次のようになります。

function ellipse(canvasContext, x, y, width, height){
  var z = canvasContext, X = Math.round(x), Y = Math.round(y), wd = Math.round(width), ht = Math.round(height), h6 = Math.round(ht/6);
  var y2 = Math.round(Y+ht/2), xw = X+wd, ym = Y-h6, yp = Y+ht+h6, cs = cards, c = this.card;
  z.beginPath(); z.moveTo(X, y2); z.bezierCurveTo(X, ym, xw, ym, xw, y2); z.bezierCurveTo(xw, yp, X, yp, X, y2); z.fill(); z.stroke();
  return z;
}

canvasContext.fillStyle = 'rgba(0,0,0,0)';このデザインで塗りつぶされていないことを確認してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.