在地图指针之后,让我们来尝试下更多有趣的SVG Path形状:一个tooltip。点击查看例子
tooltip形状的path路径是由5个变量决定的:width
,height
,pointer offset
,corner radius
和placement
(left,top,right或bottom)。
简单来说,我们先来完成一个没有圆角的向上的tooltip,我们需要这么做: 1、移动到点A:M aX,aY 2、连线到点B:L bX,bY 3、垂直连线到点C:H cX 4、水平连线到点D:V dY 5、垂直连线到点E:H eX 6、水平连线到点F:V fY 7、垂直连线到点G:H gX 8、连线到点A:L aX,aY 9、结束路径:z
点A的坐标是(0,0),其他点的坐标由width,height和offset计算出来:
1 2 3 4 5 6 bx = -offset by = -offset cx = -width / 2 dy = -offset - height ex = width / 2 gx = offset
整个路径实现出来就会是这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function topTooltipPath (width, height, offset ) { const left = -width / 2 const right = width / 2 const top = -offset - height const bottom = -offset return `M 0,0 L ${-offset} ,${bottom} H ${left} V ${top} H ${right} V ${bottom} H ${offset} L 0,0 z` }
上面的代码解释下就是:画笔移动到点M,再画直线到点B,再水平画直线到点C,再垂直画直线到点D,再水平画直线到点E,再垂直画直线到点F,再水平画直线到点G,再画直线到点M,并结束。 需要注意的是坐标系是左上角为原点,x轴正方向朝右,y轴正方向朝下。
实现圆角,我们有2个方案:弧(Arc)和二次贝塞尔曲线(Quadratic Bezier curve)。 弧需要使用7个变量,但是二次贝塞尔曲线就要简单得多:它在当前路径上取得起点,另外2个点是顶点(vX,vY)
和终点(tX,tY)
。在SVG中,二次贝塞尔曲线的写法是Q vX,vY tX,tY
。
拿圆角C来举例,设圆角半径是r,圆角的起点在B到C的水平直线上,,故起点坐标是(cX+r, cY)
,终点在出圆角去点D的垂直直线上,故终点坐标是(cX,cY-r)
,顶点就是点C(cX,cY)
,把上面例子中的直角变成圆角,我们只需要把H cX V dY
换成H cX+r Q cX, cY cX, (cY - r) V dY
,这样就得到了半径是r
的圆角C
,下面是优化后的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function topTooltipPath (width, height, offset, radius ) { const left = -width / 2 const right = width / 2 const top = -offset - height const bottom = -offset return `M 0,0 L ${-offset} ,${bottom} H ${left + radius} Q ${left} ,${bottom} ${left} ,${bottom - radius} V ${top + radius} Q ${left} ,${top} ${left + radius} ,${top} H ${right - radius} Q ${right} ,${top} ${right} ,${top + radius} V ${bottom - radius} Q ${right} ,${bottom} ${right - radius} ,${bottom} H ${offset} L 0,0 z` }
获得圆角的向下的tooltip,我们只需要把路径中的Y坐标全部取反:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function bottomTooltipPath (width, height, offset, radius ) { const left = -width / 2 const right = width / 2 const bottom = offset + height const top = offset return `M 0,0 L ${-offset} ,${top} H ${left + radius} Q ${left} ,${top} ${left} ,${top + radius} V ${bottom - radius} Q ${left} ,${bottom} ${left + radius} ,${bottom} H ${right - radius} Q ${right} ,${bottom} ${right} ,${bottom - radius} V ${top + radius} Q ${right} ,${top} ${right - radius} ,${top} H ${offset} L 0,0 z` }
同样的可以得到朝左和朝右的tooltip:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function leftTooltipPath (width, height, offset, radius ) { const left = -offset - width const right = -offset const top = -height / 2 const bottom = height / 2 return `M 0,0 L ${right} ,${-offset} V ${top + radius} Q ${right} ,${top} ${right - radius} ,${top} H ${left + radius} Q ${left} ,${top} ${left} ,${top + radius} V ${bottom - radius} Q ${left} ,${bottom} ${left + radius} ,${bottom} H ${right - radius} Q ${right} ,${bottom} ${right} ,${bottom - radius} V ${offset} L 0,0 z` } function rightTooltipPath (width, height, offset, radius ) { const left = offset const right = offset + width const top = -height / 2 const bottom = height / 2 return `M 0,0 L ${left} ,${-offset} V ${top + radius} Q ${left} ,${top} ${left + radius} ,${top} H ${right - radius} Q ${right} ,${top} ${right} ,${top + radius} V ${bottom - radius} Q ${right} ,${bottom} ${right - radius} ,${bottom} H ${left + radius} Q ${left} ,${bottom} ${left} ,${bottom - radius} V ${offset} L 0,0 z` }
参考