wake-up-neo.com

Wie zeichnet man ein Oval in html5-Leinwand?

Es scheint keine native Funktion zu geben, um eine ovale Form zu zeichnen. Ich suche auch nicht nach der Ei-Form.

Ist es möglich, ein Oval mit 2 Bezierkurven zu zeichnen?

Mein Ziel ist es, ein paar Augen zu zeichnen und eigentlich nur mit Bögen zu arbeiten.

Lösung

Scale () ändert also die Skalierung für alle nächsten Shapes. __ Save () speichert die Einstellungen vor dem Wiederherstellen und wird zum Wiederherstellen der Einstellungen verwendet, um neue Shapes ohne Skalierung zu zeichnen.

Danke an 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();
72
Tammo

Aktualisierung:

  • die Skalierungsmethode kann das Aussehen der Strichbreite beeinflussen 
  • die richtige Skalierungsmethode kann die Strichbreite beibehalten
  • canvas verfügt über eine Ellipse-Methode, die Chrome jetzt unterstützt 
  • jSBin wurde um aktualisierte Tests erweitert 

JSBin-Testbeispiel (aktualisiert, um die Antworten anderer zum Vergleich zu testen)

  • Bezier - Zeichnen basierend auf der oberen linken Ecke, die Rect und Breite/Höhe enthält
  • Bezier mit Mittelpunkt - Zeichnen Sie basierend auf Mittelpunkt und Breite/Höhe
  • Bögen und Skalierung - Zeichnen Sie anhand des Zeichnungskreises und der Skalierung
  • Quadratische Kurven - Zeichnen Sie mit Quadraten
    • test scheint nicht ganz das Gleiche zu zeichnen, kann Implementierung sein
    • siehe Oyophants Antwort
  • Canvas-Ellipse - unter Verwendung der W3C-Standard-Ellipse () -Methode
    • test scheint nicht ganz das Gleiche zu zeichnen, kann Implementierung sein
    • siehe Loktars Antwort

Original:

Wenn Sie ein symmetrisches Oval wünschen, können Sie immer einen Kreis mit Radiusbreite erstellen und ihn dann auf die gewünschte Höhe skalieren (Bearbeiten: Beachten Sie, dass dies das Aussehen der Strichbreite beeinflussen wird (siehe Antwort von acdameli) Ich möchte die Ellipse vollständig steuern. Hier ist eine Möglichkeit, Bezier-Kurven zu verwenden. 

<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>
105
Steve Tranby

Hier ist eine vereinfachte Version von Lösungen an anderer Stelle. Ich zeichne einen kanonischen Kreis, übersetze und skaliere und streiche dann.

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

Der Ansatz der Bezierkurve eignet sich hervorragend für einfache Ovale. Für mehr Kontrolle können Sie eine Schleife verwenden, um eine Ellipse mit unterschiedlichen Werten für den x- und y-Radius (Radien, Radien?) Zu zeichnen. 

Durch das Hinzufügen eines RotationAngle-Parameters kann das Oval um einen beliebigen Winkel um seine Mitte gedreht werden. Partielle Ovale können gezeichnet werden, indem der Bereich (var i) geändert wird, über den die Schleife läuft. 

Durch das Rendern des Ovals auf diese Weise können Sie die exakte x, y-Position aller Punkte auf der Linie bestimmen. Dies ist nützlich, wenn die Position anderer Objekte von der Position und Ausrichtung des Ovals abhängt. 

Hier ist ein Beispiel für den 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);
    }
}

Ein interaktives Beispiel finden Sie hier: http://www.scienceprimer.com/draw-oval-html5-canvas

15

Sie benötigen 4 Bezierkurven (und eine magische Zahl), um eine Ellipse zuverlässig zu reproduzieren. Siehe hier:

www.tinaja.com/glib/ellipse4.pdf

Zwei Beziers reproduzieren eine Ellipse nicht genau. Um dies zu beweisen, probieren Sie einige der beiden oben genannten Bezier-Lösungen mit gleicher Höhe und Breite aus - sie sollten sich im Idealfall einem Kreis annähern, werden dies jedoch nicht. Sie sehen immer noch oval aus, was beweist, dass sie nicht das tun, was sie sollen. 

Hier ist etwas, das funktionieren sollte:

http://jsfiddle.net/BsPsj/

Hier ist der 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();


}
11
Alankar Misra

Sie können auch versuchen, eine nicht einheitliche Skalierung zu verwenden. Sie können X- und Y-Skalierung bereitstellen. Legen Sie einfach die X- oder Y-Skalierung größer als die andere fest und zeichnen Sie einen Kreis, und Sie haben eine Ellipse.

11

Ich habe eine kleine Anpassung von diesem Code (teilweise von Andrew Staroscik dargestellt) für Leute gemacht, die keine so allgemeine Ellipse wollen und nur über die größeren Halbachsen und die Exzentrizitätsdaten der Ellipse verfügen (gut für astronomisches Javascript) Spielzeug, um beispielsweise Umlaufbahnen zu zeichnen).

Hiermit können Sie sich daran erinnern, dass Sie die Schritte in i anpassen können, um eine höhere Genauigkeit in der Zeichnung zu erhalten:

/* 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();
}
8
Girardi

Es gibt jetzt eine native Ellipse - Funktion für die Leinwand, die der Bogenfunktion sehr ähnlich ist, obwohl wir jetzt zwei Radiuswerte und eine Drehung haben, die großartig ist.

ellipse (x, y, RadiusX, RadiusY, Drehung, StartAngle, EndAngle, gegen den Uhrzeigersinn) 

Live Demo

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

Nur scheint derzeit in Chrome zu arbeiten

8
Loktar

Meine Lösung ist ein bisschen anders als alle diese. Am nächsten, denke ich, ist die am meisten gewählte Antwort, aber ich denke, dass dieser Weg ein bisschen sauberer und leichter zu verstehen ist.

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

Und benutze es dann so:

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

Es gibt ein paar Anwendungsbeispiele in der Geige, zusammen mit einem fehlgeschlagenen Versuch, mit quadraticCurveTo ein solches zu erstellen.

4
jaredwilli

Ich mag die Bezierkurven-Lösung oben. Ich habe bemerkt, dass die Skala auch die Linienbreite beeinflusst. Wenn Sie also versuchen, eine Ellipse zu zeichnen, die breiter als groß ist, werden die oberen und unteren "Seiten" dünner als die linken und rechten "Seiten".

ein gutes Beispiel wäre:

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

sie sollten bemerken, dass die Breite der Linie an der Spitze und am Tal der Ellipse halb so breit ist wie an der linken und rechten Spitze (Scheitelpunkte?).

3
acdameli

Chrome und Opera unterstützen Ellipse - Methode für Canvas 2d-Kontext, aber IE, Edge, Firefox und Safari unterstützen dies nicht.

Wir können die Ellipsenmethode von JS implementieren oder eine Polyfill eines Drittanbieters verwenden.

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

Anwendungsbeispiel:

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

Sie können eine canvas-5-polyfill verwenden, um eine Ellipsenmethode bereitzustellen.

Oder fügen Sie einfach etwas js-Code ein, um die Ellipse-Methode bereitzustellen:

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

ellipse 1

ellipse 2

ellipse 3

ellipse 4

2
cuixiping

Ja, es ist mit zwei Bezier-Kurven möglich - hier ein kurzes Tutorial/Beispiel: http://www.williammalone.com/briefs/how-to-draw-ellipse-html5-canvas/

2
tagawa

Da niemand mit einer einfacheren quadraticCurveTo einen Ansatz gefunden hat, füge ich eine Lösung hinzu. Ersetzen Sie einfach die bezierCurveTo-Aufrufe in den @ Steve's answer durch Folgendes:

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

Sie können auch die closePath entfernen. Das Oval sieht jedoch etwas anders aus.

1
oyophant

Damit können Sie sogar Segmente einer Ellipse zeichnen:

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/

1
super

Dies ist eine weitere Möglichkeit, eine ellipsenartige Form zu erstellen, obwohl die Funktion "fillRect ()" verwendet wird. Dies kann zum Ändern der Argumente in der Funktion fillRect () verwendet werden.

<!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>
1
aspYou

Ich habe hier eine Funktion geschrieben, die dieselben Werte wie der Ellipsenbogen in SVG verwendet. X1 und Y1 sind die letzten Koordinaten, X2 und Y2 sind die Endkoordinaten, der Radius ist ein Zahlenwert und der Uhrzeigersinn ist ein boolescher Wert. Es wird auch davon ausgegangen, dass Ihr Leinwandkontext bereits definiert wurde.

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
Grant

Wenn Sie möchten, dass die Ellipse vollständig in ein Rechteck passt, ist dies wirklich so:

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

Stellen Sie sicher, dass Ihre canvasContext.fillStyle = 'rgba(0,0,0,0)'; mit diesem Entwurf nicht gefüllt wird.

0
PHPglue