前一段时间一直在搞毕业的事情,在经历了反反复复的论文修改之后,终于要和大学说再见了,想想还有点伤感那。回来之后除了项目中的事情,也抽空看了一下 SVG 当中的贝塞尔曲线(Bézier curve)这个知识点。今天整理一下这部分的内容。

一、贝塞尔曲线是干嘛的?

贝塞尔曲线在计算机图形中具有非常重要的作用,具体的历史背景 维基百科 上有详细的介绍。贝塞尔曲线早期用于汽车的工业设计,还可以用来绘制图形与轨迹。实际上,贝塞尔曲线对于前端同学来说,还是挺重要的。这无论是在 CSS3 的 animation-timing-function 运动还是 canvas 的 bezierCurveTo 当中都使用到了贝塞尔曲线。

在 SVG 中 PATH 绘制图形就使用到了贝塞尔曲线。比如我告诉你我画了一条直线,其实我只需要告诉你这条直线的起始与终止这两个坐标就好了,计算机就能绘制这条直线。

如何表示一条直线

直线很好理解,但是如果我告诉你我画了一条曲线,你又如何知道这条曲线是什么形状?

如何表示一条曲线

很快就有人发现曲线的轨迹其实是有规律可循的,通过起始终止坐标还有其他点坐标的『约束』,就能完美复制一份曲线。贝塞尔曲线分为线性曲线、二次方曲线、三次方曲线等,这里只分析前三种。

二、线性贝塞尔曲线

所谓的线性贝塞尔曲线其实就是一根直线(曲线包括直线),通过给定 P0、P1 两点,那么这条直线可以根据下面这个公式推导出来。

线性贝塞尔曲线方程

P0 点代表了这条『曲线』的起始坐标,P1 点代表了这条曲线的终点坐标,t 是一个大于 0 小于 1 的数,可以抽象的认为 t 代表了这条曲线的进度,而 B 其实就是公示所代表的曲线。

线性方贝塞尔曲线演变

为了方便理解 B 的位置,我用一张 SVG 动画来演示在线性贝塞尔曲线运动当中 B 所处的位置。点击 Run 查看 B 的运动轨迹。↓↓↓

P0 ↑ 我是位置B P1 t = 0.08

通过上图的 SVG 可以很清楚的看到随着进度 t 的不断累加,B 的位置在不断的变化,而 B 所走过的这条路,其实就是线性贝塞尔曲线所表示的线条。实际上这个过程是一个很简单的直线方程求解的问题,已知两点的坐标求出他们之间的连线。和套用两点式方程式一个道理。假设起点坐标(15、15),终点坐标(350、150)。

x = (1 - t) * 15 + t * 350;  
y = (1 - t) * 15 + t * 150;  
P0 ↑ 我是位置B P1 t = 0.08

B 位置计算

  • x轴坐标 = (1 - t) * 15 + t * 350;
  • y轴坐标 = (1 - t) * 15 + t * 150;
  • 根据 t 值得改变,所以就能推算出线条的轨迹。线性贝塞尔曲线相对简单,如果想用 SVG 实现直接使用 PATH 的 L 指令就好了。

    三、二次方贝塞尔曲线

    二次方贝塞尔曲线要相对于线性贝塞尔曲线复杂一点,二次方贝塞尔曲线的路径需要三点(P0、P1、P2)来指定。

    二次方贝塞尔曲线

    P0、P1、P2的位置关系是怎么样的?维基百科的这张 gif 图表示的非常的直观,曲线经过了 P0 与 P2 两点,P1 点的存在确定了这条曲线的形状。

    二次方贝塞尔曲线

    已知 P0、P1、P2 而 t 是一个恒小于 1 大于 0 的数,由此可以推导出 B 也是一个取值范围。为了方便演示根据 P1 的位置而决定的 B 曲线的形状,我用 SVG 做了一副二次贝塞尔曲线的动画。拖拽下图中的 P1 点,查看 P1 点位置对于二次贝塞尔曲线的影响。

    P1点 ↑ B点位置 P0点 P2点

    根据 P1 点位置的不同,B 这条轨迹也会被不断重绘。那么在 SVG 当中如何用 PATH 来表示这条曲线?使用 PATH 来绘制二次贝塞尔曲线需要用到 Q 和 T 这两个指令。

    指令 作用 使用
    Q 制作二次贝塞尔曲线 Q (x1 y1 x y)+
    T 制作光滑的二次贝塞尔曲线 Q (x y)+

    所以也就是其实不需要考虑 P0 点的作用,只需要知道 P1 点与 P2 点的坐标就能实现二次贝塞尔曲线。

    P0 P1(x1, y1) P2(x2, y2)

    Q x1 y1, x2 y2  
    

    而平滑的二次方贝塞尔曲线 T 是为了让已经执行过 Q 命令的曲线平滑过渡,T 指令是专门给 Q 指令进行辅助作用的, w3.org 上有这样一段描述:T 命令的作用点是根据前一个 Q 命令的反射而自动计算出来的。

    Note that the control point for the "T" command is computed automatically as the reflection of the control point for the previous "Q" command relative to the start point of the "T" command.

    P0 P1(x1, y1) P2(x2, y2) P3(x3, y3)

    所以 T 命令的使用方法就是下面这种,注意 T 必须紧接着 Q 命令才可以使用。

    T x3 y3  
    

    四、三次贝塞尔曲线

    既然已经有了二次贝塞尔曲线,我们已经能够实现曲线的制作,哪为啥还要花时间去学习三次贝塞尔曲线?这是因为三次贝塞尔曲线要比二次贝塞尔曲线有更多的控制点,通过这些控制点的约束,三次贝塞尔曲线制作出来的路径要比二次贝塞尔灵活的多。

    比如我想用 SVG 来画一个 R 这个字母,在使用二次贝塞尔曲线与三次贝塞尔曲线的作用下,你觉得下面哪一个更像 R?

    ←二次贝塞尔曲线←三次贝塞尔曲线

    三次贝塞尔曲线公式要比二次的当然还要复杂一点,其实不需要完全知道这个公式背后的原理,前期会套用这个公式就可以了。

    三次贝塞尔曲线

    与二次贝塞尔曲线相同,t 都是一个恒小于 1 大于 0 的数,P0 代表的是起点、P1、P2 则是三次贝塞尔曲线的控制点,而 P3 是当前这条曲线的终点坐标。随着 t 的不断进行,B 所经过的轨迹就是绘制的三次贝塞尔曲线。维基百科的这张 gif 图很能说明问题。

    三次贝塞尔曲线位置

    在 SVG PATH 当中使用三次贝塞尔曲线需要用到 C 和 S 指令。

    指令 作用 使用
    C 制作三次贝塞尔曲线 C (x1 y1 x2 y2 x y)+
    S 制作光滑的三次贝塞尔曲线 S (x2 y2 x y)+

    为了方便演示三次贝塞尔曲线,我写了一个简单的动态生成三次贝塞尔曲线的例子,拖拽下图中的 P1 与 P2 点,查看SVG 曲线的变化。

    ←P1点(x1, y1)←P2点(x2, y2)←P0点↑P3点(x, y)

    SVG 三次贝塞尔曲线指令

  • C (x1 y1 x2 y2 x y)+
  • C 140 220, 140 70, 300 70
  • 五、总结

    理解贝塞尔曲线还是非常重要的,在绘制图形运动轨迹方面有非常重要作用。贝塞尔曲线其实不只有线性、二次、三次贝塞尔曲线这几种。但是对于常规的 SVG 绘制,三次贝塞尔已经能够满足需求了。

    这个月时间被压榨的紧,前端菜鸟进行中,终于要在下周毕业了。