SVG abgerundete Ecke

84

Ich habe die folgende SVG:

<svg>
  <g>
    <path id="k9ffd8001" d="M64.5 45.5 82.5 45.5 82.5 64.5 64.5 64.5 z" stroke="#808600" stroke-width="0" transform="rotate(0 0 0)" stroke-linecap="square" stroke-linejoin="round" fill-opacity="1" stroke-opacity="1" fill="#a0a700"></path>
    <path id="kb8000001" d="M64.5 45.5 82.5 45.5 82.5 64.5 64.5 64.5 z" stroke="#808600" stroke-width="0" transform="rotate(0 0 0)" stroke-linecap="square" stroke-linejoin="round" fill-opacity="1" stroke-opacity="1" fill="url(#k9ffb0001)"></path>
  </g>
</svg>

Ich möchte ein CSS-ähnliches border-top-right-radiusund border-top-bottom-radiuseffektives Ergebnis erzielen.

Wie kann ich diesen Effekt mit abgerundeten Ecken erzielen?

Danis
quelle
Es ist schade, dass CSS border-radiusund seine Varianten in SVG nicht funktionieren.
Steven Vachon
7
Übrigens. Wenn Sie ein Rechteck haben, können Sie einfach die Ecken hinzufügen rx=3oder ry=3abrunden. developer.mozilla.org/en-US/docs/Web/SVG/Attribute/rx
Lukas Liesis

Antworten:

126

So können Sie mit SVG Path ein abgerundetes Rechteck erstellen:

<path d="M100,100 h200 a20,20 0 0 1 20,20 v200 a20,20 0 0 1 -20,20 h-200 a20,20 0 0 1 -20,-20 v-200 a20,20 0 0 1 20,-20 z" />

Erläuterung

m100,100: gehe zum Punkt (100,100)

h200: Zeichne eine horizontale Linie von 200 Pixel von dort, wo wir sind

a20,20 0 0 1 20,20: Zeichnen Sie einen Bogen mit einem Radius von 20 x X und einem Radius von 20 x Y im Uhrzeigersinn zu einem Punkt mit einem Unterschied von 20 x in der X- und Y-Achse

v200: Zeichnen Sie eine vertikale 200-Pixel- Linie von dort, wo wir uns befinden

a20,20 0 0 1 -20,20: Zeichnen Sie einen Bogen mit einem X- und Y-Radius von 20 Pixel im Uhrzeigersinn zu einem Punkt mit einem Unterschied von -20 Pixel in X und einem Unterschied von 20 Pixel in der Y-Achse

h-200: Zeichnen Sie eine horizontale Linie von -200 Pixel von dort, wo wir uns befinden

a20,20 0 0 1 -20, -20: Zeichnen Sie einen Bogen mit einem X- und Y-Radius von 20 Pixel im Uhrzeigersinn zu einem Punkt mit einem Unterschied von -20 Pixel in X und einem Unterschied von -20 Pixel in Y

v-200: Zeichnen Sie eine vertikale Linie von -200 Pixel von dort, wo wir uns befinden

a20,20 0 0 1 20, -20: Zeichnen Sie einen Bogen mit einem X- und Y-Radius von 20 Pixel im Uhrzeigersinn zu einem Punkt mit einer Differenz von 20 Pixel in X und einer Differenz von 20 Pixel in der Y-Achse

z: Schließen Sie den Pfad

<svg width="440" height="440">
  <path d="M100,100 h200 a20,20 0 0 1 20,20 v200 a20,20 0 0 1 -20,20 h-200 a20,20 0 0 1 -20,-20 v-200 a20,20 0 0 1 20,-20 z" fill="none" stroke="black" stroke-width="3" />
</svg>

hmak.me
quelle
3
Für alle, die sich für weitere Details zum Lichtbogen interessieren, ist dies die API: A rx ry x-axis-rotation large-arc-flag sweep-flag x y( developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths )
Nic Scozzaro
Wenn Sie nur ein abgerundetes Rechteck und keine komplexere Form wünschen (wie ich dies beim Googeln festgestellt habe), könnte ein einfacherer Ansatz darin bestehen, <svg viewBox="0 0 110 110" xmlns="http://www.w3.org/2000/svg"> "<rect x =" 5 "y =" 5 "width =" 100 "height zu verwenden = "100" rx = "15" style = "Strich: # 000000; Füllung: #FFFFFF" /> `</svg>
John Sibly
58

Ich bin mir nicht sicher, warum niemand eine tatsächliche SVG-Antwort gepostet hat. Hier ist ein SVG-Rechteck mit abgerundeten Ecken (Radius 3) oben:

<svg:path d="M0,0 L0,27 A3,3 0 0,0 3,30 L7,30 A3,3 0 0,0 10,27 L10,0 Z" />

Dies ist eine Verschiebung nach (M), Linie nach (L), Bogen nach (A), Linie nach (L), Bogen nach (A), Linie nach (L), Pfad schließen (Z).

Die durch Kommas getrennten Zahlen sind absolute Koordinaten. Die Bögen werden mit zusätzlichen Parametern definiert, die den Radius und die Art des Bogens angeben. Dies könnte auch mit relativen Koordinaten erreicht werden (verwenden Sie Kleinbuchstaben für L und A).

Die vollständige Referenz für diese Befehle finden Sie auf der Seite W3C- SVG-Pfade. Zusätzliches Referenzmaterial zu SVG-Pfaden finden Sie in diesem Artikel .

vallismortis
quelle
12
Dies ist nicht direkt die Antwort, nach der ich gesucht habe, aber guter Gott, wenn es nicht nützlich ist. Ich habe mich immer gefragt, wozu die Briefe gut sind.
Alex McCabe
1
Vielen Dank für die Erklärung :)
Osman Erdi
47

Wie in meiner Antwort zum Anwenden abgerundeter Ecken auf Pfade / Polygone erwähnt , habe ich eine Routine in Javascript zum generischen Runden von Ecken von SVG-Pfaden mit Beispielen hier geschrieben: http://plnkr.co/edit/kGnGGyoOCKil02k04snu .

Es funktioniert unabhängig von eventuellen Schlaganfällen. Fügen Sie zur Verwendung die Datei rounding.js aus dem Plnkr hinzu und rufen Sie die Funktion folgendermaßen auf:

roundPathCorners(pathString, radius, useFractionalRadius)

Das Ergebnis ist der abgerundete Pfad.

Die Ergebnisse sehen folgendermaßen aus:

Beispiele für SVG-Pfadrundungen

Yona Appletree
quelle
Schön, obwohl die Unterstützung für relative Befehle noch besser wäre.
Joachim Breitner
1
Ich stimme zu :) Dies war nur ein kleines Einzelstück, um mein Problem zu lösen, kein Versuch einer vollwertigen Bibliothek. Ich würde eine Gabel mit dieser Funktionalität begrüßen!
Yona Appletree
Hast du ein Repo damit? Es ist großartig, vielen Dank, dass du es gemacht hast.
Djave
1
Ich habe mir nie die Mühe gemacht, ein Repo dafür zu machen ... obwohl ich es wahrscheinlich sollte.
Yona Appletree
Ich möchte auf @ Djaves Frage zum Repo
zurückkommen
37

Sie haben explizit Ihr stroke-linejoinauf, roundaber Ihr stroke-widthauf festgelegt 0, sodass Sie natürlich keine abgerundeten Ecken sehen, wenn Sie keinen Strich zum Runden haben.

Hier ist ein modifiziertes Beispiel mit abgerundeten Ecken, die durch Striche erstellt wurden:
http://jsfiddle.net/8uxqK/1/

<path d="M64.5 45.5 82.5 45.5 82.5 64.5 64.5 64.5 z"
      stroke-width="5"
      stroke-linejoin="round"
      stroke="#808600"
      fill="#a0a700" />

Andernfalls müssen Sie - wenn Sie eine tatsächlich abgerundete Formfüllung und nicht nur einen abgerundeten Fettstrich benötigen - das tun, was @Jlange sagt, und eine tatsächlich abgerundete Form erstellen.

Phrogz
quelle
Ich sehe das auf jsfiddle richtig, aber beim Kopieren in ein lokales HTML-Dokument ist es nur ein einfaches Rechteck.
Mads Skjern
6
Sie können stroke-linecapanstelle von verwenden stroke-linejoin. Für mich geht das.
Lobodart
32

Ich würde auch in Betracht ziehen, ein einfaches altes zu verwenden <rect>, das die rxund ryAttribute bereitstellt

MDN SVG docs <- Beachten Sie das zweite gezeichnete Rect-Element

Joshua
quelle
2
Das OP möchte jedoch, dass nur einige Ecken abgerundet werden.
Robert Longson
9
Dies beantwortet MEINE Frage, die mich auf diese Seite gebracht hat. So danke!
Steven Vachon
1
Wenn Sie abgerundete Ecken für eine Gruppe von Elementen und nicht nur für ein Rechteck verwenden müssen, können Sie dies mit clipPath developer.mozilla.org/pt-BR/docs/Web/SVG/Element/clipPath tun, wie Sie hier sehen können jsfiddle.net/thiagomata/mp28rnj6/1
Thiago Mata
Toter Link im OP. :(
posfan12
@ posfan12 Das für dich behoben :)
Joshua
12

Ich bin heute selbst auf dieses Problem gestoßen und habe es geschafft, es durch Schreiben einer kleinen JavaScript-Funktion zu lösen.

Von dem, was ich sagen kann, gibt es keine einfache Möglichkeit , ein Pfadelement in einer SVG zu geben abgerundeten Ecken , außer wenn Sie brauchen nur die Grenzen gerundet werden, wobei in diesem Fall die (CSS) Attribute stroke, stroke-widthund vor allem stroke-linejoin="round"ist vollkommen ausreichend.

In meinem Fall habe ich jedoch ein Pfadobjekt verwendet, um benutzerdefinierte Formen mit n Ecken zu erstellen , die mit einer bestimmten Farbe ausgefüllt sind und keine sichtbaren Ränder haben, ähnlich wie folgt:

Geben Sie hier die Bildbeschreibung ein

Ich habe es geschafft, eine Schnellfunktion zu schreiben, die ein Array von Koordinaten für einen SVG-Pfad verwendet und die fertige Pfadzeichenfolge zurückgibt, um sie in das dAttribut des Pfad-HTML-Elements einzufügen. Die resultierende Form sieht dann ungefähr so ​​aus:

Geben Sie hier die Bildbeschreibung ein

Hier ist die Funktion:

/**
 * Creates a coordinate path for the Path SVG element with rounded corners
 * @param pathCoords - An array of coordinates in the form [{x: Number, y: Number}, ...]
 */
function createRoundedPathString(pathCoords) {
    const path = [];
    const curveRadius = 3;

    // Reset indexes, so there are no gaps
    pathCoords = pathCoords.slice();

    for (let i = 0; i < pathCoords.length; i++) {

      // 1. Get current coord and the next two (startpoint, cornerpoint, endpoint) to calculate rounded curve
      const c2Index = ((i + 1) > pathCoords.length - 1) ? (i + 1) % pathCoords.length : i + 1;
      const c3Index = ((i + 2) > pathCoords.length - 1) ? (i + 2) % pathCoords.length : i + 2;

      const c1 = pathCoords[i];
      const c2 = pathCoords[c2Index],
      const c3 = pathCoords[c3Index];

      // 2. For each 3 coords, enter two new path commands: Line to start of curve, bezier curve around corner.

      // Calculate curvePoint c1 -> c2
      const c1c2Distance = Math.sqrt(Math.pow(c1.x - c2.x, 2) + Math.pow(c1.y - c2.y, 2));
      const c1c2DistanceRatio = (c1c2Distance - curveRadius) / c1c2Distance;
      const c1c2CurvePoint = [
        ((1 - c1c2DistanceRatio) * c1.x + c1c2DistanceRatio * c2.x).toFixed(1),
        ((1 - c1c2DistanceRatio) * c1.y + c1c2DistanceRatio * c2.y).toFixed(1)
      ];

      // Calculate curvePoint c2 -> c3
      const c2c3Distance = Math.sqrt(Math.pow(c2.x - c3.x, 2) + Math.pow(c2.y - c3.y, 2));
      const c2c3DistanceRatio = curveRadius / c2c3Distance;
      const c2c3CurvePoint = [
        ((1 - c2c3DistanceRatio) * c2.x + c2c3DistanceRatio * c3.x).toFixed(1),
        ((1 - c2c3DistanceRatio) * c2.y + c2c3DistanceRatio * c3.y).toFixed(1)
      ];

      // If at last coord of polygon, also save that as starting point
      if (i === pathCoords.length - 1) {
        path.unshift('M' + c2c3CurvePoint.join(','));
      }

      // Line to start of curve (L endcoord)
      path.push('L' + c1c2CurvePoint.join(','));
      // Bezier line around curve (Q controlcoord endcoord)
      path.push('Q' + c2.x + ',' + c2.y + ',' + c2c3CurvePoint.join(','));
    }
    // Logically connect path to starting point again (shouldn't be necessary as path ends there anyway, but seems cleaner)
    path.push('Z');

    return path.join(' ');
}

Sie können die Rundungsstärke bestimmen, indem Sie die Variable edgeRadius oben einstellen . Der Standardwert ist 3 für ein Koordinatensystem mit 100 x 100 (Ansichtsfenster). Abhängig von der Größe Ihres SVG müssen Sie dies möglicherweise anpassen.

Mvin
quelle
1
Diese Mathematik ist großartig. Ich habe es verstanden und in Android implementiert, damit Polygone eine runde Ecke haben.
Adil Soomro
1
Deshalb liebe ich StackOverflow.
Bangkokian
5

Diese Frage ist das erste Ergebnis für Googeln "svg abgerundete Ecken Pfad". Der Vorschlag von Phrogz zur Verwendung strokeweist einige Einschränkungen auf (nämlich, dass ich den Strich nicht für andere Zwecke verwenden kann und dass die Abmessungen für die Strichbreite korrigiert werden müssen).

Jlange Vorschlag, eine Kurve zu verwenden, ist besser, aber nicht sehr konkret. Am Ende habe ich quadratische Bézier-Kurven zum Zeichnen abgerundeter Ecken verwendet. Betrachten Sie dieses Bild einer Ecke, die mit einem blauen Punkt und zwei roten Punkten an benachbarten Kanten markiert ist:

Ecke einer blau markierten Figur mit zwei Punkten an den angrenzenden Kanten

Die zwei Zeilen könnten mit dem LBefehl erstellt werden. Um diese scharfe Ecke in eine abgerundete Ecke zu verwandeln, zeichnen Sie eine Kurve vom linken roten Punkt (verwenden Sie M x,ydiese Option, um zu diesem Punkt zu gelangen). Jetzt hat eine quadratische Bézier-Kurve nur einen einzigen Kontrollpunkt, den Sie auf den blauen Punkt setzen müssen. Setzen Sie das Ende der Kurve auf den rechten roten Punkt. Da die Tangente an den beiden roten Punkten in Richtung der vorherigen Linien verläuft, sehen Sie einen fließenden Übergang, "abgerundete Ecken".

Um nun die Form nach der abgerundeten Ecke fortzusetzen, kann eine gerade Linie in einer Bézier-Kurve erreicht werden, indem der Kontrollpunkt zwischen auf der Linie zwischen den beiden Ecken festgelegt wird.

Um mir bei der Ermittlung des Pfads zu helfen, habe ich dieses Python-Skript geschrieben, das Kanten und einen Radius akzeptiert. Vektormathematik macht dies eigentlich sehr einfach. Das resultierende Bild aus der Ausgabe:

Form aus Skriptausgabe erstellt

#!/usr/bin/env python
# Given some vectors and a border-radius, output a SVG path with rounded
# corners.
#
# Copyright (C) Peter Wu <[email protected]>

from math import sqrt

class Vector(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def sub(self, vec):
        return Vector(self.x - vec.x, self.y - vec.y)

    def add(self, vec):
        return Vector(self.x + vec.x, self.y + vec.y)

    def scale(self, n):
        return Vector(self.x * n, self.y * n)

    def length(self):
        return sqrt(self.x**2 + self.y**2)

    def normal(self):
        length = self.length()
        return Vector(self.x / length, self.y / length)

    def __str__(self):
        x = round(self.x, 2)
        y = round(self.y, 2)
        return '{},{}'.format(x, y)

# A line from vec_from to vec_to
def line(vec_from, vec_to):
    half_vec = vec_from.add(vec_to.sub(vec_from).scale(.5))
    return '{} {}'.format(half_vec, vec_to)

# Adds 'n' units to vec_from pointing in direction vec_to
def vecDir(vec_from, vec_to, n):
    return vec_from.add(vec_to.sub(vec_from).normal().scale(n))

# Draws a line, but skips 'r' units from the begin and end
def lineR(vec_from, vec_to, r):
    vec = vec_to.sub(vec_from).normal().scale(r)
    return line(vec_from.add(vec), vec_to.sub(vec))

# An edge in vec_from, to vec_to with radius r
def edge(vec_from, vec_to, r):
    v = vecDir(vec_from, vec_to, r)
    return '{} {}'.format(vec_from, v)


# Hard-coded border-radius and vectors
r = 5
a = Vector(  0,  60)
b = Vector(100,   0)
c = Vector(100, 200)
d = Vector(  0, 200 - 60)

path = []
# Start below top-left edge
path.append('M {} Q'.format(a.add(Vector(0, r))))

# top-left edge...
path.append(edge(a, b, r))
path.append(lineR(a, b, r))
path.append(edge(b, c, r))
path.append(lineR(b, c, r))
path.append(edge(c, d, r))
path.append(lineR(c, d, r))
path.append(edge(d, a, r))
path.append(lineR(d, a, r))

# Show results that can be pushed into a <path d="..." />
for part in path:
    print(part)
Lekensteyn
quelle
3

Hier sind einige Pfade für Registerkarten:

https://codepen.io/mochime/pen/VxxzMW

<!-- left tab -->
<div>
  <svg width="60" height="60">
    <path d="M10,10 
             a10 10 0 0 1 10 -10
             h 50   
             v 47
             h -50
             a10 10 0 0 1 -10 -10
             z"
      fill="#ff3600"></path>
  </svg>
</div>

<!-- right tab -->
<div>
  <svg width="60" height="60">
    <path d="M10 0   
             h 40
             a10 10 0 0 1 10 10
             v 27
             a10 10 0 0 1 -10 10
             h -40
             z"
      fill="#ff3600"></path>
  </svg>
</div>

<!-- tab tab :) -->
<div>
  <svg width="60" height="60">
    <path d="M10,40 
             v -30
             a10 10 0 0 1 10 -10
             h 30
             a10 10 0 0 1 10 10
             v 30
             z"
      fill="#ff3600"></path>
  </svg>
</div>

Die anderen Antworten erklärten die Mechanik. Besonders gut hat mir die Antwort von Hossein-Maktoobian gefallen.

Die Pfade im Stift machen die Hauptlast der Arbeit. Die Werte können so geändert werden, dass sie den gewünschten Abmessungen entsprechen.

Jackie
quelle
1

Ich habe eine Lösung gefunden, aber sie ist etwas hackig, sodass sie möglicherweise nicht immer funktioniert. Ich fand heraus, dass ein Bogen (A oder a) mit wirklich kleinen Werten ihn zwingt, an einer Stelle eine Kurve zu erstellen, wodurch eine abgerundete Ecke entsteht ...

<svg viewBox="0 0 1 0.6" stroke="black" fill="grey" style="stroke-width:0.05px;">
  <path d="M0.7 0.2 L0.1 0.1 A0.0001 0.0001 0 0 0 0.099 0.101 L0.5 0.5Z"></path>
</svg>

Dennis Ranish
quelle
1

Um die Implementierung der Antwort von @ hmak.me zu vereinfachen, finden Sie hier einen kommentierten Teil des React-Codes zum Generieren abgerundeter Rechtecke.

const Rect = ({width, height, round, strokeWidth}) => {
    // overhang over given width and height that we get due to stroke width
    const s = strokeWidth / 2;

    // how many pixels do we need to cut from vertical and horizontal parts
    // due to rounded corners and stroke width
    const over = 2 * round + strokeWidth;

    // lengths of straight lines
    const w = width - over;
    const h = height - over;

    // beware that extra spaces will not be minified
    // they are added for clarity
    const d = `
        M${round + s},${s}
        h${w}
        a${round},${round} 0 0 1 ${round},${round}
        v${h}
        a${round},${round} 0 0 1 -${round},${round}
        h-${w}
        a${round},${round} 0 0 1 -${round},-${round}
        v-${h}
        a${round},${round} 0 0 1 ${round},-${round}
        z
    `;
    return (
        <svg width={width} height={height}>
            <path d={d} fill="none" stroke="black" strokeWidth={strokeWidth} />
        </svg>
    );
};

ReactDOM.render(
    <Rect width={64} height={32} strokeWidth={2} round={4} />,
    document.querySelector('#app'),
);

Jsfiddle Link.

polkovnikov.ph
quelle
-2
<?php
$radius = 20;
$thichness = 4;
$size = 200;

if($s == 'circle'){
  echo '<svg width="' . $size . '" height="' . $size . '">';
  echo '<circle cx="' . ($size/2) . '" cy="' . ($size/2) . '" r="' . (($size/2)-$thichness) . '" stroke="black" stroke-width="' . $thichness . '" fill="none" />';
  echo '</svg>';
}elseif($s == 'square'){
  echo '<svg width="' . $size . '" height="' . $size . '">';
  echo '<path d="M' . ($radius+$thichness) . ',' . ($thichness) . ' h' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 ' . $radius . ',' . $radius . ' v' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 -' . $radius . ',' . $radius . ' h-' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 -' . $radius . ',-' . $radius . ' v-' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 ' . $radius . ',-' . $radius . ' z" fill="none" stroke="black" stroke-width="' . $thichness . '" />';
  echo '</svg>';
}
?>
Monsenhor enVide neFelibata
quelle
-5

Sie verwenden ein Pfadelement. Warum geben Sie dem Pfad nicht einfach eine Kurve? Hier erfahren Sie, wie Sie mithilfe von Pfadelementen Kurven erstellen: http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands

RestingRobot
quelle
Danke für deine Antworten. Sie sind wirklich hilfreich, aber das Problem ist, dass ich KendoUI-Diagramme verwende und die Pfade dynamisch erstellt werden. Ich habe versucht, sie mit der Methode zu ändern, die Phrogz bietet, aber ich erhalte den Effekt border-radius = 10px, aber ich benötige border-top- linker Radius = 10px und Rand-unten-links-Radius = nur 10px. Ich bin wirklich neu in SVG, also die zweite Methode nicht für mich. Kannst du mir die Pfadkoordinaten schreiben? Vielen Dank im Voraus
Danis
So gerne ich dies für Sie tun würde, ich habe einfach keine Zeit, um die Mathe- / Koordinatenposition durchzugehen. Es sollte nicht zu schwierig sein, wenn Sie die elliptischen Bogenbefehle im Link verwenden.
RestingRobot