CSS动画中的贝塞尔曲线

前言

最近在学习CSS动画,其中动画时间函数的部分涉及到了贝塞尔曲线的相关知识。对于这部分知识,之前一直没有好好学习过,正好借着这个机会学习下。

1. 贝塞尔曲线

首先简单介绍下贝塞尔曲线。

贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段节点 组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线 的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。 ——《百度百科》

贝塞尔曲线在图形学中广泛应用,是非常重要的曲线。那么如何绘制一条贝塞尔曲线呢?

线性曲线

给定点P0、P1,线性贝塞尔曲线只是一条两点之间的直线。这条线由下式给出:

B ( t ) = P 0 + ( P 1 P 0 ) t = ( 1 t ) P 0 + t P 1 , t [ 0 , 1 ] B(t) = P_0 + (P_1-P_0)t = (1-t)P_0 + tP_1, t\in[0, 1]

线性贝塞尔曲线函数中的t会经过由 P 0 P_0 P 1 P_1 B ( t ) B(t) 所描述的曲线。例如当 t = 0.25 t=0.25 时,即 B ( t ) B(t) 一条由点 P 0 P_0 P 1 P_1 径的四分之一处。就像由0至1的连续 t t B ( t ) B(t) 描述一条由 P 0 P_0 P 1 P_1 的直线。

Bezier_1_big.gif

二次曲线

二次方贝塞尔曲线的路径由给定点 P 0 P_0 P 1 P_1 P 2 P_2 的函数 B ( t ) B(t) 追踪:

B ( t ) = ( 1 t 2 ) P 0 + 2 t ( 1 t ) P 1 + t 2 P 2 , t [ 0 , 1 ] B(t) = (1-t^2)P_0 + 2t(1-t)P_1 + t^2P_2 , t\in[0, 1]

为建构二次贝塞尔曲线,可以中介点 Q 0 Q_0 Q 1 Q_1 作为由0至1的 t t

  • P 0 P_0 P 1 P_1 的连续点 Q 0 Q_0 ,描述一条线性贝塞尔曲线。
  • P 1 P_1 P 2 P_2 的连续点 Q 1 Q_1 ,描述一条线性贝塞尔曲线。
  • Q 0 Q_0 Q 1 Q_1 的连续点 B ( t ) B(t) ,描述一条二次贝塞尔曲线。

Bezier_2_big.svg.png

此处可以进行公式推导:

Q 0 ( t ) = ( 1 t ) P 0 + t P 1 / / P 0 P 1 可得 Q 0 Q_0(t) = (1-t)P_0 + tP_1 // 由P_0-P_1可得Q_0
Q 1 ( t ) = ( 1 t ) P 1 + t P 2 / / P 1 P 2 可得 Q 1 Q_1(t) = (1-t)P_1 + tP_2 // 由P_1-P_2可得Q_1
B ( t ) = ( 1 t ) Q 0 ( t ) + t Q 1 ( t ) = 1 t 2 P 0 + 2 t ( 1 t ) P 1 + t 2 P 2 B(t) = (1-t)Q_0(t) + tQ_1(t) = (1-t)^2P_0 + 2t(1-t)P_1 + t^2P_2

Bezier_2_big.gif

三次曲线

P 0 P_0 P 1 P_1 P 2 P_2 P 3 P_3 四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于 P 0 P_0 走向 P 1 P_1 ,并从 P 2 P_2 的方向来到 P 3 P_3 。一般不会经过 P 1 P_1 P 2 P_2 ;这两个点只是在那里提供方向信息。 P 0 P_0 P 1 P_1 之间的间距,决定了曲线在转而趋进 P 2 P_2 之前,走向 P 1 P_1 方向的“长度有多长”。

480px-Bezier_3_big.svg.png

可以利用之前的线性公式和二阶曲线公式进行推导:

R 0 ( t ) = 1 t 2 P 0 + 2 t ( 1 t ) P 1 + t 2 P 2 / / P 0 P 1 P 2 可得 R 0 R_0(t) =(1-t)^2P_0 + 2t(1-t)P_1 + t^2P_2 //由P_0-P_1-P_2可得R_0
R 1 ( t ) = 1 t 2 P 1 + 2 t ( 1 t ) P 2 + t 2 P 3 / / P 1 P 2 P 3 可得 R 1 R_1(t) =(1-t)^2P_1 + 2t(1-t)P_2 + t^2P_3 //由P_1-P_2-P_3可得R_1
B ( t ) = ( 1 t ) R 0 ( t ) + t R 1 ( t ) = P 0 ( 1 t ) 3 + 3 P 1 t ( 1 t ) 2 + 3 P 2 t 2 ( 1 t ) + P 3 t 3 , t [ 0 , 1 ] B(t) = (1-t)R_0(t) + tR_1(t) = P_0(1-t)^3 + 3P_1t(1-t)^2 + 3P_2t^2(1-t)+P_3t^3, t\in[0,1]

Bezier_3_big.gif

2. CSS中的贝塞尔曲线

在CSS中,应用到贝塞尔曲线的属性主要有transitionanimation

transition中有 transition-timing-function ,animation中有 animation-timing-function

这两个属性类型相同,包含一个或多个类型的值。语法如下:

<easing-function>#
where 
<easing-function> = linear | <cubic-bezier-timing-function> | <step-timing-function>

where 
<cubic-bezier-timing-function> = ease | ease-in | ease-out | ease-in-out | cubic-bezier(<number [0,1]>, <number>, <number [0,1]>, <number>)
<step-timing-function> = step-start | step-end | steps(<integer>[, <step-position>]?)

where 
<step-position> = jump-start | jump-end | jump-none | jump-both | start | end

其中我们重点关注<cubic-bezier-timing-function>类型。可以看到该类型有五个值:

  • ease
  • ease-in
  • ease-out
  • ease-in-out
  • cubic-bezier(<number [0,1]>, <number>, <number [0,1]>, <number>)

前四个关键字都是简写,最后一个则是可以通过cubic-bezier()函数生成自定义的贝塞尔曲线。

cubic-bezier()

cubic-bezier()函数定义了一个三次贝塞尔曲线。三次贝塞尔曲线通过四个点 P 0 P_0 P 1 P_1 P 2 P_2 P 3 P_3 来定义。 P 0 P_0 P 3 P_3 是曲线的起点和终点,在CSS中起点和终点都是固定的, P 0 P_0 ( 0 , 0 ) (0, 0) ,表示初始时间或位置以及初始状态, P 3 P_3 ( 1 , 1 ) (1, 1) 表示最终时间或位置以及最终状态。

语法如下:

cubic-bezier(x1, y1, x2, y2)

x 1 x_1 y 1 y_1 定义了 P 1 P_1 点的横纵坐标, x 2 x_2 y 2 y_2 定义了 P 2 P_2 点的横纵坐标。其中 x 1 x_1 x 2 x_2 的范围必须在 [ 0 , 1 ] [0,1] 区间内,否则这个曲线就是无效的。

CSS中如果声明了无效的三次贝塞尔曲线,那么整个属性的都声明都会被无视。

Untitled 2.png

上图所示为cubic-bezier(0.075, 0.75, 0.875, 0.36)声明的曲线。

ease

Untitled 3.png

ease关键字等同于cubic-bezier(0.25, 0.1, 0.25, 1.0),特点是曲线刚开始加速较快,结束时平稳。

ease-in

Untitled 4.png

ease-in关键字等同于cubic-bezier(0.42, 0.0, 1.0, 1.0),特点是曲线刚开始较为平稳,结束时加速剧烈。

ease-out

Untitled 5.png

ease-out关键字等同于cubic-bezier(0.0, 0.0, 0.58, 1.0),特点是曲线开始就突然加速,结束时平稳减速。

ease-in-out

Untitled 6.png

ease-in-out关键字等同于cubic-bezier(0.42, 0.0, 0.58, 1.0)。特点是曲线开始时加速缓慢平稳,表现和ease-in相同;结束时缓慢平稳减速,表现和ease-out相同。

3. 贝塞尔曲线的应用

在CSS动画中,贝塞尔曲线的横坐标代表时间比率,纵坐标代表输出比率。

所以在某个时间点的斜率就代表动画的运动速度,曲线越陡峭,速度越快;曲线越平缓,速度则越慢。我们可以通过自定义曲线来做不同的运动动画。

cubic-bezier.com/ 这个网站可以帮助我们快速生成自定义的贝塞尔曲线,非常的实用:

Untitled 7.png

比较经典的例子是,将参考点 P 1 P_1 设置在第四象限,让曲线先向下走再向上走:

Untitled 8.png

利用这样的特殊的贝塞尔曲线,可以生成具有回弹效果的运动轨迹:

.circle {
    position: relative;
    left: 100px;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background: deepskyblue;
    animation: jump 3s cubic-bezier(.6,-0.47,.63,1) forwards;
}
@keyframes jump {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(300px);
    }
}

效果如下:

2022-06-23_17.42.20.gif

也可以为opacity属性添加动画,制作出先显示,再隐藏,再显示的动画:

Untitled 9.png

.visible {
    position: relative;
    top: 100px;
    width: 100px;
    height: 100px;
    background: black;
    animation: visible 5s cubic-bezier(.14,.33,.16,-0.88) both;
}
@keyframes visible {
    0% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}

效果如下:

2022-06-23_20.20.57.gif

总结

本文介绍了线性贝塞尔曲线、二次贝塞尔曲线以及三次贝塞尔曲线,没有介绍高阶贝塞尔曲线是因为在CSS中只用到cubic-bezier()函数,也就是三次贝塞尔曲线。在CSS transitionanimation中使用贝塞尔曲线可以制作多种多样的缓动动画。

除了CSS动画,在图形绘制中也经常使用贝塞尔曲线,多一些了解对于在日常的开发也会有很多的帮助,大家不妨多多尝试~

猜你喜欢

转载自juejin.im/post/7112631311122038791