【问题标题】:SVG: How to round a vertex of a polygonal path based on user inputSVG:如何根据用户输入对多边形路径的顶点进行舍入
【发布时间】:2022-07-08 17:11:35
【问题描述】:

假设我在 SVG 中有一个(可能是闭合的)多边形,即相邻的直线。我想对这个形状的任意顶点进行四舍五入,类似于 CSS border-radius 属性的效果。

我知道<rect> 支持rxry 属性,但我的形状不一定是矩形。

我知道<polygon><polyline> 元素不支持半径,一个简单的解决方案是设置更粗的笔划 (Svg polygon rounding)。但较粗的笔划适用于该形状的所有顶点。相反,我想对任意顶点进行四舍五入。

我也知道我可以将<path> (SVG rounded corner) 与A 命令一起使用。但是A 命令绘制椭圆弧。我希望顶点看起来平滑。

这是我想要的一个例子。基本上,一个肘部:

此外,我希望此肘部的“半径”取决于用户输入。换句话说,用户可以选择肘部的圆度。

【问题讨论】:

    标签: typescript svg rounded-corners


    【解决方案1】:

    我想出的解决方案涉及使用 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
        }
    }
    

    然后只需将此函数的输出格式化为&lt;path&gt; 元素,就可以了。 (可运行的 sn-p 即将推出)

    【讨论】:

      猜你喜欢
      • 2016-03-27
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      • 2019-12-09
      • 2013-04-21
      • 2022-01-16
      • 2018-10-14
      • 1970-01-01
      相关资源
      最近更新 更多