我想出的解决方案涉及使用 SVG Q 命令,该命令绘制二次贝塞尔曲线。出现在d 属性中的主要语法是:
Q x,y px,py
其中x,y 是曲线的控制点,px,py 是曲线的终点。我觉得这张照片很有帮助:
所以为了让这条曲线看起来像肘部两侧的自然延续,曲线起点和终点的导数必须等于两条直线的斜率。
让我们考虑一个<path>,它在 P(145,75) 处形成一个尖角:
<path d="M 20,40 L 145,75 L 35,175.5" stroke="black"/>
按照上图,那个角就是二次曲线的控制点。您现在想要在距顶点一定距离处停止直线L,根据用户输入计算,这将是二次贝塞尔曲线的起点。终点将是上面显示的px,py。
因此我们可以像这样修改我们的路径(使用字符串插值):
<path d=`M 20,40 L ${bezierStart} Q ${vertex} ${bezierEnd} L 35,175.5` stroke="black"/>
图形化:
为了计算所有这些,您只需要三个点来标识以您要圆整的顶点为根的三角形。计算是基于this Math.SE post,只是这里提到的d/dt比率是已知的:它来自用户。
在我的例子中,我让用户输入整数,就好像他们在设置 border-radius 属性一样,然后除以 100,以便用户输入转换为与顶点的偏移量。
函数如下(在Typescript中):
type Point = [number,number]
type QBezierAnchors = {
vertex: Point
start: Point
end: Point
}
// p: corner to round off
// a1: left anchor
// a2: right anchor
// as if the polygonal shape is drawn from a1 -> p -> a2
// r: user-supplied radius
export function quadraticBezier(p: Point, a1: Point, a2: Point, r: number): QBezierAnchors {
// the corner to round is also the control point of the quadratic bezier
const ctrl = p
// formula for finding point p at a certain distance from p0 on a line that passes through p1
// px = (1-t)p0x + tp1x
// py = (1-t)p0y + tp1y
// t is ratio of distances d/dt where d = distance(p0,p1) and dt = distance(p0,p)
// but in our case we already know the ratio because it's set by the user
const t = r / 100
const start = [((1-t)*p[0] + (t*a1[0])), ((1-t)*p[1] + (t*a1[1]))]
const end = [((1-t)*p[0] + (t*a2[0])), ((1-t)*p[1] + (t*a2[1]))]
return {
vertex: ctrl,
start,
end
}
}
然后只需将此函数的输出格式化为<path> 元素,就可以了。 (可运行的 sn-p 即将推出)