Kreiszeichnung mit SVGs Bogenpfad


146

Kurze Frage: Mit dem SVG-Pfad können wir 99,99% eines Kreises zeichnen und er wird angezeigt. Wenn es sich jedoch um 99,99999999% eines Kreises handelt, wird der Kreis nicht angezeigt. Wie kann es behoben werden?

Der folgende SVG-Pfad kann 99,99% eines Kreises zeichnen: (Versuchen Sie es unter http://jsfiddle.net/DFhUF/1381/ und prüfen Sie, ob Sie 4 oder nur 2 Bögen sehen. Beachten Sie jedoch, dass es sich um einen IE handelt in VML gerendert, nicht SVG, aber mit dem gleichen Problem)

M 100 100 a 50 50 0 1 0 0.00001 0

Aber wenn es 99,99999999% eines Kreises ist, wird dann überhaupt nichts angezeigt?

M 100 100 a 50 50 0 1 0 0.00000001 0    

Und das ist auch bei 100% eines Kreises so (es ist immer noch ein Bogen, nicht wahr, nur ein sehr vollständiger Bogen)

M 100 100 a 50 50 0 1 0 0 0 

Wie kann das behoben werden? Der Grund dafür ist, dass ich eine Funktion verwende, um einen Prozentsatz eines Bogens zu zeichnen, und wenn ich einen 99,9999% oder 100% Bogen "Sonderfall" machen muss, um die Kreisfunktion zu verwenden, wäre das irgendwie albern.

Ein Testfall für jsfiddle mit RaphaelJS finden Sie unter http://jsfiddle.net/DFhUF/1381/
(und wenn es sich um VML in IE 8 handelt, wird selbst der zweite Kreis nicht angezeigt ... Sie müssen ihn ändern 0,01)


Aktualisieren:

Dies liegt daran, dass ich einen Bogen für eine Punktzahl in unserem System rendere, sodass 3,3 Punkte 1/3 eines Kreises erhalten. 0,5 ergibt einen halben Kreis und 9,9 Punkte ergeben 99% eines Kreises. Aber was ist, wenn es in unserem System Werte gibt, die 9,99 betragen? Muss ich überprüfen, ob es sich um 99,999% eines Kreises handelt, und eine arcFunktion oder eine circleFunktion entsprechend verwenden? Was ist dann mit einer Punktzahl von 9.9987? Welches verwenden? Es ist lächerlich zu wissen, welche Art von Partituren einem "zu vollständigen Kreis" zugeordnet und zu einer Kreisfunktion gewechselt werden. Wenn es sich um "bestimmte 99,9%" eines Kreises oder eine 9,9987-Partitur handelt, verwenden Sie die Bogenfunktion .


@minitech natürlich funktioniert es ... es ist dann 99% eines Kreises (nur grob gesagt). Der Fall ist, dass es 98%, 99%, 99,99% eines Kreises zeichnen kann, aber nicht 99,9999999% oder 100%
Unpolarität

1
Beide Links verweisen auf dasselbe und funktionieren in Safari einwandfrei.
Mark Bessey

Richtig, gleicher Link, ich möchte nur, dass die Leute den Testfall früher sehen, also füge ich den Link am Anfang der Frage hinzu. Richtige Safari wird es schaffen, wie schön ... Chrome und Firefox werden es nicht ... irgendwie seltsam, weil Safari und Chrome beide Webkit sind ... aber hängt die SVG-Engine von Webkit ab?
Unpolarität

@Marcin sieht gut aus wie? Sehen Sie 4 Bögen oder 2 Bögen? Hast du dir den Code überhaupt angesehen?
Unpolarität

2
eine aktualisierte jsfiddle mit Raphael als Bibliothek, um umgehende Fehler beim Laden von raphael.js zu umgehen: jsfiddle.net/DFhUF/1381
ericsoco

Antworten:


35

Gleiches gilt für den XAML-Bogen. Schließen Sie einfach den 99,99% -Bogen mit a Zund Sie haben einen Kreis!


Können Sie also den dritten Fall im jsfiddle-Beispiel schließen und zum Laufen bringen?
Unpolarität

mit Absenken des Wertes auf 0,0001, ja. Das Problem hängt mit der Implementierung von WebKit durch verschiedene Browser zusammen, nicht mit SVG selbst. Auf diese Weise erstellen Sie jedoch einen Kreis in der SVG / XAMLs-Arc-Komponente.
Todd Main

Ahhh, Betrug, immer die beste Lösung. Der 100% -Fall muss noch behandelt werden, aber nur durch "Abrunden" auf 99,999% und nicht mit einem ganzen separaten Code-Zweig.
SimonR

Irgendeine Demo? Diese Antwort erfüllt nicht
Medet Tleukabiluly

@MedetTleukabiluly Sie haben den Bogen oben in der Frage, fügen Sie zam Ende ein und fertig. Möglicherweise müssen Sie Nullen entfernen, zum Beispiel musste ich im neuesten Chrome schreiben 0.0001, damit es funktioniert. Wenn Sie suchen, wo die Pfadzeichenfolge abgelegt werden soll, sehen Sie sich Pfade auf MDN an .
TWiStErRob

415

Ich weiß, dass es etwas spät im Spiel ist, aber ich erinnerte mich an diese Frage, als sie neu war und ich ein ähnliches Dillemma hatte, und fand versehentlich die "richtige" Lösung, wenn noch jemand nach einer sucht:

<path 
    d="
    M cx cy
    m -r, 0
    a r,r 0 1,0 (r * 2),0
    a r,r 0 1,0 -(r * 2),0
    "
/>

Mit anderen Worten:

<circle cx="100" cy="100" r="75" />

kann als ein Weg damit erreicht werden:

  <path 
        d="
        M 100, 100
        m -75, 0
        a 75,75 0 1,0 150,0
        a 75,75 0 1,0 -150,0
        "
  />

Der Trick besteht darin, zwei Bögen zu haben, wobei der zweite dort aufnimmt, wo der erste aufgehört hat, und den negativen Durchmesser verwendet, um zum ursprünglichen Bogenstartpunkt zurückzukehren.

Der Grund, warum es nicht als vollständiger Kreis in einem Bogen gemacht werden kann (und ich spekuliere nur), ist, dass Sie ihm sagen würden, er solle einen Bogen von sich selbst (sagen wir 150.150) zu sich selbst (150.150) zeichnen, den er rendert als "oh, ich bin schon da, kein Lichtbogen nötig!".

Die Vorteile der von mir angebotenen Lösung sind:

  1. Es ist einfach, von einem Kreis direkt in einen Pfad zu übersetzen
  2. Es gibt keine Überlappung in den beiden Bogenlinien (was zu Problemen führen kann, wenn Sie Markierungen oder Muster usw. verwenden). Es ist eine saubere durchgehende Linie, wenn auch in zwei Teilen gezeichnet.

Nichts davon wäre von Bedeutung, wenn nur Textpfade Formen akzeptieren könnten. Aber ich denke, sie vermeiden diese Lösung, da Formelemente wie Kreise technisch gesehen keinen "Startpunkt" haben.

jsfiddle-Demo: http://jsfiddle.net/crazytonyi/mNt2g/

Aktualisieren:

Wenn Sie den Pfad als textPathReferenz verwenden und möchten, dass der Text am äußeren Rand des Bogens gerendert wird, verwenden Sie genau dieselbe Methode, ändern jedoch das Sweep-Flag von 0 auf 1, sodass die Außenseite des Bogens behandelt wird Pfad als Oberfläche statt nach innen (stellen Sie sich vor, 1,0jemand sitzt in der Mitte und zeichnet einen Kreis um sich selbst, während 1,1jemand in Radiusentfernung um die Mitte geht und seine Kreide neben sich zieht, wenn dies hilfreich ist). Hier ist der Code wie oben, aber mit der Änderung:

<path 
    d="
    M cx cy
    m -r, 0
    a r,r 0 1,1 (r * 2),0
    a r,r 0 1,1 -(r * 2),0
    "
/>

1
+1, Danke für die Formeln. Natürlich könnten die ersten beiden Befehle konsolidiert werden.
John Gietzen

+1 für die Klarheit und Praktikabilität der Lösung im Allgemeinen, aber insbesondere für "Formelemente wie Kreis haben technisch gesehen keinen" Startpunkt " - eine so klare Art, das Problem auszudrücken.
David John Welsh

11
Ich denke, das Problem hier ist nicht, dass "Oh, ich bin schon da, kein Lichtbogen notwendig!". Wenn Sie 1 als Großbogen-Flag angeben, teilen Sie der Engine ausdrücklich mit, dass Sie möchten, dass sie den weiteren Weg geht. Das eigentliche Problem ist, dass es unendlich viele Kreise gibt, die die Einschränkungen des Durchgangs durch Ihren Punkt erfüllen. Dies erklärt, warum der Motor nicht weiß, was zu tun ist, wenn Sie einen 360 * -Bogen wünschen. Der Grund, warum es für 359.999 nicht funktioniert, ist, dass dies eine numerisch instabile Berechnung ist, und obwohl es technisch genau einen Kreis gibt, der der Anfrage entspricht, wäre es wahrscheinlich nicht der, den Sie sowieso wollten
qbolec

@qbolec - Sie haben Recht, ich habe darüber nachgedacht, dass der große Bogen und der Sweep ausgeschaltet sind und der Bogen kein Ziel hat. Oder zumindest, dass selbst bei großem Bogen und Sweep ein grundlegendes Design vorhanden war, das einen Bogen als Kurve zwischen zwei Punkten definierte (so dass ein Punkt, der zweimal verwendet wurde, als kollabierter Einzelpunkt angesehen wurde.) Das Sehen des Bogens, der möglicherweise 360 ​​Kreise um den einen Punkt zeichnet, ist sinnvoll, obwohl es bei Definition eines Zentrums zu einem Kreis zusammenbrechen würde, was die Frage aufwirft, ob ein einzelner Bogen einen vollen Kreis bilden könnte, wenn ein Zentrum vorhanden wäre.
Anthony

1
"Formelemente wie Kreis haben technisch gesehen keinen" Startpunkt ". Das ist eigentlich nicht wahr. Die SVG-Spezifikation gibt absolut an, wo sich der Startpunkt aller Formen befindet. Dies muss geschehen, damit Dash-Arrays an Formen arbeiten können.
Paul LeBeau

17

In Bezug auf Anthonys Lösung ist hier eine Funktion, um den Pfad zu erhalten:

function circlePath(cx, cy, r){
    return 'M '+cx+' '+cy+' m -'+r+', 0 a '+r+','+r+' 0 1,0 '+(r*2)+',0 a '+r+','+r+' 0 1,0 -'+(r*2)+',0';
}

10

Ein ganz anderer Ansatz:

Anstatt mit Pfaden zu fummeln, um einen Bogen in svg anzugeben, können Sie auch ein Kreiselement nehmen und ein Strich-Dasharray im Pseudocode angeben:

with $score between 0..1, and pi = 3.141592653589793238

$length = $score * 2 * pi * $r
$max = 7 * $r  (i.e. well above 2*pi*r)

<circle r="$r" stroke-dasharray="$length $max" />

Die Einfachheit ist der Hauptvorteil gegenüber der Methode mit mehreren Bogenpfaden (z. B. wenn Sie beim Erstellen von Skripten nur einen Wert eingeben und für jede Bogenlänge fertig sind)

Der Bogen beginnt am äußersten rechten Punkt und kann mithilfe einer Drehtransformation verschoben werden.

Hinweis: Firefox hat einen seltsamen Fehler, bei dem Drehungen über 90 Grad oder mehr ignoriert werden. Um den Bogen von oben zu starten, verwenden Sie:

<circle r="$r" transform="rotate(-89.9)" stroke-dasharray="$length $max" />

Beachten Sie, dass diese Lösung nur für Fälle gilt, in denen Sie keine Füllung verwenden und nur ein Bogensegment ohne Abweichungen benötigen. In dem Moment, in dem Sie Sektoren zeichnen möchten, benötigen Sie sogar einen neuen SVG-Pfadknoten, der andernfalls in einem einzelnen Pfad-Tag behandelt werden könnte.
mxfh

@mxfh nicht wirklich, wenn Sie die Strichbreite doppelt so groß wie r nehmen, erhalten Sie perfekte Sektoren.
MVDS

Mit ist stroke-dasharray="0 10cm 5cm 1000cm"es möglich, die Transformation zu vermeiden
Stenci

@stenci ja, aber der Nachteil ist, dass Sie nicht einen Parameter im Dasharray haben können, um von 0% auf 100% Füllung zu skalieren (was eines meiner Ziele war)
mvds

5

Adobe Illustrator verwendet Bezierkurven wie SVG und erstellt für Kreise vier Punkte. Sie können einen Kreis mit zwei elliptischen Bogenbefehlen erstellen ... aber für einen Kreis in SVG würde ich ein <circle />:) verwenden


"Sie können einen Kreis mit zwei elliptischen Bogenbefehlen erstellen"? Was meinst du? Kannst du nicht einfach einen benutzen? Zumindest indem man von (0,0) nach (0,01, 0) geht, damit es etwas rendert. Informationen zur Verwendung circlefinden Sie stattdessen im Update in der Frage.
Unpolarität

Nein, ich glaube nicht, dass Sie nur einen verwenden können. Sie haben gezeigt, dass dies nicht möglich ist, und Sie haben ein ähnliches Problem mit HTML5-Canvas-Bögen.
Phrogz

Sie meinen, mit arc, um einen vollen Kreis mit dem linken und dem rechten Bogen zu erstellen? Der Bogen kann also keinen vollständigen Kreis zeichnen ... dazu sind zwei arcPfade erforderlich
Unpolarität

2
@ 動靜 能量 Richtig, es werden zwei Bogenpfade benötigt (oder der hässliche Hack eines Bogens, der den größten Teil des Weges zurücklegt, und dann ein Befehl zum Schließen des Pfades zur Geraden bis zum Ende).
Phrogz

1
Illustrator saugt. Sie können keinen Kreis mit Bezierkurven erstellen. Nur etwas in der Nähe eines Kreises, aber immer noch kein Kreis.
Adius

4

Es ist eine gute Idee, mit zwei Bogenbefehlen einen vollen Kreis zu zeichnen.

Normalerweise benutze ich eine Ellipse oder ein Kreiselement, um einen vollen Kreis zu zeichnen.


5
Eine wesentliche Einschränkung von svg besteht darin, dass Formelemente nicht für Textpfade verwendet werden können. Dies ist wahrscheinlich die Hauptmotivation für alle, die diese Frage besuchen.
Anthony

1
@ Anthony können sie jetzt. Firefox unterstützt textPath für jede Form. Ich vermute, Chrome auch.
Robert Longson

@RobertLongson - Können Sie ein Beispiel oder Links zu einem Beispiel bereitstellen? Neugierig, wie entschieden wird, wo der Textpfad beginnt und ob er sich außen oder innen (usw.) befindet, wenn er ohne traditionellen Startpunkt gezeichnet wird.
Anthony

@Anthony Siehe die SVG 2-Spezifikation, z. B. w3.org/TR/SVG2/shapes.html#CircleElement , auch den neuen textPath Seite Attribut
Robert Longson

4

Aufbauend auf den Antworten von Anthony und Anton habe ich die Möglichkeit integriert, den generierten Kreis zu drehen, ohne das gesamte Erscheinungsbild zu beeinträchtigen. Dies ist nützlich, wenn Sie den Pfad für eine Animation verwenden und steuern müssen, wo sie beginnt.

function(cx, cy, r, deg){
    var theta = deg*Math.PI/180,
        dx = r*Math.cos(theta),
        dy = -r*Math.sin(theta);
    return "M "+cx+" "+cy+"m "+dx+","+dy+"a "+r+","+r+" 0 1,0 "+-2*dx+","+-2*dy+"a "+r+","+r+" 0 1,0 "+2*dx+","+2*dy;
}

Genau das brauchte ich. Ich habe das Greensock-Morph-Plugin verwendet, um einen Kreispfad in einen rechteckigen Pfad zu verwandeln, und musste leicht gedreht werden, damit er genau richtig aussieht.
RoboKozo

3

Für diejenigen wie mich, die nach Ellipsenattributen für die Pfadkonvertierung suchten:

const ellipseAttrsToPath = (rx,cx,ry,cy) =>
`M${cx-rx},${cy}a${rx},${ry} 0 1,0 ${rx*2},0a${rx},${ry} 0 1,0 -${rx*2},0`

2

Als Funktion geschrieben sieht es so aus:

function getPath(cx,cy,r){
  return "M" + cx + "," + cy + "m" + (-r) + ",0a" + r + "," + r + " 0 1,0 " + (r * 2) + ",0a" + r + "," + r + " 0 1,0 " + (-r * 2) + ",0";
}

2
Das ist eine großartige Antwort! Nur eine kleine Ergänzung: Dieser Weg ist nach Osten, Norden, Westen, Süden gezeichnet. Wenn Sie sich der Kreis zu verhalten sich genau wie ein wollen <circle>, müssen Sie die Richtung nach Osten, Süden ändern, Westen, Norden: "M" + cx + "," + cy + "m" + (r) + ",0" + "a" + r + "," + r + " 0 1,1 " + (-r * 2) + ",0" + "a" + r + "," + r + " 0 1,1 " + (r * 2) + ",0" Der Vorteil ist , dass stroke-dashoffsetund startOffsetjetzt die gleiche Art und Weise arbeiten.
Loominade

2

Diese Antworten sind viel zu kompliziert.

Eine einfachere Möglichkeit, dies zu tun, ohne zwei Bögen zu erstellen oder in verschiedene Koordinatensysteme zu konvertieren.

Dies setzt voraus, dass Ihr Leinwandbereich Breite wund Höhe hat h.

`M${w*0.5 + radius},${h*0.5}
 A${radius} ${radius} 0 1 0 ${w*0.5 + radius} ${h*0.5001}`

Verwenden Sie einfach die Flagge "Langer Bogen", damit die volle Flagge gefüllt ist. Machen Sie dann die Bögen zu 99,9999% zum vollen Kreis. Optisch ist es das gleiche. Vermeiden Sie die Sweep-Flagge, indem Sie den Kreis am äußersten rechten Punkt des Kreises beginnen (ein Radius direkt horizontal von der Mitte).


1

Eine andere Möglichkeit wäre die Verwendung von zwei kubischen Bezierkurven. Dies ist für iOS- Benutzer gedacht, die PocketSVG verwenden, das den Parameter svg arc nicht erkennt.

C x1 y1, x2 y2, xy (oder c dx1 dy1, dx2 dy2, dx dy)

Kubische Bezier-Kurve

Der letzte Satz von Koordinaten hier (x, y) ist der Punkt, an dem die Linie enden soll. Die anderen beiden sind Kontrollpunkte. (x1, y1) ist der Kontrollpunkt für den Beginn Ihrer Kurve und (x2, y2) für den Endpunkt Ihrer Kurve.

<path d="M25,0 C60,0, 60,50, 25,50 C-10,50, -10,0, 25,0" />

1

Ich habe eine Jsfiddle gemacht, um es hier zu tun:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}

function describeArc(x, y, radius, startAngle, endAngle){

var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);

var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

var d = [
    "M", start.x, start.y, 
    "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
].join(" ");

return d;       
}
console.log(describeArc(255,255,220,134,136))

Verknüpfung

Alles, was Sie tun müssen, ist, die Eingabe von console.log zu ändern und das Ergebnis in console zu erhalten


Genau das habe ich gesucht. Beste Antwort hier! stimme diesem Kerl zu
Måns Dahlström
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.