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
Лукас Лисис

Ответы:

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: перейти к точке (100,100)

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

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

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

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

h-200: нарисуйте горизонтальную линию -200 пикселей от того места, где мы находимся

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

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

a20,20 0 0 1 20, -20: нарисуйте дугу с радиусом 20 пикселей по X и Y, по часовой стрелке, до точки с разницей в 20 пикселей по оси X и -20 пикселей по оси 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>

hmak.me
источник
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 )
Ник Скоццаро
Если вам просто нужен прямоугольник с закругленными углами, а не более сложная форма (именно так я нашел это при поиске в Google), более простой подход может заключаться в использовании <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
У вас есть репо с этим? Это здорово, большое спасибо за это.
Djave
1
Я никогда не удосужился сделать репо по нему ... хотя, наверное, должен.
Yona Appletree
Я хотел бы вернуться к вопросу @Djave по поводу репо; -]
t3chb0t
38

Вы явно указали stroke-linejoinна, roundно не stroke-widthна 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, и создать фактическую закругленную форму.

Phrogz
источник
Я правильно вижу это в jsfiddle, но при копировании в локальный HTML-документ это просто прямоугольник.
Mads Skjern
6
Вы можете использовать stroke-linecapвместо stroke-linejoin. Меня устраивает.
lobodart
33

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

MDN SVG docs <- обратите внимание на второй нарисованный прямоугольный элемент

Джошуа
источник
2
Но OP хочет, чтобы были закруглены только некоторые углы.
Роберт Лонгсон 01
9
Это отвечает на МОЙ вопрос, который привел меня на эту страницу. Тогда спасибо!
Стивен Вашон,
1
Если вам нужно использовать закругленные углы в некоторой группе элементов, а не только в прямоугольнике, вы можете сделать это с помощью clipPath developer.mozilla.org/pt-BR/docs/Web/SVG/Element/clipPath, как вы можете видеть здесь. jsfiddle.net/thiagomata/mp28rnj6/1
Тьяго Мата
Мертвая ссылка в ОП. :(
posfan12
@ posfan12 Исправил это для вас :)
Джошуа
12

Сегодня я сам столкнулся с этой проблемой, и мне удалось решить ее, написав небольшую функцию JavaScript.

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

Однако в моем случае я использовал объект пути для создания пользовательских фигур с n углами, которые заполнены определенным цветом и не имеют видимых границ, примерно так:

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

Мне удалось написать быструю функцию, которая принимает массив координат для пути SVG и возвращает готовую строку пути для вставки в dатрибут элемента пути 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 (область просмотра), но в зависимости от размера вашего SVG вам может потребоваться настроить это.

Мвин
источник
1
Эта математика потрясающая. Я понял это и реализовал в android, чтобы полигоны были скругленными.
Адиль Соомро,
Я сделал уменьшенную и упрощенную версию stackoverflow.com/a/65186378/4655042, которая, на мой взгляд, более элегантна.
janispritzkau
5

Этот вопрос является первым результатом поиска в Google "пути с закругленными углами 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)
Lekensteyn
источник
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.

polkovnikov.ph
источник
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>';
}
?>
Monsenhor enVide neFelibata
источник
-4

Вы используете элемент контура, почему бы просто не придать контуру кривую? См. Здесь, как создавать кривые с использованием элементов пути: http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands

ОтдыхающийРобот
источник
Спасибо за ответы. Они действительно полезны, но проблема в том, что я использую диаграммы KendoUI, а пути создаются динамически. Я пытался изменить их с помощью метода, который предлагает Phrogz, но я получаю эффект border-radius = 10px, но мне нужен border-top- только left-radius = 10px и border-bottom-left-radius = 10px. Я действительно новичок в SVG, поэтому второй метод не для меня. Так что можешь написать мне координаты пути. Заранее спасибо
Данис
Как бы мне ни хотелось сделать это для вас, у меня просто нет времени разбираться в математических / координатных точках. Это не должно быть слишком сложно, если вы используете команды эллиптической дуги в ссылке.
RestingRobot