在网页上制作动画的方法真的数不胜数。 我们之前在这篇文章中介绍过 不同动画技术的比较。 今天,我们将深入探讨我最喜欢的实现方式之一的分步指南:使用 GreenSock.
(他们没有给我任何报酬,我只是真的很喜欢使用它们。)
为什么我更喜欢 GreenSock 而不是其他方法? 从技术上讲,它通常是完成任务的最佳工具。 即使对于复杂的运动,它的使用也极其直观。 以下是我更喜欢使用它的几个原因:
- 您可以在 DOM 元素以及 WebGL/Canvas/Three.js 上下文中使用它们。
- 缓动效果非常复杂。 CSS 动画在缓动方面仅限于两个贝塞尔曲线手柄,这意味着如果您想要一个弹跳效果,您将不得不为每个传递制作向上和向下,以及向上和向下的关键帧。 GreenSock 允许多个贝塞尔曲线手柄来创建高级效果。 弹跳只是一行代码。 您可以查看他们的 缓动效果可视化工具,了解我的意思。
- 您可以在时间轴上排列运动。 当您必须同时协调多件事时,CSS 动画可能会变得有些混乱。 GreenSock 保持非常清晰,并允许您控制时间轴本身。 您甚至可以对动画进行动画处理!🤯
- 它会在后台进行一些计算,以防止出现奇怪的跨浏览器行为,以及规范规定应为真的情况,例如堆叠变换的工作方式。
- 它以插件的形式提供许多高级功能,如果您想进一步完善作品,可以使用这些功能,例如变形 SVG 形状、绘制 SVG 路径、拖放、惯性等等。
人们有时会问我,为什么我会选择这个特定的库,而不是其他所有选择。 它比大多数其他库都要先进——它们已经存在于 Flash 还是主流的时候了。 这个演示卷非常鼓舞人心,同时也说明了严肃的网页动画师确实会选择使用这个工具。
以下是有关如何在网页上创建运动的细分,我将其分解成尽可能小的单位。 我们开始吧!
对 DOM 元素进行动画处理
考虑一个用 <div>
制作的球体,它使用 border-radius
值为 50% 进行样式设置。 以下是使用 GreenSock 对其进行缩放和移动的方法
<div class="ball"></div>
gsap.to('.ball', {
duration: 1,
x: 200,
scale: 2
})
在这种情况下,我们告诉 GreenSock (gsap
) 对具有类 .ball
的元素进行 .to()
操作,以更改几个不同的属性。 我们将 transform: translateX(200px)
的 CSS 属性缩短为简化的 x: 200
(注意不需要单位,但可以以字符串的形式传递它们)。 我们也没有编写 transform: scale(2)
。 以下是对您可能希望在动画中使用的变换以及其相应的 CSS 语法的参考
x: 100 // transform: translateX(100px)
y: 100 // transform: translateY(100px)
z: 100 // transform: translateZ(100px)
// you do not need the null transform hack or hardware acceleration, it comes baked in with
// force3d:true. If you want to unset this, force3d:false
scale: 2 // transform: scale(2)
scaleX: 2 // transform: scaleX(2)
scaleY: 2 // transform: scaleY(2)
scaleZ: 2 // transform: scaleZ(2)
skew: 15 // transform: skew(15deg)
skewX: 15 // transform: skewX(15deg)
skewY: 15 // transform: skewY(15deg)
rotation: 180 // transform: rotate(180deg)
rotationX: 180 // transform: rotateX(180deg)
rotationY: 180 // transform: rotateY(180deg)
rotationZ: 180 // transform: rotateZ(180deg)
perspective: 1000 // transform: perspective(1000px)
transformOrigin: '50% 50%' // transform-origin: 50% 50%
持续时间是您可能想到的:它是一秒钟的时间段。
那么,好的,我们如何对,比如说,SVG 进行动画处理呢? 让我们考虑上面相同的代码,但用 SVG 表示。
<svg viewBox="0 0 500 400">
<circle class="ball" cx="80" cy="80" r="80" />
</svg>
gsap.to('.ball', {
duration: 1,
x: 200,
scale: 2
})
从动画的角度来看,它实际上是 完全相同的。 它会获取具有类 .ball
的元素,并转换这些属性。 由于 SVG 实际上是 DOM 元素,我们可以对其中任何一个添加类,并以相同的方式对其进行动画处理!
太棒了!我们正在高效地进行操作。
缓动效果
我之前提到过,缓动效果是我最喜欢的功能之一,让我们看看如何使用它们。
让我们看看最初的球体。 也许我们想尝试一些更独特的弹跳缓动效果。 它会像这样
gsap.to('.ball', {
duration: 1.5,
x: 200,
scale: 2,
ease: 'bounce'
})
就是这样! GreenSock 的这个版本假设您想要使用 ease-out
时间函数(它更适合入场动画),因此它将其用作默认值。 您只需要将“bounce”指定为字符串,就可以开始使用它了。
您可能已经注意到我们也稍微延长了持续时间。 这是因为球体在初始状态和最终状态之间需要做更多的“工作”。 一秒钟的持续时间虽然对于线性或正弦缓动效果来说非常棒,但对于弹跳或弹性缓动效果来说,速度有点太快了。
延迟和时间轴
我提到过,默认的 ease-out
时间函数很适合入场动画。 那 ease-in
或 ease-in-out 出场动画呢? 让我们也来做一下。
gsap.to('.ball', {
duration: 1.5,
x: 200,
scale: 2,
ease: 'bounce'
})
gsap.to('.ball', {
duration: 1.5,
delay: 1.5,
x: 0,
scale: 1,
ease: 'back.inOut(3)'
})
您可能已经注意到发生了一些事情。 例如,我们在倒数第二行 (ease: 'back.inOut(3)'
) 没有使用 bounce.in
。 相反,我们使用了另一种称为 back
的缓动效果,用于 ease-in-out
。 我们还传递了一个配置选项,因为如您从 Greensock 的缓动效果可视化工具中看到的那样,我们不仅仅限于该缓动效果的默认配置。 我们可以根据自己的需要对其进行调整。 太棒了!
您可能还注意到,我们使用延迟将动画链接在一起。 我们获取了第一个动画的持续时间,并确保下一个动画的延迟与之匹配。 现在,这在这里起作用,但这非常脆弱。 如果我们想更改第一个动画的长度呢? 那么我们必须返回并更改后面的延迟。 如果我们之后还有另一个动画怎么办? 以及另一个动画? 好吧,我们必须返回并计算所有后续延迟。 这需要大量的手动工作。
我们可以将这项工作转交给计算机。 我的一些更复杂的动画包含数百个链接在一起的动画! 如果我完成工作并想要调整开头的内容,我不希望必须返回并修改所有内容。 这就是时间轴的作用
gsap
.timeline()
.to('.ball', {
duration: 1.5,
x: 200,
scale: 2,
ease: "bounce"
})
.to('.ball', {
duration: 1.5,
x: 0,
scale: 1,
ease: "back.inOut(3)"
});
这会实例化一个时间轴,然后将这两个动画链接到它。
但是我们仍然有一些重复,我们在每个动画中都使用了相同的持续时间。 让我们创建一个默认值,作为传递给时间轴的选项。
gsap
.timeline({
defaults: {
duration: 1.5
}
})
.to('.ball', {
x: 200,
scale: 2,
ease: "bounce"
})
.to('.ball', {
x: 0,
scale: 1,
ease: "back.inOut(3)"
});
太酷了! 好吧,您可能已经开始了解以这种方式构建事物的方式了。 虽然在这样一个简单的动画中可能没什么大不了的,但在真正复杂的动画中,默认值和时间轴可以真正保持代码的可维护性。
现在,如果我们想在球体的另一个方向上镜像此运动,并且只是……让它继续下去怎么办? 换句话说,如果我们想让它循环播放怎么办? 这时我们就添加 repeat: -1
,它可以应用于单个动画或整个时间轴。
gsap
.timeline({
repeat: -1,
defaults: {
duration: 1.5
}
})
.to('.ball', {
x: 200,
scale: 2,
ease: "bounce"
})
.to('.ball', {
x: 0,
scale: 1,
ease: "back.inOut(3)"
})
.to('.ball', {
x: -200,
scale: 2,
ease: "bounce"
})
.to('.ball', {
x: 0,
scale: 1,
ease: "back.inOut(3)"
});
我们不仅可以使其重复播放,还可以使其来回重复播放,就像一个弹簧玩具一样。 这就是我们为什么将其称为 yoyo: true 的原因。 为了说明这一点,我们将只显示第一个动画。 您可以看到它会向前播放,然后会反向播放。
gsap
.timeline({
repeat: -1,
yoyo: true,
defaults: {
duration: 1.5
}
})
.to('.ball', {
x: 200,
scale: 2,
ease: "bounce"
})
重叠和标签
我们可以轻松地将动画链接在一起,这很棒,但现实生活中的运动并不完全是这样。 如果您穿过房间去拿一杯水,您不会走。 然后停下来。 然后拿起水。 然后喝下去。 您更有可能以一种连续的运动来完成这些动作。 因此,让我们简要讨论一下如何重叠运动,以及如何使事物同时发生。
如果我们想确保事物在时间轴上的特定时间点之前和之后发生,我们可以使用增量器或减量器。 如果我们以以下示例为例,它显示三个球体一个接一个地进行动画处理,那么它会显得有点僵硬。
gsap
.timeline({
defaults: {
duration: 1.5
}
})
.to('.ball', {
x: 300,
scale: 2,
ease: "bounce"
})
.to('.ball2', {
x: 300,
scale: 2,
ease: "bounce"
})
.to('.ball3', {
x: 300,
scale: 2,
ease: "bounce"
})
如果我们使用作为字符串传递的减量器略微重叠运动,那么事物会变得更加流畅。
gsap
.timeline({
defaults: {
duration: 1.5
}
})
.to('.ball', {
x: 300,
scale: 2,
ease: "bounce"
})
.to('.ball2', {
x: 300,
scale: 2,
ease: "bounce"
}, '-=1')
.to('.ball3', {
x: 300,
scale: 2,
ease: "bounce"
}, '-=1')
我们还可以使用一种称为标签的东西来实现这一点。 标签确保事物在动画播放头的特定时间点开始。 它看起来像这样:.add('labelName')
gsap
.timeline({
defaults: {
duration: 1.5
}
})
.add('start')
.to('.ball', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start')
.to('.ball2', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start')
.to('.ball3', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start')
我们甚至可以从标签中增加和减少。 我实际上在我的动画中经常这样做。 它看起来像这样'start+=0.25'
。
gsap
.timeline({
defaults: {
duration: 1.5
}
})
.add('start')
.to('.ball', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start')
.to('.ball2', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start+=0.25')
.to('.ball3', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start+=0.5')
哇! 我们可以用它做这么多事情! 以下是一个动画示例,它将许多这些前提组合在一起,并使用纯 JavaScript 进行了一些交互。 请务必点击铃铛。
如果您正在寻找更多使用 GreenSock 进行的框架驱动的动画,这里有一篇 我写过的介绍 Vue 5 动画的文章,以及一篇 我发表的演讲,它介绍了 React——这篇文章已经有一两年了,但基本前提仍然适用。
但是,还有很多我们没有涵盖的内容,包括交错、SVG 形变、绘制 SVG、在屏幕上移动物体、沿着路径移动物体、动画文本…… 你可以随意提!我建议您前往 GreenSock 文档 查看这些内容的详细信息。我还有一门 Frontend Masters 的课程 更深入地介绍了所有这些内容,并且课程资料 在我的 GitHub 上开源。我还有一些 公开的 Pen 代码供您分叉和使用。
希望这能帮助您开始在网络上进行动画制作!我迫不及待地想看看您会做出什么作品!
Sarah 太棒了!我是她的忠实粉丝,我开始接触 SVG 和动画,我通常发现仅仅通过文档学习很难,但这篇介绍很棒!
GSAP 是网络动画的行业标准,但我们很少听到有关它的消息。它不够新潮?还是什么原因?
那……那完全就是这篇文章要讲的内容…… 我…… 什么?
我有一个问题,我们应该在哪里编写 Babel 代码?我们必须把它写到 JavaScript 文件中吗?