上个月做活动落地页,设计师在交互稿里标了七八处动画:标题要淡入、卡片要依次滑入、按钮悬停时加个小抖动、页面滚到每个区块时元素还要逐个浮现。要是全手写 @keyframes,光是调缓动曲线和兼容前缀就够忙半天。我从之前项目里翻了几个常用的 CSS 动画库出来,不到半小时全给接上了。
这篇文章就把我这些年用得最多的六个 CSS 动画库梳理一遍:Animate.css、Hover.css、AOS、Magic Animations、CSShake,加一个我习惯当库用的 Animista 生成器。它们各自解决什么问题、怎么接入、有哪些坑、性能和工程化上要注意什么,都会尽量讲透。适合正在做活动页、落地页、个人网站,或者刚接手带动效需求页面的朋友参考。
1. 动画库到底解决了什么:手写 keyframes 的三个真实痛点
1.1 重复造轮子:大部分动画的套路高度一致
我见过不少新手项目,一个页面里自己写了七八组 @keyframes,从 fadeIn 到 slideInUp 到 bounce,看着挺努力,其实全是重复劳动。把动画单独抽成库或者工具类,本质上就是在做同一件事:把"通用动效"从业务代码里剥离出去。
写多了你会发现,页面里常用的动画套路就那么几种:淡入淡出、上下左右位移、缩放、抖动、翻转、弹跳。这些效果社区早就沉淀得非常成熟了,直接拿来用,比自己每次重新调参要稳得多。尤其是缓动曲线(easing),cubic-bezier(0.25, 0.1, 0.25, 1)这种东西不是每次都能拍脑袋拍对的,用现成库等于借用了社区反复打磨过的参数。
1.2 兼容性处理:你不想在每个老浏览器上补前缀
CSS 动画的兼容性在现代浏览器里已经很好了,但总会有那么一两个场景需要回退处理。早期 Safari 需要-webkit-前缀,某些老版本浏览器对animation-fill-mode的行为也有出入。这些兼容细节,成熟的动画库早就处理好了,你不用在自己项目里维护一堆前缀补丁。
1.3 你真正需要的是两种能力:预制效果与可控触发
用库之前,我建议先分清自己的需求是哪种类型:
- 预制效果:像 Animate.css、Magic Animations、CSShake 这类,给元素加个类名,动画就有了。适合快速开发、统一规范、不需要从零调细节的场景。
- 可控触发:像 AOS、WOW.js 这类,靠滚动位置、JS 事件来控制动画什么时候发生。适合页面滚动展示、交互反馈这类带时序的逻辑。
- 生成自定义:Animista 这类工具,现场选效果、调参数、复制代码,适合现成库里找不到你想要的动画时。
这个分类很重要,因为它决定了后面所有选型和集成方式。很多人一上来就全量引入 Animate.css,最后才发现要的是滚动触发,又去补一个 AOS,包体积和样式冲突就是这么一点点堆出来的。
2. Animate.css 和 Hover.css:入场与悬停,两个最常用的预制库
2.1 Animate.css 的版本变化,别再用老教程的类名了
Animate.css 应该是历史最悠久、使用者最多的 CSS 动画库之一。它的接入非常简单,CDN 一行:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />然后给目标元素加上animate__animated加具体效果类即可:
<div class="animate__animated animate__bounce">我会弹跳</div>这里有个大坑,网上大量教程还在教 v3 的写法:class="animated bounce"。从 v4 开始,官方把所有类名都加了animate__前缀,原因是 v3 的animated和bounce这种类名太通用,很容易和项目自己的样式冲突。如果你看到老代码,请自动更新成animate__animated animate__bounce的格式。
v4 还引入了 CSS 变量来控制时长和延迟,这个比 v3 时代自己写animation-duration覆盖类名要优雅得多:
:root { --animate-duration: 800ms; --animate-delay: 0.3s; }只想让某个元素变快,就直接在该元素的 style 上覆盖变量:
<div class="animate__animated animate__fadeIn" style="--animate-duration: 0.5s;">快一点</div>2.2 重播动画:一个经常被忽略的细节
Animate.css 的动画默认只播放一次,做完就停在结束状态(或回到初始状态,取决于效果和 fill mode)。有些场景需求是"点了某个按钮,动画重新播一遍",这就要用 JS 手动触发。
我常用的重播方式是:移除类 – 强制重绘 – 再加回类:
const el = document.querySelector('.target'); el.classList.remove('animate__heartBeat'); void el.offsetWidth; // 强制 reflow,让浏览器忘记之前的动画状态 el.classList.add('animate__heartBeat');void el.offsetWidth这行就是关键,很多刚开始接触的人漏掉它,然后发现类加了又减,动画根本不重播。原因就是浏览器在同一个渲染帧里没感知到类的变化,直接当成"没变过"处理了。
2.3 Hover.css:把悬停反馈做成产品细节
如果说 Animate.css 管的是元素"进来",那 Hover.css 管的就是元素"摸上去"。这个库专门做 hover 效果,ICON 风格的按钮反馈、文字下划线滑动、阴影扩散、气泡提示、卷角等,全都能通过加类名搞定。
<a class="hvr-grow" href="#">变大一点点</a> <a class="hvr-underline-from-center" href="#">中间向两边划一下</a>引入方式同样简单,直接引 hover-min.css 即可。用下来我最常搭配的是hvr-grow、hvr-shrink、hvr-pulse、hvr-sweep-to-right这几个,它们都是电商和后台系统里按钮反馈的标配。
用 Hover.css 有一个容易踩的坑:transform 不能用在 inline 元素上。如果你的悬停对象是普通文本链接<a>,那 grow、shrink 这类基于 transform 的效果会完全失效,点击区域和视觉反馈都对不上。解决办法是先把它变成 inline-block 或 block 元素:
.hvr-grow { display: inline-block; }另一个细节是,很多 hover 效果需要元素本身有相对定位或 z-index 参与,特别是阴影扩散、圆角扫过这类,设计稿如果对层级很敏感,建议先在浏览器里调一遍再落到代码。
2.4 别把全量 CSS 直接扔进项目,按需引入并不难
Animate.css 全量压缩后其实也就几十 KB,不算夸张,但很多项目一个页面只用三五个效果,引入整个库还是浪费。工程化项目里我一般这样处理:直接复制用到的几段关键 keyframes 到自己的 less/scss 文件里,或者用 PurgeCSS 这类工具在构建时把没出现的类清掉。
Hover.css 同样支持按需构建,它提供 Sass 源码,你可以在配置里只开启需要的效果模块再编译。团队如果已经在用 Sass,这种方式比全量引 CSS 文件干净不少,也不会污染全局类名。
3. AOS 和 WOW.js:滚动场景下该选哪个,很多人没想清楚
3.1 老牌组合 WOW.js + Animate.css 怎么用
WOW.js 是很多老项目里的常客,它的思路是:页面滚动到目标元素时,动态给元素加上 Animate.css 的动画类,从而实现"滚动浮现"效果。用起来分三步:
<script src="https://cdnjs.cloudflare.com/ajax/libs/wow/1.1.2/wow.min.js"></script> <script> new WOW().init(); </script>然后在目标元素上带上wow类,再加 Animate.css 的效果类:
<div class="wow animate__fadeInUp"><link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css" /> <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script> <script> AOS.init(); </script>HTML 里通过><div>AOS.init({ duration: 600, // 动画时长,默认 400 easing: 'ease-out-back',// 缓动,ease-out-back 会有轻微回弹,比较有质感 once: true, // 只播放一次,这个对活动页特别重要 offset: 80 // 元素进入视口底部 80px 时触发 });
once: true是我做活动页时的默认选择,因为用户来回滚动时动画反复播放会显得页面很"飘",反而影响阅读。
AOS 还有一个特性是>AOS.refresh();
如果你用了 Vue 或 React 的异步渲染,记得在数据加载完成后再调用 refresh,否则新出现的元素会一直处于不可见状态。
4. Magic Animations 与 CSShake:特殊动效库,适合点缀不适合铺满
4.1 Magic Animations 的魔幻系动效
Magic Animations 是一个体积很小但风格很鲜明的预制动画库,它的效果偏"魔幻"和"夸张":有各种角度的翻转、扭曲、弹跳、旋入,还有一些叫不上名字的独特动效,比如twisterInUp、swashIn、spaceOutUp,一眼就能看出和普通 fade、slide 不是一个路子的。
使用方式是这样:
<link rel="stylesheet" href="path/to/magic.min.css" /> <div class="magictime puffIn">凭空出现</div> <div class="magictime swap">翻转交换</div>核心类名是magictime,它负责设定动画时长和播放方式,后面再加具体效果类。你也可以用 CSS 变量或直接加style改时长:
<div class="magictime boingInUp" style="animation-duration: 1s;">有弹性的入场</div>我的使用场景一般是:活动页主视觉标题、抽奖按钮、"惊喜出现"这类需要抓眼球的元素。普通列表和正文内容基本不会用它,因为风格太强烈,用多了会喧宾夺主。
Magic Animations 同样支持 hover 效果,库里面带了一批带hover场景的类名,可以做出悬停时的魔幻反馈。不过我在实际项目里用得不多,通常还是让它管入场和强调,hover 反馈交给 Hover.css,职责更清晰。
4.2 CSShake:各种抖动,适合错误反馈和强调
CSShake 是个特别"小而专"的库,它只做一件事:抖动。但它把抖动做了很多变种:水平抖、垂直抖、旋转抖、轻微抖、剧烈抖、持续抖、透明度抖,基本覆盖了你对"抖动"这个词的所有想象。
<link rel="stylesheet" href="path/to/csshake.min.css" /> <div class="shake">标准抖动</div> <div class="shake shake-hard">剧烈抖动</div> <div class="shake shake-horizontal">水平抖动</div> <div class="shake shake-vertical">垂直抖动</div> <div class="shake shake-little">轻微抖动</div>我最常用的场景有两个:表单校验失败和操作成功反馈。比如密码输错了,给输入框加一个shake-horizontal,非常直观地告诉用户"这里不对";收藏按钮点完,加一个shake-vertical或者轻微抖,反馈感比单纯变个颜色好很多。
用 CSShake 时注意一个点:抖动是持续的,如果给常驻元素加了这个类,它会一直抖,很干扰视觉。一般配合 JS 使用,比如错误提示出现时加类,1 秒后移除:
el.classList.add('shake-horizontal'); setTimeout(() => el.classList.remove('shake-horizontal'), 800);CSShake 也支持通过 Sass 按需引入和自定义抖动幅度的变量,如果你的项目有特殊抖动需求,改它的$shake-x、$shake-y、$shake-rotation这类变量比自己写 keyframes 方便得多。
4.3 买一送一:Animista 生成器,没有现成效果时的最后一招
Animista 严格来说不算"库",它是一个可视化动画生成网站,但我特别想把它放进来,因为它解决的是前面几个库都覆盖不到的问题:没有现成效果的时候怎么办。
Animista 的用法很直观:打开网站,按分类浏览效果(入口、出口、文字、背景、基础变换等),选中一个效果后右侧可以调节时长、延迟、缓动、循环次数、播放方向,还能选不同的触发方式,最后直接把生成的 CSS 复制或下载进项目。
/* 比如在 Animista 上生成的一段代码 */ .text-pop-up-top { animation: text-pop-up-top 0.6s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; } @keyframes text-pop-up-top { 0% { transform: translateY(0); transform-origin: 50% 50%; text-shadow: none; } 100% { transform: translateY(-50px); transform-origin: 50% 50%; text-shadow: 0 1px 0 #ccc, 0 2px 0 #ccc; } }看到没,它连@keyframes都给你生成好了,复制粘贴就能用,没有任何运行时依赖。这非常适合那些"只想要一两个特殊效果,但不想为两个效果引入整个动画库"的诉求。
我个人的习惯是:把 Animista 当成一个备用的设计工具,设计师说"这个标题要有个特别的入场",我先去 Animista 上快速试几个效果,选中了再存进项目里做成通用类。这样既不会因为找不到合适效果而卡住,也不会让项目里堆一堆用不上的库文件。
5. 动画性能、无障碍与工程化落地:库只是起点,后面才是经验
5.1 性能第一原则:优先 transform 和 opacity,别动布局属性
不管用哪个 CSS 动画库,这条原则都成立:能用 transform 和 opacity 实现的动画,绝不碰 width、height、top、left、margin 这些布局属性。原因在于,transform 和 opacity 的变化不需要触发布局(layout)和重绘(paint),浏览器可以走合成(composite)流程,性能开销小得多;而 width、top 这类属性一变化,浏览器就要重新计算布局,成本立即上一个量级。
Animate.css 的很多效果都遵循了这个原则,因为它内部大量使用 transform 和 opacity。但自己从 Animista 复制代码或者手写 keyframes 时,就要有意识地检查一下动画属性。如果发现要动画一个元素的left,尽量改成 transform:
/* 不推荐:left 变化会触发布局 */ @keyframes slide { from { left: 0; } to { left: 300px; } } /* 推荐:translate 只触发合成 */ @keyframes slide { from { transform: translateX(0); } to { transform: translateX(300px); } }另外,不要在一个页面里同时让几十个元素做大面积位移或缩放动画。活动页虽然强调视觉冲击,但滚动时满屏元素都在飘,在低端设备上会明显卡顿。我一般会控制同一时刻执行动画的元素数量,并把入场动画做成错峰触发,也就是加 100ms、200ms 的延时,让它们依次出现,既能优化性能,视觉上也更精致。
5.2 prefers-reduced-motion:尊重用户系统的"减少动画"设置
这个点很多做前端的朋友会忽略。操作系统和浏览器都有"减少动态效果"的辅助功能设置,打开后网站还满屏动画,对有前庭障碍或对动效敏感的用户来说体验会很差。
主流 CSS 动画库基本都能通过媒体查询关掉动画。比如在全局样式里加一段:
@media (prefers-reduced-motion: reduce) { .animate__animated, .magictime, .shake, [data-aos] { animation: none !important; transition: none !important; } }AOS 还提供了更精细的配置,可以初始化时判断系统设置:
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)'); AOS.init({ disable: prefersReducedMotion.matches });这段代码写起来不难,但对无障碍体验的提升很大,属于投入产出比非常高的一行配置。我做活动页的时候会专门在检查清单里加一项:关掉系统动效后页面是否还能正常阅读和信息传达。
5.3 在 Vue / React 里集成这些库的几个经验
工程化项目里直接往 HTML 里贴 CDN 链接通常不是最优解。我更建议按 npm 包的方式引入,然后根据框架机制来做事件触发。
Vue 场景里,Animate.css 经常配合<Transition>使用,只需要把 enter-active-class 和 leave-active-class 指过去:
<Transition enter-active-class="animate__animated animate__fadeInLeft" leave-active-class="animate__animated animate__fadeOutRight" > <div v-if="visible">内容</div> </Transition>React 里如果是类名切换,核心逻辑和原生差不多。要注意的是 React 18 的 StrictMode 下,函数组件会双执行渲染,动画重播时记得把"加类再删类"的操作放到useEffect里,而不是直接在渲染函数里改 DOM 状态。
还有一个小经验:如果项目里有大量交互式动画,且团队对 JS 动画更熟悉,那 GSAP 这类库可能比纯 CSS 库更合适。但这篇文章主要聊 CSS 动画库,我的建议是——先用最简单的方式满足需求。80% 的活动页动效,上面这六个库完全够用,没必要一开始就上重型动画引擎。
6. 我的最终推荐组合:什么时候用哪个,一句话说明白
文章最后把我自己的选择习惯总结成一张清单,方便你对号入座:
| 需求场景 | 推荐选择 | 理由 |
|---|---|---|
| 页面元素入场/出场 | Animate.css | 类名标准、效果好、社区案例多 |
| 按钮/卡片悬停反馈 | Hover.css | 反馈效果丰富,能提升交互质感 |
| 滚动到视口时浮现 | AOS | 自带动画样式,配置灵活,更新活跃 |
| 老项目已有 Animate.css 再补滚动 | WOW.js | 和 Animate.css 配合顺手,改造小 |
| 强调、惊喜、特殊转场 | Magic Animations | 风格独特,适合点缀场景 |
| 表单错误、操作反馈抖动 | CSShake | 抖动专业,分类细,体积极小 |
| 现成库里没有的特殊动画 | Animista | 可视化生成代码,零依赖 |
我个人实际使用时的默认组合是:Animate.css 管入场,Hover.css 管悬停,AOS 管滚动,CSShake 管错误反馈,Magic Animations 只在大转场或主视觉上用,Animista 只有当设计稿提出特殊要求时才打开。这个组合覆盖了我这几年接到的大部分动效需求。
如果你刚接触这些库,我建议不要一次性全上,先从 Animate.css 加一个入场动画开始跑通流程,再慢慢按需加别的。CSS 动画本身不复杂,真正有价值的是你逐渐积累的节奏感——什么时候让元素进来、让它怎么动、动多快、用什么样的缓动,这些判断一旦形成,做出来的页面会和"一套默认动画走天下"完全不一样。