SVG zaokrąglony róg

86

Mam następujący plik 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>

Chcę uzyskać CSS-jak border-top-right-radiusi border-top-bottom-radiusefektu.

Jak mogę uzyskać efekt zaokrąglonego narożnika?

Danis
źródło
1
Szkoda, że ​​CSS border-radiusi jego warianty nie działają w SVG.
Steven Vachon
9
BTW. Jeśli masz prostokąt, możesz po prostu dodać rx=3lub ry=3zaokrąglić rogi. developer.mozilla.org/en-US/docs/Web/SVG/Attribute/rx
Lukas Liesis

Odpowiedzi:

131

Oto jak możesz utworzyć zaokrąglony prostokąt ze ścieżką SVG:

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

Wyjaśnienie

m100,100: przejdź do punktu (100,100)

h200: narysuj 200px poziomą linię z miejsca, w którym się znajdujemy

a20,20 0 0 1 20,20: narysuj łuk o promieniu 20px X, promieniu 20px Y, zgodnie z ruchem wskazówek zegara, do punktu o 20px różnicy w osiach X i Y

v200: narysuj 200px pionową linię z miejsca, w którym się znajdujemy

a20,20 0 0 1 -20,20: narysuj łuk o promieniu 20px X i Y, zgodnie z ruchem wskazówek zegara, do punktu o -20px różnicy w X i 20px różnicy w osi Y

h-200: narysuj poziomą linię -200px z miejsca, w którym się znajdujemy

a20,20 0 0 1 -20, -20: narysuj łuk o promieniu 20px X i Y, zgodnie z ruchem wskazówek zegara, do punktu z -20px różnicą w X i -20px różnicy w osi Y

v-200: narysuj pionową linię -200px z miejsca, w którym się znajdujemy

a20,20 0 0 1 20, -20: narysuj łuk o promieniu 20px X i Y, zgodnie z ruchem wskazówek zegara, do punktu z różnicą 20px w X i -20px w osi Y

z: zamknij ścieżkę

<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
źródło
3
Dla każdego, kto jest zainteresowany szczegółami dotyczącymi łuku, jest to 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,
Jeśli potrzebujesz tylko zaokrąglonego prostokąta, a nie bardziej złożonego kształtu (tak to znalazłem podczas wyszukiwania w Google), prostszym podejściem może być użycie <svg viewBox="0 0 110 110" xmlns="http://www.w3.org/2000/svg"> „<rect x =" 5 "y =" 5 "width =" 100 "height = "100" rx = "15" style = "obrys: # 000000; fill: #FFFFFF" /> `</svg>
John Sibly
58

Nie jestem pewien, dlaczego nikt nie opublikował rzeczywistej odpowiedzi SVG. Oto prostokąt SVG z zaokrąglonymi narożnikami (promień 3) u góry:

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

To jest Ruch do (M), Linia do (L), Łuk do (A), Linia do (L), Łuk do (A), Linia do (L), Zamknij ścieżkę (Z).

Liczby oddzielone przecinkami są współrzędnymi bezwzględnymi. Łuki są definiowane za pomocą dodatkowych parametrów określających promień i typ łuku. Można to również osiągnąć za pomocą współrzędnych względnych (użyj małych liter dla L i A).

Pełne odniesienie do tych poleceń znajduje się na stronie Ścieżki SVG W3C , a dodatkowe materiały referencyjne na temat ścieżek SVG można znaleźć w tym artykule .

vallismortis
źródło
12
To nie jest bezpośrednia odpowiedź, której szukałem, ale dobry Boże, jeśli nie jest przydatna. Zawsze się zastanawiałem, do czego służą te litery.
Alex McCabe,
1
Dzięki za wyjaśnienie :)
Osman Erdi
49

Jak wspomniano w mojej odpowiedzi na temat Stosowanie zaokrąglonych rogów do ścieżek / wielokątów , napisałem procedurę w javascript do ogólnego zaokrąglania rogów ścieżek SVG, z przykładami, tutaj: http://plnkr.co/edit/kGnGGyoOCKil02k04snu .

Będzie działać niezależnie od wszelkich efektów udaru, które możesz mieć. Aby użyć, dołącz plik rounding.js z Plnkr i wywołaj funkcję w ten sposób:

roundPathCorners(pathString, radius, useFractionalRadius)

Rezultatem będzie zaokrąglona ścieżka.

Wyniki wyglądają następująco:

Przykłady zaokrąglania ścieżki SVG

Yona Appletree
źródło
Fajnie, chociaż obsługa poleceń względnych byłaby jeszcze przyjemniejsza.
Joachim Breitner
1
Zgadzam się :) To była tylko jednorazowa próba rozwiązania mojego problemu, a nie próba stworzenia pełnoprawnej biblioteki. Przyjąłbym widelec z taką funkcjonalnością!
Yona Appletree
Czy masz repozytorium z tym w tym? Wielkie dzięki za zrobienie tego.
Djave
1
Nigdy nie zadałem sobie trudu, aby zrobić repo ... choć prawdopodobnie powinienem.
Yona Appletree
Chciałbym wrócić do pytania @ Djave o repozytorium; -]
t3chb0t
38

Zostały jawnie ustawić stroke-linejoinaby roundale swoją stroke-widthTO 0, więc oczywiście nie zobaczysz zaokrąglonych rogów, jeśli nie masz skoku do zaokrąglenia.

Oto zmodyfikowany przykład z zaokrąglonymi rogami wykonanymi za pomocą pociągnięć:
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" />

W przeciwnym razie - jeśli potrzebujesz rzeczywistego wypełnienia o zaokrąglonym kształcie, a nie tylko zaokrąglonego tłustego obrysu - musisz zrobić to, co mówi @Jlange i zrobić rzeczywisty zaokrąglony kształt.

Phrogz
źródło
Widzę to poprawnie na jsfiddle, ale podczas kopiowania do lokalnego dokumentu HTML jest to zwykły prostokąt.
Mads Skjern,
6
Możesz użyć stroke-linecapzamiast stroke-linejoin. Mi to pasuje.
lobodart
33

Rozważyłbym również użycie zwykłego starego, <rect>który zawiera atrybuty rxiry

Dokumentacja MDN SVG <- zwróć uwagę na drugi narysowany element prostokątny

Joshua
źródło
2
Ale OP chce, aby tylko niektóre rogi były zaokrąglane.
Robert Longson,
9
To odpowiada na MOJE pytanie, które doprowadziło mnie do tej strony. Więc dziękuję!
Steven Vachon
1
Jeśli potrzebujesz użyć zaokrąglonych narożników w jakiejś grupie elementów, a nie tylko w prostocie, możesz to zrobić za pomocą clipPath developer.mozilla.org/pt-BR/docs/Web/SVG/Element/clipPath, jak widać tutaj jsfiddle.net/thiagomata/mp28rnj6/1
Thiago Mata
Martwy link w OP. :(
posfan12
@ posfan12 Naprawiliśmy to dla ciebie :)
Joshua
12

Sam napotkałem ten problem dzisiaj i udało mi się go rozwiązać, pisząc małą funkcję JavaScript.

Z tego, co wiem, nie ma łatwego sposobu na podanie elementu ścieżki w zaokrąglonych rogach SVG, z wyjątkiem sytuacji, gdy potrzebujesz tylko zaokrąglenia granic, w którym to przypadku atrybuty (CSS) stroke, stroke-widtha co najważniejszestroke-linejoin="round" są całkowicie wystarczające.

Jednak w moim przypadku użyłem obiektu ścieżki do stworzenia niestandardowych kształtów z n rogami, które są wypełnione określonym kolorem i nie mają widocznych granic, podobnie jak to:

wprowadź opis obrazu tutaj

Udało mi się napisać szybką funkcję, która pobiera tablicę współrzędnych dla ścieżki SVG i zwraca gotowy ciąg ścieżki do umieszczenia w datrybucie elementu html ścieżki. Wynikowy kształt będzie wtedy wyglądał mniej więcej tak:

wprowadź opis obrazu tutaj

Oto funkcja:

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

Możesz określić siłę zaokrąglenia, ustawiając zmienną curveRadius u góry. Domyślną wartością jest 3 dla układu współrzędnych 100x100 (rzutnia), ale w zależności od rozmiaru pliku SVG może być konieczne dostosowanie tego.

Mvin
źródło
1
Ta matematyka jest niesamowita. Zrozumiałem to i zaimplementowałem to w Androidzie, aby wielokąty miały okrągły róg.
Adil Soomro
Zrobiłem mniejszą i uproszczoną wersję stackoverflow.com/a/65186378/4655042, która moim zdaniem jest bardziej elegancka.
janispritzkau
5

To pytanie jest pierwszym wynikiem wyszukiwania w Google „ścieżki zaokrąglonych rogów svg”. Sugestia Phrogz do użyciastroke ma pewne ograniczenia (mianowicie, że nie mogę używać obrysu do innych celów i że wymiary muszą być skorygowane o szerokość obrysu).

Sugestia Jlange, aby użyć krzywej, jest lepsza, ale niezbyt konkretna. Skończyło się na użyciu kwadratowych krzywych Béziera do rysowania zaokrąglonych rogów. Rozważ to zdjęcie narożnika oznaczonego niebieską kropką i dwoma czerwonymi punktami na sąsiednich krawędziach:

róg figury oznaczony na niebiesko dwoma punktami na sąsiednich krawędziach

Dwie linie można utworzyć za pomocą Lpolecenia. Aby zmienić ten ostry narożnik w zaokrąglony, zacznij rysować krzywą od lewego czerwonego punktu (użyj, M x,yaby przejść do tego punktu). Teraz kwadratowa krzywa Béziera ma tylko jeden punkt kontrolny, który należy ustawić na niebieskim punkcie. Ustaw koniec krzywej w prawym czerwonym punkcie. Ponieważ styczna w dwóch czerwonych punktach jest skierowana w kierunku poprzednich linii, zobaczysz płynne przejście, „zaokrąglone rogi”.

Teraz, aby kontynuować kształt za zaokrąglonym rogiem, można uzyskać prostą linię na krzywej Béziera, ustawiając punkt kontrolny między na linii między dwoma rogami.

Aby pomóc mi w określeniu ścieżki, napisałem ten skrypt w Pythonie, który akceptuje krawędzie i promień. Matematyka wektorowa sprawia, że ​​jest to naprawdę bardzo łatwe. Wynikowy obraz z wyjścia:

kształt utworzony na podstawie danych wyjściowych skryptu

#!/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
źródło
3

Oto kilka ścieżek do kart:

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>

Inne odpowiedzi wyjaśniały mechanikę. Szczególnie podobała mi się odpowiedź hossein-maktoobian.

Najważniejsze są ścieżki w piórze, wartości można modyfikować w celu dostosowania do dowolnych pożądanych wymiarów.

Jackie
źródło
1

Znalazłem rozwiązanie, ale jest trochę hakerskie, więc może nie zawsze działać. Odkryłem, że jeśli masz łuk (A lub a) o naprawdę małych wartościach, zmusza to do utworzenia krzywej w jednym miejscu, tworząc w ten sposób zaokrąglony róg ...

<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
źródło
1

Aby uprościć implementację odpowiedzi @ hmak.me, oto skomentowany fragment kodu Reacta do generowania zaokrąglonych prostokątów.

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
źródło
0

Zasadniczo robi to to samo, co odpowiedź Mvinsa , ale jest bardziej skompresowaną i uproszczoną wersją. Działa poprzez cofnięcie odległości promienia linii sąsiadujących z rogiem i połączenie obu końców krzywą Beziera, której punkt kontrolny znajduje się w oryginalnym punkcie narożnym.

function createRoundedPath(coords, radius, close) {
  let path = ""
  const length = coords.length + (close ? 1 : -1)
  for (let i = 0; i < length; i++) {
    const a = coords[i % coords.length]
    const b = coords[(i + 1) % coords.length]
    const t = Math.min(radius / Math.hypot(b.x - a.x, b.y - a.y), 0.5)

    if (i > 0) path += `Q${a.x},${a.y} ${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`

    if (!close && i == 0) path += `M${a.x},${a.y}`
    else if (i == 0) path += `M${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`

    if (!close && i == length - 1) path += `L${b.x},${b.y}`
    else if (i < length - 1) path += `L${a.x * t + b.x * (1 - t)},${a.y * t + b.y * (1 - t)}`
  }
  if (close) path += "Z"
  return path
}
janispritzkau
źródło
-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
źródło
-4

Używasz elementu ścieżki, dlaczego po prostu nie nadasz ścieżce krzywej? Zobacz tutaj, jak tworzyć krzywe przy użyciu elementów ścieżki: http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands

OdpoczynekRobot
źródło
Dziękuję za odpowiedzi. Są naprawdę pomocne, ale problem w tym, że używam wykresów KendoUI i ścieżki tworzą się dynamicznie, próbowałem je zmienić metodą, która oferuje Phrogz, ale otrzymuję efekt border-radius = 10px, ale potrzebuję border-top- left-radius = 10px i border-bottom-left-radius = tylko 10px. Naprawdę nowy w SVG, więc druga metoda nie jest dla mnie. Czy możesz napisać dla mnie współrzędne ścieżki. Z góry dziękuję
Danis
Chociaż bardzo chciałbym to dla ciebie zrobić, po prostu nie mam czasu, aby przejść przez matematykę / lokalizację współrzędnych. Nie powinno być zbyt trudne, jeśli użyjesz poleceń łuku eliptycznego w łączu.
RestingRobot