SVG rounded corner

I have the following 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>

I want to get a CSS-like border-top-right-radius and border-top-bottom-radius effect.

How can I achieve that rounded corner effect?

203401 次浏览

You are using a path element, why don't you just give the path a curve? See here for how to make curves using path elements: http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands

你已经明确地将你的 stroke-linejoin设置为 round,但是你的 stroke-width设置为 0,所以如果你没有划圆的行程,你就不会看到 of course的圆角。

下面是一个修改过的例子,通过笔画画出圆角:
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 所说的做出一个实际的圆形。

这个问题是谷歌搜索“ svg 圆角路径”的第一个结果。Phrogz 建议使用 stroke有一些限制(即,我不能使用笔画的其他目的,尺寸必须为笔画宽度校正)。

Jlange 建议使用曲线比较好,但不是很具体。我最终使用二次 Bézier 曲线绘制圆角。考虑下面这张图片,一个角上有一个蓝点,两个红点在相邻的边上:

corner of a figure marked blue with two points on the adjacent edges

可以使用 L命令生成这两行代码。要将这个尖角变成圆角,从左边的红点开始绘制一条曲线(使用 M x,y移动到该点)。现在二次 Bézier 曲线只有一个控制点,你必须设置在蓝点上。将曲线的末端设置为右边的红点。由于两个红点的切线方向与前面的线相同,你会看到一个流畅的过渡,“圆角”。

现在继续形状后的圆角,一条直线在贝塞尔曲线可以实现通过设置控制点之间的线两个角之间。

为了帮助我确定路径,我编写了这个 Python 脚本,它接受边和半径。矢量数学让这个变得非常简单。输出的结果图像:

shape created from script output

#!/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)

我还会考虑使用一个提供 rxry属性的普通老式 <rect>

MDN SVG docs <-注意第二个绘制的 rect 元素

正如我在对 Applying rounded corners to paths/polygons的回答中所提到的,我已经用 javascript 编写了一个 SVG 路径通常圆角的例程,这里有一些例子: http://plnkr.co/edit/kGnGGyoOCKil02k04snu

它将独立于你可能产生的任何中风效应而工作。使用时,包含 Plnkr 中的 ounding.js 文件,并像下面这样调用函数:

roundPathCorners(pathString, radius, useFractionalRadius)

结果将是圆形路径。

结果是这样的:

SVG Path Rounding Examples

不知道为什么没人发布真正的 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) ,线到(L) ,关闭路径(Z)。

逗号分隔的数字是绝对坐标。弧的定义带有指定弧半径和弧类型的附加参数。这也可以通过相对坐标来实现(用小写字母表示 L 和 A)。

这些命令的完整参考资料在 W3CSVG 路径页面上,关于 SVG 路径的其他参考资料可以在 这篇文章中找到。

Here is how you can create a rounded rectangle with 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: < em > 移动到点(100,100)

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

A 20,2000120,20: < em > 画一个半径为20px X,半径为20px Y 的圆弧,顺时针方向画一个 X 轴和 Y 轴相差20px 的点

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

a20,20 0 0 1 -20,20: draw an arc with 20px X and Y radius, clockwise, to a point with -20px difference in X and 20px difference in Y axis

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

A 20,20001-20,-20: < em > 沿顺时针方向画一条半径为20px 的弧线,到 X 轴差为 -20px,Y 轴差为 -20px 的点

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

a20,20 0 0 1 20,-20: draw an arc with 20px X and Y radius, clockwise, to a point with 20px difference in X and -20px difference in Y axis

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>

下面是一些制表符的路径:

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-maktobian 的答案。

钢笔中的路径是工作的主要部分,值可以被修改以适应任何需要的尺寸。

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

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

然而,在我的例子中,我使用了一个 path 对象来创建具有 N角的自定义形状,这些角用特定的颜色填充,并且没有可见的边界,就像下面这样:

enter image description here

I managed to write a quick function that takes an array of coordinates for an SVG path and returns the finished path string to put in the d attribute of the path html element. The resulting shape will then look something like this:

enter image description here

函数如下:

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

您可以通过在顶部设置 曲线半径变量来确定舍入强度。100x100(viewport)的默认坐标系是3,但是根据 SVG 的大小,您可能需要调整这个值。

I found a solution but it is a 有点粗俗 so it may not always work. I found that if you have an arc (A or a) with really small values it forces it to create a curve in one spot thus forming a rounded comer...

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

为了简化@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 链接。

这基本上与 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
}

对于我的情况,我需要半径开始和结束 path:

enter image description here

With stroke-linecap: round; I change it to what I want:

enter image description here

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

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/

我编写了这个小的打印脚本函数,这样我就可以动态地为一个复杂的圆角矩形创建路径,这个函数类似于带 border-radius的 div 函数。

Export 函数 round dedRectPath ( 数字, 数字, 宽度: 数字, 身高: 数字, 斜角[ number,number,number,number ] = [3,3,3,3] ) : string { 返回“ M”+ x + “ ,”+ y + m 0 ${ bevel [0]}’ + ‘ q 0-${斜面[0]} ${斜面[0]}-${斜面[0]}’ + ‘ l ${ width-bevel [0]-bevel [1]}0’ + ‘ q ${斜面[1]}0 ${斜面[1]} ${斜面[1]’ + ‘ l 0 ${ height-bevel [1]-bevel [2]}’ + ‘ q 0 ${ bevel [2]}-${ bevel [2]} ${ bevel [2]}’ + ‘ l-${ width-bevel [2]-bevel [3]}0’ + ‘ q-${斜面[3]}0-${斜面[3]}-${斜面[3]}’ + ‘ z’; }