4、路径绘制进阶:贝塞尔曲线实战、弧线命令A、路径组合技巧
好,咱们继续往下走。上一章我们把直线和矩形这些基础路径摸透了,但说真的,真正的 SVG 动画魅力,全藏在曲线里。你想想看,一个圆角按钮、一条流动的波浪线、一个平滑的过渡动画——哪个离得开贝塞尔曲线?
我个人习惯把路径绘制分成三个阶段:能画直线 → 能画曲线 → 能组合出复杂图形。今天我们就卡在第二阶段和第三阶段的交界处。我会把贝塞尔曲线、弧线命令 A,还有路径组合的技巧一次性讲透。
4.1 贝塞尔曲线:从理论到肌肉记忆
贝塞尔曲线这东西,说白了就是用几个控制点来「拉扯」一条曲线。SVG 里给了我们两种:二次贝塞尔(Q/q) 和 三次贝塞尔(C/c)。
我在项目中遇到过最典型的场景:用三次贝塞尔画一个平滑的波浪线,用来做数据可视化的趋势图。当时我直接用直线连数据点,结果被设计师怼了——「这锯齿感也太强了吧?」嗯,从那以后我就老老实实用贝塞尔了。
4.1.1 二次贝塞尔 Q/q:一个控制点就够了
语法很简单:Q x1 y1, x y。其中 (x1, y1) 是控制点,(x, y) 是终点。起点就是你当前画笔的位置。
<!-- 从 (50,100) 到 (200,100),控制点在 (125,20) -->
<path d="M 50 100 Q 125 20, 200 100" fill="none" stroke="#4A90D9" stroke-width="3"/>
你想想看,这个控制点就像一根绳子拽着曲线往它那边偏。控制点离起点和终点的连线越远,曲线拱得越高。
4.1.2 三次贝塞尔 C/c:两个控制点,更灵活
三次贝塞尔有两个控制点:C x1 y1, x2 y2, x y。第一个控制点影响曲线起点的方向,第二个影响终点的方向。
<!-- 一个 S 形曲线 -->
<path d="M 50 150 C 100 50, 150 250, 200 150" fill="none" stroke="#E74C3C" stroke-width="3"/>
为什么会这样?因为第一个控制点 (100,50) 把曲线往右上角拉,第二个控制点 (150,250) 又把它往右下角拽。两个力一结合,就扭出了 S 形。
我记得有一次做加载动画,需要一条波浪线来回流动。用三次贝塞尔配合 animate 标签,让控制点随时间变化,效果特别丝滑。代码其实不复杂,但视觉冲击力很强。
c(小写)时,控制点和终点的坐标都是相对于当前画笔位置的。如果你连续画多条曲线,很容易算错偏移量。我的建议是:复杂路径先用绝对坐标 C 调试,稳定后再考虑优化成相对坐标。
4.2 弧线命令 A:画圆角、画扇形、画一切
弧线命令 A 可能是 SVG 路径里最让人头疼的一个。参数多,逻辑绕。但一旦你理解了它的「几何直觉」,你会发现它其实很优雅。
语法:A rx ry x-axis-rotation large-arc-flag sweep-flag x y
七个参数,一个都不能少。我们来拆解一下:
| 参数 | 含义 | 说明 |
|---|---|---|
| rx, ry | 椭圆弧的 x 和 y 半径 | 如果 rx=ry,画的是正圆弧 |
| x-axis-rotation | 椭圆相对于 x 轴的旋转角度 | 通常设为 0,除非你要画斜椭圆 |
| large-arc-flag | 是否取大弧(0 或 1) | 0 取小弧,1 取大弧 |
| sweep-flag | 弧的方向(0 逆时针,1 顺时针) | 决定了弧线往哪边弯 |
| x, y | 弧线的终点 | 起点是当前画笔位置 |
你可能会问:为什么需要 large-arc-flag 和 sweep-flag 两个标志位?
原因很简单:给定起点、终点和半径,理论上可以画出四条不同的弧线(大弧/小弧 × 顺时针/逆时针)。这两个标志位就是用来精确指定你要哪一条。
<!-- 画一个半圆:从 (50,100) 到 (150,100),半径 50 -->
<path d="M 50 100 A 50 50 0 0 1 150 100" fill="none" stroke="#2ECC71" stroke-width="3"/>
<!-- 画另一半圆(逆时针) -->
<path d="M 150 100 A 50 50 0 0 1 50 100" fill="none" stroke="#E67E22" stroke-width="3"/>
4.3 路径组合技巧:把碎片拼成艺术品
单个路径命令能画的东西有限。真正的实战,是把直线、曲线、弧线组合成一个完整的 <path>。
4.3.1 连续绘制:不用重复 M
很多人刚开始会犯一个错误:每画一段就写一个 M。其实没必要。只要你不提笔(不写 M),路径就是连续的。
<!-- 错误示范:每段都写 M -->
<path d="M 10 10 L 50 10 M 50 10 Q 80 0, 100 20" />
<!-- 正确做法:连续绘制 -->
<path d="M 10 10 L 50 10 Q 80 0, 100 20" />
你想想看,这样不仅代码更简洁,而且动画过渡也更自然。如果你用 stroke-dasharray 做描边动画,连续的路径会一笔画完,断开的话就会出现「断笔」效果。
4.3.2 闭合路径 Z:省掉最后一条线
当你画完一个封闭图形时,用 Z 或 z 来自动连接终点和起点。这比手动写一条直线要精确得多——它保证连接处完全重合,不会出现微小的缝隙。
<!-- 画一个圆角矩形 -->
<path d="M 20 20 L 80 20 Q 100 20, 100 40 L 100 80 Q 100 100, 80 100 L 20 100 Q 0 100, 0 80 L 0 40 Q 0 20, 20 20 Z" />
4.3.3 子路径:一个 path 画多个图形
一个 <path> 里可以包含多个子路径,每个子路径以 M 开头。这在做图标时特别有用——比如一个「加号」图标,其实就是两条交叉的线。
<!-- 加号图标:两条线在一个 path 里 -->
<path d="M 10 30 L 50 30 M 30 10 L 30 50" stroke="#333" stroke-width="6" stroke-linecap="round"/>
为什么要放在一个 path 里?因为你可以统一控制描边颜色、宽度、动画效果。如果拆成两个 path,动画同步起来就麻烦多了。
4.4 实战:画一个带圆角的云朵图标
光说不练假把式。我们来组合一下今天学的所有技巧,画一个云朵图标。
<path d="
M 40 80
Q 20 80, 20 60
Q 20 40, 40 40
Q 40 20, 60 20
Q 80 20, 80 40
Q 100 40, 100 60
Q 100 80, 80 80
Z
" fill="#B0D4F1" stroke="#4A90D9" stroke-width="3"/>
这个云朵由 6 段二次贝塞尔曲线组成。每一段 Q 都控制一个弧形的凸起。你仔细看:起点在左下角 (40,80),然后往上拱到 (20,60),再拱到 (40,40)……最后用 Z 闭合。
我在项目中用过这个云朵做天气动画。配合 animateTransform 让它缓缓飘动,再叠加一个透明度变化,效果非常治愈。
4.5 本章小结
今天我们干了三件事:
- 贝塞尔曲线:Q 一个控制点画简单弧,C 两个控制点画复杂曲线。记住控制点就是「拉力点」。
- 弧线命令 A:七个参数,核心是 large-arc-flag 和 sweep-flag 的组合。多练几次就能形成肌肉记忆。
- 路径组合:连续绘制、Z 闭合、子路径——这些技巧能让你的代码更干净,动画更流畅。
下一章我们会进入 SVG 动画的核心——animate 和 animateTransform。到时候你会发现,今天学的这些路径知识,就是动画的「骨架」。骨架搭好了,动起来才自然。
嗯,今天就到这里。去打开你的编辑器,画一朵云、一个 S 形曲线、或者一个圆角矩形。动手试试,比看十遍都管用。