SVG закруглений кут


87

У мене є такий 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>

Я хочу , щоб отримати CSS-як border-top-right-radiusі border-top-bottom-radiusефект.

Як я можу досягти ефекту закругленого кута?


1
Шкода, що CSS border-radiusта його варіанти не працюють у SVG.
Стівен Вашон

9
До речі. Якщо у вас є прямокутник, ви можете просто додати rx=3або, ry=3щоб округлити кути. developer.mozilla.org/en-US/docs/Web/SVG/Attribute/rx
Lukas Liesis

Відповіді:


131

Ось як ви можете створити округлий прямокутник за допомогою SVG Path:

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

Пояснення

m100,100: переміщення до точки (100100)

h200: намалюйте горизонтальну лінію розміром 200 пікселів з того місця, де ми знаходимося

a20,20 0 0 1 20,20: намалюйте дугу з радіусом 20px X, радіусом 20px Y за годинниковою стрілкою до точки з різницею 20px по осях X та Y

v200: намалюйте вертикальну лінію на 200 пікселів з того місця, де ми перебуваємо

a20,20 0 0 1 -20,20: намалюйте дугу з радіусом 20px X та Y за годинниковою стрілкою до точки з різницею -20px у X та різницею 20px по осі Y

h-200: проведіть горизонтальну лінію -200px з місця, де ми знаходимося

a20,20 0 0 1 -20, -20: намалюйте дугу з радіусом 20px X і Y за годинниковою стрілкою до точки з різницею -20px в осі X і -20px в осі Y

v-200: проведіть вертикальну лінію -200px там, де ми знаходимося

a20,20 0 0 1 20, -20: намалюйте дугу з радіусом 20px X та Y за годинниковою стрілкою до точки з різницею 20px у X та -20px різницею по осі Y

z: закрити шлях

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


3
Для тих, кого цікавить детальна інформація про дугу, це API: A rx ry x-axis-rotation large-arc-flag sweep-flag x y( developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths )
Нік Скоццаро,

Якщо вам просто потрібен округлий прямокутник, а не більш складна фігура (саме так я знайшов це під час гуглиння), а простішим підходом може бути використання <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 = "stroke: # 000000; fill: #FFFFFF" /> `</svg>
Джон Сіблі

58

Не знаю, чому ніхто не опублікував фактичну відповідь SVG. Ось прямокутник SVG із закругленими кутами (радіус 3) зверху:

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

Це переміщення до (M), лінія до (L), дуга до (A), лінія до (L), дуга до (A), лінія до (L), закриття шляху (Z).

Числа, розділені комами, є абсолютними координатами. Дуги визначаються додатковими параметрами, що визначають радіус і тип дуги. Цього також можна досягти за допомогою відносних координат (використовуйте малі літери для L та A).

Повне посилання на ці команди знаходиться на сторінці W3C SVG Paths , а додаткові довідкові матеріали щодо шляхів SVG можна знайти в цій статті .


12
Це не безпосередньо відповідь, яку я шукав, але добрий бог, якщо вона не корисна. Завжди дивувався, для чого потрібні листи.
Alex McCabe

1
Подяка за пояснення :)
Осман Ерді,

50

Як зазначено у моїй відповіді на Застосування закруглених кутів до шляхів / багатокутників , я написав підпрограму в javascript для загального округлення кутів шляхів SVG, з прикладами, тут: http://plnkr.co/edit/kGnGGyoOCKil02k04snu .

Це буде працювати незалежно від будь-яких наслідків інсульту, які ви можете мати. Для використання включіть файл rounding.js з Plnkr і викличте функцію так:

roundPathCorners(pathString, radius, useFractionalRadius)

Результатом буде округлий шлях.

Результати виглядають так:

Приклади округлення шляху SVG


Приємно, хоча підтримка відносних команд була б ще приємнішою.
Йоахім Брайтнер,

1
Я згоден :) Це було лише невеликим разовим рішенням моєї проблеми, а не спробою повноцінної бібліотеки. Я вітаю виделку з такою функціональністю!
Yona Appletree

У вас є репо з цим? Це чудово, дякую, що зробили це.
Джаве

1
Я ніколи не турбувався зробити репо для цього ... хоча, мабуть, мав би.
Yona Appletree

Я хотів би повернутися до запитання @ Djave про репо; -]
t3chb0t

38

Ви явно встановити придатною stroke-linejoinдля roundале ваш stroke-widthTo 0, тому , звичайно , , ви не збираєтеся , щоб побачити закруглені кути , якщо у вас немає обведення раунд.

Ось модифікований приклад із закругленими кутами, зробленими за допомогою штрихів:
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" />

В іншому випадку - якщо вам потрібна справжня заливка округлої форми, а не просто округлений жирний штрих - ви повинні зробити те, що каже @Jlange, і зробити фактичну округлу форму.


Я бачу це правильно на jsfiddle, але при копіюванні в локальний HTML-документ це просто звичайний прямокутник.
Mads Skjern

6
Ви можете використовувати stroke-linecapзамість stroke-linejoin. Це працює для мене.
lobodart

33

Я б також розглянув можливість використання звичайного старого файлу, <rect>який містить атрибути rxтаry

MDN SVG docs <- зверніть увагу на другий намальований прямокутний елемент


2
Але ОП хоче, щоб лише деякі кути були закруглені.
Роберт Лонгсон,

9
Це відповідає на МОЄ запитання, саме це призвело мене до цієї сторінки. Отже, спасибі!
Стівен Вашон

1
Якщо вам потрібно використовувати закруглені кути для якоїсь групи елементів, а не лише для прямокутника, ви можете зробити це за допомогою clipPath developer.mozilla.org/pt-BR/docs/Web/SVG/Element/clipPath, як ви можете бачити тут jsfiddle.net/thiagomata/mp28rnj6/1
Тьяго Мата

Мертве посилання в OP. :(
posfan12

@ posfan12 Виправив це для вас :)
Джошуа

12

Я сам зіткнувся з цією проблемою сьогодні і зумів її вирішити, написавши невелику функцію JavaScript.

З того, що я можу сказати, немає простого способу надати елемент шляху в закруглених кутах SVG, за винятком того, що вам потрібно лише округлити межі, і в цьому випадку атрибути (CSS) stroke, stroke-widthа головне stroke-linejoin="round", цілком достатні.

Однак у моєму випадку я використовував об'єкт шляху для створення власних фігур з n кутами, які заповнені певним кольором і не мають видимих ​​меж, приблизно так:

введіть тут опис зображення

Мені вдалося написати швидку функцію, яка приймає масив координат для шляху SVG і повертає готовий рядок шляху до dатрибуту елемента path html. Отримана фігура буде виглядати приблизно так:

введіть тут опис зображення

Ось функція:

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

Ви можете визначити силу округлення, встановивши вгорі змінну curveRadius . Типовим значенням є 3 для системи координат 100x100 (viewport), але залежно від розміру SVG вам, можливо, доведеться це налаштувати.


1
Ця математика чудова. Я це зрозумів, і я застосував його в android, щоб полігони мали круглий кут.
Adil Soomro

Я зробив меншу та спрощену версію stackoverflow.com/a/65186378/4655042, яка, на мою думку, є більш елегантною.
janispritzkau

5

Це питання є першим результатом для Googling "svg округлених кутів шляху". Пропозиція Phrogz для використанняstroke має деякі обмеження (а саме те, що я не можу використовувати обведення для інших цілей, і що розміри повинні бути скореговані на ширину обведення).

Рекомендація Jlange використовувати криву краще, але не дуже конкретно. У підсумку я використав квадратичні криві Безьє для малювання закруглених кутів. Розгляньте це зображення кута, позначеного синьою крапкою та двома червоними точками на сусідніх краях:

кут фігури, позначений синім кольором з двома точками на сусідніх краях

Два рядки можна зробити за допомогою Lкоманди. Щоб перетворити цей гострий кут на закруглений, почніть малювати криву з лівої червоної точки (використовуйте M x,yдля переміщення до цієї точки). Тепер квадратна крива Безьє має лише одну контрольну точку, яку ви повинні встановити на синю точку. Встановіть кінець кривої в правій червоній точці. Оскільки дотична до двох червоних точок знаходиться у напрямку попередніх ліній, ви побачите плавний перехід, "закруглені кути".

Тепер, щоб продовжити форму після закругленого кута, можна досягти прямої лінії на кривій Безьє, встановивши контрольну точку між на лінії між двома кутами.

Щоб допомогти мені з визначенням шляху, я написав цей сценарій Python, який приймає краї та радіус. Векторна математика робить це насправді дуже простим. Отримане зображення з виводу:

форма, створена з виводу сценарію

#!/usr/bin/env python
# Given some vectors and a border-radius, output a SVG path with rounded
# corners.
#
# Copyright (C) Peter Wu <peter@lekensteyn.nl>

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)

3

Ось кілька шляхів для вкладок:

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>

Інші відповіді пояснювали механіку. Мені особливо сподобалась відповідь Хоссей-Мактобіана.

Шляхи в ручці роблять основну роботу, значення можна змінювати відповідно до бажаних розмірів.


1

Я знайшов рішення, але воно трохи хакерське, тому воно може не завжди працювати. Я виявив, що якщо у вас є дуга (A або a) з дійсно малими значеннями, вона змушує її створювати криву в одному місці, утворюючи таким чином округлий кут ...

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


1

Просто для спрощення реалізації відповіді @ hmak.me, ось коментований фрагмент коду React для створення округлих прямокутників.

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 посилання.


0

Це в основному те саме, що відповідає Mvins , але є більш стиснутим і спрощеним варіантом. Він працює, повертаючи назад відстань радіуса ліній, прилеглих до кута, і з'єднуючи обидва кінці кривою Безьє, контрольна точка якої знаходиться у вихідній точці кута.

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
}

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

-4

Ви використовуєте елемент шляху, чому б вам просто не дати кривій контуру? Подивіться тут, як зробити криві за допомогою елементів шляху: http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands


Дякую за відповіді. Вони дійсно корисні, але проблема в тому, що я використовую діаграми KendoUI, і шляхи створюються динамічно. Я намагався змінити їх методом, що пропонує Phrogz, але я отримую ефект border-radius = 10px, але мені потрібен border-top- радиус ліворуч = 10 пікс. та радіус знизу ліворуч = 10 пікс. Я справді новачок у SVG, тому другий метод не для мене. Тож чи можете ви написати для мене координати шляху. Попереднє спасибі
Даніс,

Як би я не хотів зробити це для вас, я просто не встигаю пройти математичне / координатне розташування. Це не повинно бути занадто важко, якщо ви використовуєте команди еліптичної дуги у посиланні.
RestingRobot
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.