Creating custom easing effects in CSS animations using the linear() function. Learn how to have more control over your CSS animations by using the linear() function. A vibrant gradient behind artwork of CSS and a line graph chart.

使用 linear() 函数在 CSS 动画中创建自定义缓动效果

作者头像Michelle Barker阅读时间:11 分钟

动画不仅仅是将事物从一个地方移动到另一个地方。如何移动(或以某种方式更改)对于传达目的感同样重要。在动画世界中,对象在一段时间内从一种状态过渡到另一种状态的方式称为缓动

在这篇文章中,我们将比较 CSS 中的不同缓动函数。CSS 中的linear()函数是一个新的缓动函数,它使我们能够更好地控制动画的制作。我们将探讨linear()函数的工作原理,并查看一些可以使用的实际示例。

CSS 中的缓动

正确的缓动类型对于使动画看起来自然和逼真至关重要。想象一下在一个地板上滚动一个球。它不会一直以相同的速度移动:它可能会迅速加速到最大速度,然后逐渐减速并停止(称为三次贝塞尔缓动)。将其与在整个动画时间线上以相同速度移动的球体进行比较(称为线性缓动)。感觉不太自然,是吗?

控制 CSS 动画中的缓动

在 CSS 中,我们使用animation-timing-function属性将缓动应用于元素的动画。

css
.box {
  animation-name: move;
  animation-duration: 2000ms;
  animation-timing-function: ease-in;
}

让我们回顾一些已有的 CSS 缓动技术。如果您已经熟悉这些内容,可以安全地跳过本节,并继续了解有关使用linear()函数应用自定义缓动的部分。

缓动关键字

在 CSS 中制作动画时,我们可以从许多关键字中进行选择:ease-inease-outease-in-outease(默认值)和linear。它们也可以通过简写animation属性应用。

css
.box {
  animation: move 2000ms ease-in;
}

Ease-in(缓入)

动画开始缓慢,然后加速,最终达到最大速度。例如,汽车起步。

The curve of the ease-in timing function which starts slowly and accelerates smoothly in progress over time.

Ease-out(缓出)

动画开始快速,然后在一段时间内减速——有点像球在木板上滚动。这里的区别是ease-out没有显示初始加速。

The curve of the ease-out timing function which starts fast and decelerates smoothly in progress over time.

Ease-in-out(缓入缓出)

动画同时使用上述两个原理:开始时逐渐加速,然后向结尾减速。

The curve of the ease-in-out timing function which accelerates and then decelerates smoothly in progress over time, combining the ease-in and ease-out functions.

Ease(缓动)

这是默认值。与ease-in-out类似,既有加速也有减速,但它们并不均匀。在这种情况下,动画有时可能看起来比ease-in-out更自然。

以下是两个关键字的效果并排比较

The curve of the ease timing function which starts slowly, accelerates sharply, and then slows gradually towards the end.

Linear(线性)

在整个动画过程中速度没有变化。尽管通常不太逼真,但线性缓动在 UI 动画中很有用,例如滚动的一排徽标或图像。

The curve of the linear timing function without any points which is a straight line at a constant speed.

缓动函数

缓动关键字对于简单的 UI 元素(例如,应用于按钮的悬停状态)很有用,但它们并不总是足以满足更复杂的动画或我们需要更好地控制速度的情况。CSS 为我们提供了一些用于制作自定义缓动的函数:steps()cubic-bezier()

Steps(分步)

steps()函数将输入时间划分为指定数量的间隔——因此我们的动画将从一个值“跳转”到下一个值。我们可以使用许多可能的值指定步数和跳转位置(可选)。本文的目的不是深入探讨steps()缓动,但我建议阅读有关分步缓动函数的内容,以了解不同值的影响。

css
.box {
  animation-timing-function: steps(3, jump-none);
}

The curve of the steps() timing function given 'steps(3, jump-none)' which progresses in three equal steps from start to finish.

Cubic-bezier(三次贝塞尔)

cubic-bezier()函数使我们能够更好地控制 CSS 动画中的缓动。该函数采用四个值,它们对应于三次贝塞尔曲线的两个控制点。

我们可以使用cubic-bezier()创建更逼真的效果,例如使元素在静止前稍微超出其最终位置。

css
.ball {
  animation-timing-function: cubic-bezier(0.57, 0.4, 0.55, 1.17);
}

The curve of the cubic-bezier() timing function given 'cubic-bezier(0.57, 0.4, 0.55, 1.17)', giving it a slow start, sharp acceleration, and a slight overshoot before coming to rest.

如果您了解其背后的数学原理,则可以非常有创意地定义自己的自定义曲线,例如 Temani Afif 在这篇文章使用 cubic-bezier() 的高级 CSS 动画中所做的那样!

幸运的是,我们不需要成为数学天才就能使用cubic-bezier()制作令人愉悦的缓动:Lea Verou 的三次贝塞尔工具允许我们调整和可视化自定义缓动曲线并导出结果。

三次贝塞尔缓动函数的局限性

尽管cubic-bezier()用途广泛,但它也有其局限性,因为我们只能控制三次贝塞尔曲线的两个点。

假设我们希望元素从左向右滑动,然后以递减的距离和速度弹跳几次,然后停止。如果我们查看easings.net,我们可以看到一系列可能适合我们不同场景需求的缓动曲线(或者可以调整为适合这些需求)。不幸的是,不可能使用cubic-bezier()重新创建所有这些曲线。

使用关键帧应用自定义缓动

实现自定义缓动的一种方法是使用关键帧动画。我们可以为元素的确切位置定义许多关键帧,并整体应用线性缓动(因为我们的缓动实际上是由关键帧决定的)。

css
@keyframes easeOutElastic {
  0% { transform: translateX(0%); }
  16% { transform: translateX(-132.27%); }
  28% { transform: translateX(-86.88%); }
  44% { transform: translateX(-104.63%); }
  59% { transform: translateX(-98.36%); }
  73% { transform: translateX(-100.58%); }
  88% { transform: translateX(-99.8%); }
  100% { transform: translateX(-100%); }
}

注意:此关键帧示例取自easings.net的“easeOutElastic”示例。

这感觉有点笨拙,而且不容易调整以满足我们的需求。还有一个问题:我们的动画只能朝一个方向播放。如果我们想从右到左应用相同的动画,则需要创建另一组关键帧。

介绍线性时序函数

与其使用关键帧将缓动构建到动画中,不如使用新的 CSSlinear()函数来创建完全自定义的缓动。

linear()函数(不要与上面介绍的linear关键字混淆)需要一个以逗号分隔的停止列表,其值范围从01。这些停止点沿时间线均匀分布。linear(0, 1)的值等效于linear关键字,其中动画在整个持续时间内的进度率没有变化。

传入三个值为00.751的停止点意味着在时间段的 50% 处,动画将完成 75% 的进度。

css
.box {
  animation-timing-function: linear(0, 0.75, 1);
}

The curve of a linear() timing function when given 'linear(0, 0.75, 1)' which progresses at a constant rate from start to finish through the given stops.

将此缓动应用于translate动画的结果是,元素在动画持续时间的前半部分看起来移动得比后半部分快。

或者,让我们尝试向缓动函数传递一个负值。

css
.box {
  animation-timing-function: linear(0, -0.1, 0.75, 1);
}

The curve of a linear() timing function when given 'linear(0, -0.1, 0.75, 1)' which progresses at a constant rate from start to finish through the provided stops.

我们看到动画元素在被推向终点之前会稍微反向移动一点。从0-0.1所需的时间与从-0.10.75所需的时间相同。

我们还添加了一个额外的停止点,这意味着与前面的示例相比,到达每个停止点所需的时间减少了:在持续时间为 1 秒的动画中,每个停止点将花费 1/3 秒,而不是第一个示例中的 0.5 秒。

起始和结束停止点

我们的停止点列表不必从 0 到 1。我们可以从时间线上的稍后位置开始动画,并且动画将从该位置以相同的持续时间播放。在这里您可以看到第二个和第三个方块花费相同的时间来走第一个方块一半的距离,从时间线上的不同位置开始和结束。

css
.box {
  animation: slide 3000ms linear(0, 1);
}

.box:nth-child(2) {
  animation-timing-function: linear(0.5, 1);
}

.box:nth-child(3) {
  animation-timing-function: linear(0, 0.5);
}

停止点长度

我们可以通过额外传入停止点长度来控制动画持续时间内的停止点位置。如果我们希望元素到达第二个停止点值的时间不是 33.33%(当有四个停止点时),而是持续时间的 20%,我们可以在linear()函数中指定该值。

css
.box {
  animation-timing-function: linear(0, -0.1 20%, 0.75, 1);
}

The curve of a linear() timing function given 'linear(0, -0.1 20%, 0.75, 1)'. The percentage value makes the second stop occur at 20% of the animation's duration instead of at 33.33% if the stops were evenly spaced.

我们还可以给停止点一个可选的结束值。

css
.box {
  animation-timing-function: linear(0, -0.1 20% 40%, 0.75, 1);
}

我们的动画将在持续时间的 20% 处到达第二个停止点值,暂停直到 40%,然后继续进行到结束。

缓动曲线现在如下所示

The curve of a linear() timing function when given 'linear(0, -0.1 20% 40%, 0.75, 1)'. The percentage values make the second stop occur at 20% of the animation's duration, stay there until 40% of the duration, then progress through the remaining stops evenly spaced.

您可能会注意到,一旦我们添加了停止点长度,其余的停止点就会在剩余的持续时间内均匀分布。对于linear(0, -0.1 20%, 0.75, 1)的值,停止点0.75将不再出现在持续时间的 2/3 处,而是出现在 60% 处;这是因为持续时间的最后 80% 在最后三个停止点之间均匀分布。

Two linear() curves with the same stop values, one with stop lengths and one without.

使用线性缓动函数创建平滑曲线

我们的动画缓动仍然看起来,嗯,相当线性。还没有人可以说这是一个“逼真”的动画!在第一个示例中,元素快速移动到0.75位置,然后突然切换到较慢的速度。如果我们想要创建更平滑的减速,我们需要添加更多停止点。

css
animation-timing-function: linear(0, -0.1 20%, 0.4, 0.63, 0.75, 0.84, 0.92, 0.97, 1);

现在元素的减速并不完全平滑,但对于快速动画来说,它可能足够平滑。如果我们的动画持续时间更长,则值的变化可能会更明显。

通常,我们使用的停止点越多,动画就越平滑,因为停止点之间的变化将难以察觉。

A comparison of two linear easing curves, one with nine stops and one with 18 stops. The curve with 18 stops is smoother but both curves look very similar and would look more or less the same when superimposed on top of each other.

当然,手动创建所有这些停止点可能会有点麻烦!如果我们知道创建缓动曲线的函数,就可以使用 JavaScript 创建一个具有大量点的平滑曲线。

此示例中的 easeOutBounce 函数改编自 easings.net。我们可以将这些缓动效果设置为自定义属性,以便在代码中使用。

js
const easeOutBounce = (x) => {
  const n1 = 7.5625;
  const d1 = 2.75;

  if (x < 1 / d1) {
    return n1 * x * x;
  } else if (x < 2 / d1) {
    return n1 * (x -= 1.5 / d1) * x + 0.75;
  } else if (x < 2.5 / d1) {
    return n1 * (x -= 2.25 / d1) * x + 0.9375;
  } else {
    return n1 * (x -= 2.625 / d1) * x + 0.984375;
  }
};

const createEase = (fn, points = 50) => {
  const result = [...new Array(points)]
    .map((d, i) => {
      const x = i * (1 / points);
      return fn(x);
    })
    .join(",");

  return `linear(${result})`;
};

document.body.style.setProperty("--easeOutBounce", createEase(easeOutBounce));
css
.bounce {
  animation-timing-function: var(--easeOutBounce);
}

将线性缓动函数与 SVG 路径一起使用

如果我们可以向 linear() 函数提供 SVG 路径值,那不是很好吗?虽然我们无法直接做到这一点,但有一个工具可以提供帮助。 Linear() 生成器 将 SVG 路径转换为为我们的 linear() 函数添加停止点,并允许我们预览结果。非常有用,对吧!

相同的工具还允许我们可视化 JavaScript 函数并将其输出为 CSS,因此我们可以从类似于上面函数的函数创建停止点,而无需发送任何客户端 JavaScript。该工具的预设中已经提供了“弹跳”函数,从而产生以下 CSS

css
:root {
  --bounce-easing: linear(0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141 13.6%, 0.25, 0.391, 0.563, 0.765, 1, 0.891 40.9%, 0.848, 0.813, 0.785, 0.766, 0.754, 0.75, 0.754, 0.766, 0.785, 0.813, 0.848, 0.891 68.2%, 1 72.7%, 0.973, 0.953, 0.941, 0.938, 0.941, 0.953, 0.973, 1, 0.988, 0.984, 0.988, 1);
}

浏览器支持

linear() 函数目前在 Safari 中不受支持。但是,如果我们使用 animation-timing-function 作为独立属性,而不是使用简写,则回退到默认的 ease 关键字(或其他一些受良好支持的动画)非常简单。不支持 linear() 函数的浏览器将回退到声明的值,因为浏览器会忽略它们无法识别的任何属性/值对。

css
/* Using the shorthand, the animation will not be applied in browsers that do not support `linear()` */
.box {
  animation: slide 3000ms linear(0, 0.75, 1);
}

/* This way, non-supporting browsers will fall back to `ease-out` */
.box {
  animation: slide 3000ms ease-out;
  animation-timing-function: linear(0, 0.75, 1);
}

或者,我们可以使用特性查询检测对 linear() 的支持,并提供替代方案。

css
/* Fallback animation. You could define an alternative animation with keyframes if you choose. */
.box {
  animation: alternativeSlide 3000ms;
}

/* Browsers that support `linear()` will get these styles */
@supports (animation-timing-function: linear(0, 1)) {
  .box {
    animation: slide 3000ms linear(0, 0.75, 1);
  }
}

总结

希望您喜欢阅读这篇文章并探索这些示例。请随时在 DiscordGitHub 上留下您的反馈、想法或问题。

资源

关注 MDN

获取 MDN 新闻通讯,不错过最新的 Web 开发趋势、技巧和最佳实践的更新。