【问题标题】:SVG rounded cornerSVG圆角
【发布时间】:2021-12-18 10:00:58
【问题描述】:

我有以下 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-radiusborder-top-bottom-radius 效果。

我怎样才能实现那种圆角效果?

【问题讨论】:

  • CSS'border-radius 及其变体在 SVG 中不起作用,这太糟糕了。
  • 顺便说一句。如果你有一个矩形,你可以添加 rx=3ry=3 来圆角。 developer.mozilla.org/en-US/docs/Web/SVG/Attribute/rx
  • 如果您的stroke-width&gt; 0(与上面的OP svg 不同):将&lt;path&gt; 角的"lines" 圆角,然后只需使用css: stroke-linejoin:round; 。 . .对行 ends 执行相同操作,然后也使用 stroke-linecap:round;。文档:stroke-linejoinstroke-linecap

标签: xml image svg vector-graphics


【解决方案1】:

以下是使用 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" />

说明

m100,100: 移动到点(100,100)

h200: 从我们所在的位置画一条 200 像素的水平线

a20,20 0 0 1 20,20: 顺时针画一条 X 半径为 20px,Y 半径为 20px 的圆弧,到 X 轴和 Y 轴相差 20px 的点 em>

v200: 从我们所在的位置画一条 200 像素的垂直线

a20,20 0 0 1 -20,20: 顺时针画一条 X 和 Y 半径为 20px 的圆弧,到 X 轴差为 -20px,Y 轴差为 20px 的点轴

h-200: 从我们所在的位置画一条 -200px 的水平线

a20,20 0 0 1 -20,-20: 顺时针绘制一条 X 和 Y 半径为 20px 的圆弧,到 X 和 -20px 差为 -20px 的点在 Y 轴上

v-200: 从我们所在的位置画一条 -200px 的垂直线

a20,20 0 0 1 20,-20: 顺时针画一条 X 和 Y 半径为 20px 的圆弧,到 X 轴差 20px 和 Y 轴差 -20px 的点轴

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>

【讨论】:

  • 对于任何对 arc 的更多细节感兴趣的人,这是 API:A rx ry x-axis-rotation large-arc-flag sweep-flag x y (developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths)
  • 如果你只想要一个圆角矩形而不是更复杂的形状(这是我在谷歌搜索时发现的),更简单的方法可能是使用 &lt;svg viewBox="0 0 110 110" xmlns="http://www.w3.org/2000/svg"&gt; ` `&lt;/svg&gt;
【解决方案2】:

不知道为什么没有人发布实际的 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 路径的其他参考资料可以在 this article 中找到。

【讨论】:

  • 请注意,使用相对坐标会更好,使用专用的hl 指令用于水平/垂直直线:m0,0 v27 a3,3 0 00 3,3 h4 a3,3 0 00 3,-3 v-27 z
【解决方案3】:

正如我对Applying rounded corners to paths/polygons 的回答中所引用的,我已经用 javascript 编写了一个例程,用于对 SVG 路径进行一般圆角,示例如下:http://plnkr.co/edit/kGnGGyoOCKil02k04snu

它将独立于您可能拥有的任何中风效果而工作。要使用,请包含 Plnkr 中的 rounding.js 文件并像这样调用函数:

roundPathCorners(pathString, radius, useFractionalRadius)

结果将是圆角路径。

结果如下所示:

【讨论】:

  • 很好,虽然对相关命令的支持会更好。
  • 我同意 :) 这只是一次性解决我的问题,而不是尝试使用成熟的库。我欢迎具有该功能的分叉!
  • 你有这个的回购吗?太好了,非常感谢制作它。
  • 我从来没有费心为它做一个回购......虽然我可能应该这样做。
  • 我想回到@Djave 关于回购的问题;-]
【解决方案4】:

您已将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所说的做一个实际的圆形。

【讨论】:

  • 我在 jsfiddle 上正确地看到了这一点,但是当复制到本地 HTML 文档时它只是一个普通的矩形。
  • 您可以使用stroke-linecap 代替stroke-linejoin。它对我有用。
【解决方案5】:

我还考虑使用普通的旧 &lt;rect&gt;,它提供 rxry 属性

MDN SVG docs

【讨论】:

【解决方案6】:

我今天自己也遇到了这个问题,并设法通过编写一个小的 JavaScript 函数来解决它。

据我所知,如果您只需要圆角边框,则没有简单的方法可以在 SVG 圆角 except 中给出路径元素,在这种情况下,(CSS)属性strokestroke-width 和最重要的是 stroke-linejoin="round" 完全足够了。

但是,在我的例子中,我使用了一个路径对象来创建带有 n 个角的自定义形状,这些角用某种颜色填充并且没有可见的边框,就像这样:

我设法编写了一个快速函数,它采用 SVG 路径的坐标数组并返回完成的路径字符串以放入路径 html 元素的 d 属性中。生成的形状将如下所示:

函数如下:

/**
 * 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 变量来确定舍入强度。对于 100x100(视口)坐标系,默认值为 3,但根据 SVG 的大小,您可能需要进行调整。

【讨论】:

  • 这个数学真棒。我明白了,我已经在 android 中实现了它,使多边形具有圆角。
  • 我做了一个更小更简化的版本stackoverflow.com/a/65186378/4655042,我认为它更优雅。
【解决方案7】:

这个问题是谷歌搜索“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)

【讨论】:

    【解决方案8】:

    以下是标签的一些路径:

    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>

    其他答案解释了机制。我特别喜欢 hossein-maktoobian 的回答。

    笔中的路径首当其冲,可以修改值以适应任何所需的尺寸。

    【讨论】:

      【解决方案9】:

      对于我的情况,我需要以 path 的开头和结尾为半径:

      使用stroke-linecap: round;,我将其更改为我想要的:

      【讨论】:

        【解决方案10】:

        为了简化@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 link.

        【讨论】:

          【解决方案11】:

          这与Mvins answer 基本相同,但是是一个更加压缩和简化的版本。它的工作原理是返回与拐角相邻的线的半径距离,并用控制点在原始拐角点的贝塞尔曲线连接两端。

          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
          }
          

          【讨论】:

            【解决方案12】:

            我找到了一个解决方案,但它有点老套,所以它可能并不总是有效。我发现如果你有一个值非常小的弧(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>

            【讨论】:

              【解决方案13】:

              这是一段反应代码,用于生成具有不同圆角半径的矩形:

              const Rect = ({width, height, tl, tr, br, bl}) => {
                  const top = width - tl - tr;
                  const right = height - tr - br;
                  const bottom = width - br - bl;
                  const left = height - bl - tl;
                  const d = `
                      M${tl},0
                      h${top}
                      a${tr},${tr} 0 0 1 ${tr},${tr}
                      v${right}
                      a${br},${br} 0 0 1 -${br},${br}
                      h-${bottom}
                      a${bl},${bl} 0 0 1 -${bl},-${bl}
                      v-${left}
                      a${tl},${tl} 0 0 1 ${tl},-${tl}
                      z
                  `;
                  return (
                      <svg width={width} height={height}>
                          <path d={d} fill="black" />
                      </svg>
                  );
              };
              
              ReactDOM.render(
                  <Rect width={200} height={100} tl={20} tr={0} br={20} bl={60} />,
                  document.querySelector('#app'),
              );
              

              https://jsfiddle.net/v1Ljpxh7/

              【讨论】:

                【解决方案14】:

                我编写了这个小打字稿函数,因此我可以动态地为一个复杂的圆角矩形创建路径,该矩形的功能类似于带有border-radius 的 div。

                export function roundedRectPath( x: number, y: number, width: number, height: number, bevel: [number, number, number, number] = [3, 3, 3, 3] ): string { return "M" + x + "," + y + `m 0 ${bevel[0]}` + `q 0 -${bevel[0]} ${bevel[0]} -${bevel[0]}` + `l ${width - bevel[0] - bevel[1]} 0` + `q ${bevel[1]} 0 ${bevel[1]} ${bevel[1]}` + `l 0 ${height - bevel[1] - bevel[2]}` + `q 0 ${bevel[2]} -${bevel[2]} ${bevel[2]}` + `l -${width - bevel[2] - bevel[3]} 0` + `q -${bevel[3]} 0 -${bevel[3]} -${bevel[3]}` + `z`; }

                【讨论】:

                  【解决方案15】:

                  您正在使用路径元素,为什么不给路径一条曲线?请参阅此处了解如何使用路径元素制作曲线:http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands

                  【讨论】:

                  • 感谢您的回答。它们真的很有帮助,但问题是我使用 KendoUI 图表并且路径是动态创建的。我尝试使用提供 Phrogz 的方法来更改它们,但我得到了边框半径 = 10px 效果,但我需要边框顶部-仅left-radius =10px 和border-bottom-left-radius =10px。我真的是 SVG 的新手,所以第二种方法不适合我。所以你能为我写路径坐标吗?提前致谢
                  • 尽管我很乐意为您做这件事,但我根本没有时间浏览数学/坐标位置。如果你使用链接中的椭圆弧命令应该不会太难。
                  猜你喜欢
                  • 1970-01-01
                  • 2021-12-27
                  • 2013-11-23
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多