1. 萌新先停手:CSS动画到底能扛多少事,又扛不住什么事
先讲个我自己的经历。前两年带新人,几乎每个刚入行的前端都默认一个事——动画就是JS的专利。一说做特效,满脑子全是轮播图插件、three.js、canvas,开口就是"这个要不要引个库"。等我把一套纯CSS按钮光效怼到他们面前,他们第一反应是:这玩意居然能不用JS?
所以我一直觉得,"前端萌新别卷JS了"这句话不是贩卖焦虑,是真心话。CSS动画这套东西,价格低、见效快、面试还爱问,完全可以作为你点亮交互能力的第一个技能树。这篇博文我把28个纯CSS动画特效整理成了一份可复用的合集,全部都是复制粘贴就能跑的程度,每个我都标注了难度和适用场景,后面还会把核心机制和踩过的坑一并交代清楚。不管你是刚入行想给个人项目加点质感,还是准备面试想补一补动画原理,这份清单都够你消化一阵子。
先说清楚一个边界:CSS动画到底能扛多少事?以我的判断,日常UI里的交互动效,80%以上用纯CSS就能搞定。按钮悬停、卡片浮起、加载转圈、文字渐变、3D翻转、玻璃拟态悬浮,这些都是CSS的主场。真正需要JS的场景,是数据驱动的动画(比如图表过渡)、用户拖拽相关的物理反馈、游戏类渲染,以及需要JS计算位置后再做动画的情况。这些是另一门课,今天不碰。
1.1 为什么我敢说你被JS惯坏了
很多新人写动画的第一反应是 setInterval + 改style,或者直接上requestAnimationFrame。不是说不行,而是大部分需求根本没必要动用这套。CSS动画的优势有三个,越到后面越值钱:
一是声明式。你只需要告诉浏览器"从哪到哪、多快、什么节奏",剩下的它自己算。JS写动画你得自己管理每一帧的状态,代码一多就乱。
二是性能。这个是硬道理。浏览器有合成器(compositor),动画如果跑在transform和opacity上,可以不进入主线程,由GPU直接合成渲染。你用JS去改left、top这些会触发layout和paint的属性,每帧都要重算布局,卡顿就是这么来的。CSS动画能自动走合成器这条快车道,这是它最被低估的优势。
三是可维护性。一个类名控制一个状态,hover就过渡,加个class就播放,语义清楚。JS动画的状态管理一多,后面人接手就想骂人。
1.2 渲染管线里的真相:为什么transform和opacity这么香
这里需要补一个偏底层的基础知识,面试问到"CSS动画为什么性能好"就靠它了。浏览器渲染一帧要走这么几步:样式计算(Style)→ 布局(Layout)→ 绘制(Paint)→ 合成(Composite)。
你改 width、height、left、top,会触发 Layout,意味着浏览器要重新计算元素的位置和尺寸,后面 Paint 和 Composite 全部跟着走一遍,这是最贵的操作。你改 background-color、box-shadow,会触发 Paint,比 Layout 便宜一些,但每一帧都得重绘。而你改 transform 和 opacity,浏览器可以直接跳过 Layout 和 Paint,把元素丢给合成器做位移、旋转、缩放和透明度变化。这就是为什么我一直强调:做动画优先用 transform 和 opacity,实在不行才去改其他属性。
你自己写动效的时候,可以记住这个判断顺序:能用 transform,就不要用 top/left;能用 opacity,就不要用 display: none;能用 transform: scale,就不要用 width/height 去硬挤。
1.3 transition和animation,选错就是你动画不动的第一个原因
很多新手分不清这两个东西,其实一句话就能说清:transition 管的是状态切换时补间过渡,它需要你改变某个属性值才触发;animation 管的是定义好的关键帧动画,它不需要外部状态改变,自己就会播。
按钮悬停变色这种"从正常态到悬浮态"的过渡,是 transition 的典型场景。而加载转圈、霓虹闪烁这种"不需要交互自己一直动"的,就必须用 animation。选错会出现什么现象?最常见的:你给一个元素写了 transition: opacity 0.3s,但根本没有状态变化触发它,那它永远不会动。或者你给一个元素写了 animation,却想在 hover 时才播,结果元素一加载就开始自顾自地转。搞清楚这两个的定位,后面抄代码才不会抄出莫名其妙的问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 28个特效全家桶:抄之前先看这份清单
这套合集的核心思路很简单:我按"能直接用在真实项目里"的标准,从这些年自己攒的代码里筛了28个纯CSS特效。每个特效都遵循三条原则:不依赖JS、不依赖外部库、尽量只用 transform/opacity 来保证性能。
2.1 我按使用场景把28个特效分成了五类
分类很重要,因为同样是特效,用错的场景比没用特效还尴尬。比如加载动画做成闪烁霓虹,用户会觉得页面坏了;按钮光效做得太花,重要操作反而没人敢点。我的分类标准是:悬停交互类用在按钮和卡片上,加载背景类用在等待和页面氛围上,文字类用在标题和品牌露出上,3D卡片类用在产品展示上,最后一类纯装饰类就是给页面加一点呼吸感。
2.2 完整清单和难度参考
下面是28个特效的完整清单,星号越多越难。我后面几个大章节会挑重点的逐一拆源码,剩下的一小节给极简速查版本,全部代码都是可以直接跑通的。
| 编号 | 特效名 | 分类 | 难度 | 典型用途 |
|---|---|---|---|---|
| 01 | 按钮流光扫过 | 悬停交互 | ★ | 主按钮、CTA |
| 02 | 按钮涟漪扩散 | 悬停交互 | ★★ | 菜单按钮、移动端 |
| 03 | 边框描画 | 悬停交互 | ★★ | 卡片、图片容器 |
| 04 | 图标上下浮动 | 悬停交互 | ★ | 功能图标、引导箭头 |
| 05 | 图标抖动 | 悬停交互 | ★ | 通知、收藏按钮 |
| 06 | 卡片上浮+阴影强化 | 悬停交互 | ★ | 任何卡片 |
| 07 | 图片悬停缩放 | 悬停交互 | ★ | 商品图、文章封面 |
| 08 | 链接下划线滑动 | 悬停交互 | ★ | 导航菜单 |
| 09 | 按钮按压回弹 | 悬停交互 | ★ | 表单提交按钮 |
| 10 | 基础圆环转圈 | 加载动画 | ★ | 通用loading |
| 11 | 双色渐变转圈 | 加载动画 | ★★ | 有品牌色的loading |
| 12 | 三圆点弹跳 | 加载动画 | ★ | 按钮内loading |
| 13 | 涟漪脉冲扩散 | 加载动画 | ★★ | 定位、语音播报提示 |
| 14 | 骨架屏闪烁 | 加载动画 | ★★ | 列表页、详情页占位 |
| 15 | 渐变背景流动 | 背景特效 | ★★ | 登录页、活动页 |
| 16 | 波纹环形扩散 | 背景特效 | ★★ | 地图标记、直播提示 |
| 17 | 渐变流光文字 | 文字特效 | ★★ | 标题、品牌词 |
| 18 | 霓虹灯闪烁 | 文字特效 | ★★ | 活动页、Logo |
| 19 | 文字辉光呼吸 | 文字特效 | ★ | 大标题、营销文案 |
| 20 | 逐字弹跳 | 文字特效 | ★★ | 欢迎页、弹窗标题 |
| 21 | 波浪文字 | 文字特效 | ★★★ | Banner标题 |
| 22 | 模拟打字光标 | 文字特效 | ★ | 输入框、博客 |
| 23 | 3D翻转卡片 | 3D卡片 | ★★★ | 会员卡、产品对比 |
| 24 | 玻璃拟态悬浮 | 3D卡片 | ★★ | 个人名片、面板 |
| 25 | 立体倾斜卡片 | 3D卡片 | ★★★ | 产品展示区 |
| 26 | 心跳动效 | 装饰特效 | ★ | 点赞、健康类 |
| 27 | 旋转渐变色环 | 装饰特效 | ★★ | 头像边框、徽章 |
| 28 | 跑马灯边框 | 装饰特效 | ★★★ | 高亮卡片、活动公告 |
3. 悬停交互类拆解:按钮光效、边框描画、图标漂浮、卡片上浮
先拆悬停交互,这是你项目里最常用的。我挑四个代表性强的写完整代码,其他几个在这个基础上改改参数就能用。
3.1 按钮流光扫过:一个伪元素搞定高级感
这个效果是百搭之王。原理不复杂:按钮上盖一个半透明的斜向渐变条,平时藏在左边,hover时让它从左边快速滑到右边,视觉上就像一道光扫过去。关键点是父元素必须 overflow: hidden,不然那条光会露出去。
css复制.btn-shine {
position: relative;
overflow: hidden;
padding: 12px 32px;
border: none;
border-radius: 8px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: #fff;
font-size: 16px;
cursor: pointer;
}
.btn-shine::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 60%;
height: 100%;
background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.5), transparent);
transform: skewX(-20deg);
transition: left 0.5s ease;
}
.btn-shine:hover::before {
left: 120%;
}
光条用 skewX(-20deg) 斜着切一刀,看起来更接近"扫过"而不是"平移"。left 这种属性在过渡里性能一般,但对于一个瞬时的 hover 光效,0.5秒内只有一次触发,没有连续动画,损失可以忽略。如果非要抠性能,可以用 translateX 替代 left,伪元素定位在按钮中心再偏移也行。实际效果差别不大,我建议先怎么简单怎么写。
3.2 边框描画:面试最爱问的hover技巧
边框描画的效果是:鼠标移上去,边框从中间向两边长出来,像一笔画完。很多人第一反应是逐帧改 border,那就大错特错了。正确的思路是用两个伪元素画横线和竖线,利用 scaleX 和 scaleY 从0放大到1,配合 transform-origin 控制生长方向。
css复制.card-border {
position: relative;
padding: 40px;
border-radius: 8px;
background: #fff;
}
.card-border::before,
.card-border::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
border: 2px solid #667eea;
border-radius: 8px;
box-sizing: border-box;
transition: transform 0.4s ease;
pointer-events: none;
}
.card-border::before {
top: 0;
left: 0;
transform: scaleX(0);
transform-origin: left center;
border-right: none;
border-bottom: none;
}
.card-border::after {
bottom: 0;
right: 0;
transform: scaleX(0);
transform-origin: right center;
border-left: none;
border-top: none;
}
.card-border:hover::before {
transform: scaleX(1);
}
.card-border:hover::after {
transition-delay: 0.2s;
transform: scaleX(1);
}
这是一个简化版,只做了横向生长。完整版要做四条边的话,需要四个伪元素分别做横竖两条线,并且用 scaleX 和 scaleY 分开控制,还要加 transition-delay 让横线和竖线错开时间,形成"先横后竖"的描画顺序。面试官问到这个效果,你只要说得出"用伪元素 + scaleX/scaleY 控制生长 + transform-origin 控制方向",基本就过关了。
3.3 图标漂浮与抖动:小细节里的活气
图标漂浮适合放在功能入口、引导箭头这些位置。它甚至不需要交互触发,用 animation 让它一直动就行。
css复制.icon-float {
display: inline-block;
animation: floaty 2s ease-in-out infinite;
}
@keyframes floaty {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-8px);
}
}
这个效果能通用的关键在 translateY 用的是百分比还是像素。translateY(-8px) 对任何尺寸的图标都成立,如果你写成 translateY(-50%),那就是按自身高度的一半来浮动,适合大尺寸图形。两种场景不同,别混用。
抖动和漂浮不一样,它通常是 hover 时触发一次,用 transition 或者一次性 animation 播放。最常见的是收藏按钮抖三下:
css复制.icon-shake:hover {
animation: shake 0.4s ease;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-4px) rotate(-6deg); }
75% { transform: translateX(4px) rotate(6deg); }
}
这里有个细节:animation: shake 0.4s ease 没有写 infinite,所以只播一次。但如果鼠标一直放在上面,动画只会在第一次 hover 时触发。要让每次 hover 都抖一次,需要先移除再重新触发,或者干脆配合 JS 加 class。纯CSS的替代方案是用 transition 配合中间的 keyframe,但这种"抖几下"的反馈,CSS 天然受限,我的建议是:这是全文28个里少数允许你偷用一点点 JS 的场景,加两行 classList 就能解决,别死磕。
3.4 卡片上浮与阴影强化:最百搭的交互模板
卡片上浮是我个人最推荐新人先抄的效果,因为它是"静态设计"和"微交互"之间性价比最高的一步。原理就是 hover 时把卡片往上抬一点,同时阴影加深、范围放大,视觉上制造出"卡片离页面更近了"的层次感。
css复制.card-lift {
padding: 32px;
background: #fff;
border-radius: 12px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card-lift:hover {
transform: translateY(-8px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12);
}
注意 transition 里同时声明了 transform 和 box-shadow。box-shadow 虽然会触发 Paint,但卡片数量少、hover 一次就结束的场景完全扛得住。真正要小心的是别随便加 transition: all 0.3s,虽然省事,但它会把所有属性都纳入过渡范围,页面一复杂就会出现"什么都想动一下"的廉价感,还有隐性的性能开销。这个习惯越早养成越好。
4. 加载与背景类拆解:转圈、弹跳点、涟漪、骨架屏、渐变流动
加载动画是纯CSS的强项,因为加载场景通常不需要交互,animation 自己转就完了。而且它还有一个隐藏价值:访谈里"用CSS写一个loading"几乎是送分题,会写三四种变体真的能加分。
4.1 基础圆环转圈的三层升级
最早的 loading 写法是 gif 图片,现在纯CSS就能做,而且更清晰。最经典的款式是半透明底 + 彩色边框 + 旋转:
css复制.spinner {
width: 48px;
height: 48px;
border: 4px solid rgba(0, 0, 0, 0.1);
border-top-color: #667eea;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
border-radius: 50% 把方形盒子变成圆,四边边框只有一条是彩色,其他三条透明,旋转时就像有一个圆弧在转。升级方向有三个:
一是加轨道:把 border-top-color 改成 border-top-style: dashed 之类的花活,或者两层圆叠加形成复合轨道。
二是改节奏:animation-timing-function 从 linear 改成 cubic-bezier(0.68, -0.55, 0.27, 1.55),转圈会有一顿一顿的弹性感,非常像原生 App 的感觉。
三是加文字:给外围套一个容器,在容器里放"加载中...",文案和转圈互不干扰。
4.2 三个圆点的弹跳加载
三圆点弹跳是按钮加载状态的常客,点击登录后按钮里的文案换成三个跳动的点,非常通用。核心是给每个点不同的 animation-delay,让它们错峰跳动。
css复制.loading-dots span {
display: inline-block;
width: 8px;
height: 8px;
margin: 0 3px;
background: currentColor;
border-radius: 50%;
animation: dotBounce 1.2s ease-in-out infinite;
}
.loading-dots span:nth-child(2) {
animation-delay: 0.15s;
}
.loading-dots span:nth-child(3) {
animation-delay: 0.3s;
}
@keyframes dotBounce {
0%, 60%, 100% {
transform: translateY(0);
opacity: 0.5;
}
30% {
transform: translateY(-10px);
opacity: 1;
}
}
要注意的是延迟和动画时长的比例。我的经验是 animation-delay 不要超过动画总时长的 25%,否则第三个点起跳的时候第一个点已经快落地了,节奏会乱。这里用 1.2s 的总时长配 0.15s 和 0.3s 的延迟,看起来是最舒服的比例。
4.3 雷达涟漪与脉冲环
涟漪脉冲特别适合地图定位、语音输入这种需要"告诉用户模块还在工作"的场景。它的核心是利用伪元素 + scale 放大 + opacity 淡出,形成一个不断向外扩散的环。
css复制.ripple {
position: relative;
width: 80px;
height: 80px;
border-radius: 50%;
background: rgba(102, 126, 234, 0.3);
}
.ripple::before,
.ripple::after {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
border: 2px solid rgba(102, 126, 234, 0.6);
animation: pulse 2s ease-out infinite;
}
.ripple::after {
animation-delay: 1s;
}
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(2.5);
opacity: 0;
}
}
一个伪元素能撑起一层的扩散,第二层靠 ::after 延迟半拍再播一遍,视觉上就连成了连续脉冲。如果你想做三个圈,可以再包一层子元素,或者用 box-shadow 的扩散模拟:box-shadow: 0 0 0 0 rgba(...) 配合动画也可以,但 box-shadow 的性能比 border 差,圈多的时候明显发热,我建议伪元素的方案。
4.4 骨架屏闪烁:不闪的骨架屏没灵魂
骨架屏比转圈更有"原生感",现在几乎成了列表页加载的标准做法。它的原理是用一个渐变底色来回移动,让人感觉内容在"加载中"。最佳实践是:灰色底色 + 一个白色渐变条扫过 + background-position 来回移动。
css复制.skeleton {
height: 16px;
border-radius: 6px;
background: linear-gradient(90deg, #eee 25%, #f5f5f5 37%, #eee 63%);
background-size: 400% 100%;
animation: shimmer 1.4s ease infinite;
}
@keyframes shimmer {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}
这里有两个容易抄错的地方。第一,background-size 必须大于 100%,否则 background-position 移动时没有足够的渐变范围可以滑,看不出流动。第二,渐变中间的亮色段 #f5f5f5 是"高光",它的位置决定了扫光在哪个区域。你想让扫光更宽,就把 37% 和 63% 这两个值互相靠近,高光带就变宽。
4.5 渐变背景自动流动
渐变背景流动是我做登录页、活动页的保留节目。以前这种效果得上 canvas 或者图片,现在几行 CSS 就够。核心是让 background-size 放大到 400%,再让 background-position 在几个位置之间来回移,视觉上像极光或者彩带在动。
css复制.bg-flow {
height: 100vh;
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
background-size: 400% 400%;
animation: bgFlow 12s ease infinite;
}
@keyframes bgFlow {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
四个颜色角度是 -45deg,让渐变不要横向排成一条直线而是斜着铺开,流动起来更自然。这个效果是全页面背景,animation 一次性 12 秒循环,用户基本感知不到重复。唯一要注意的是不要让颜色对比太刺眼,饱和度过高会抢掉前景内容的注意力,一般我会把颜色的亮度往下压 10% 左右。
5. 文字与Logo类拆解:渐变流光、霓虹、逐字弹跳、光标
文字特效是"装X"的高地,因为文字是最容易被注意到的元素。但也是最容易翻车的地方,文字要做动画,通常要么用 background-clip: text 给文字上渐变色,要么用 text-shadow 做发光,要么把文字的每个字拆成 <span> 单独控制。
5.1 渐变流光文字:background-clip:text的玩法
最近在社区里很火的渐变文字,很多人以为是图片,其实原理特别简单:把渐变色铺满文字区域,然后用 background-clip: text 把背景裁剪成文字的形状,再让文字本身透明,视觉上露出来的就是渐变文字。加上 background-position 的移动,就成了流光效果。
css复制.text-gradient {
font-size: 48px;
font-weight: 700;
background: linear-gradient(90deg, #ff6b6b, #feca57, #48dbfb, #ff6b6b);
background-size: 300% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: textFlow 4s linear infinite;
}
@keyframes textFlow {
0% {
background-position: 0% 50%;
}
100% {
background-position: 300% 50%;
}
}
-webkit-background-clip: text 这个前缀在主流浏览器里目前基本都需要,写的时候两个都写上。background-size: 300% 是为了让渐变有足够的长度来"流动",你从 0% 移动到 300% 正好循环回来,接缝处完全看不出来。
5.2 霓虹灯与呼吸辉光
霓虹灯靠的是 text-shadow 多层叠加。一层发光不够,要内外两层甚至三层,从近到远分别是白芯、主色、扩散光。
css复制.text-neon {
color: #fff;
font-size: 48px;
text-shadow:
0 0 4px #fff,
0 0 8px #0ff,
0 0 16px #0ff,
0 0 32px #0ff;
animation: neonBreath 3s ease-in-out infinite;
}
@keyframes neonBreath {
0%, 100% {
opacity: 1;
text-shadow:
0 0 4px #fff,
0 0 8px #0ff,
0 0 16px #0ff,
0 0 32px #0ff;
}
50% {
opacity: 0.8;
text-shadow:
0 0 2px #fff,
0 0 4px #0ff,
0 0 8px #0ff,
0 0 16px #0ff;
}
}
呼吸辉光和霓虹的代码几乎一样,区别只在 keyframe 里:呼吸是让阴影在"亮"和"更亮"之间波动,霓虹闪烁则是让 opacity 在 1 和 0.7 之间跳变,并且可以在某个时刻突然降到 0.4 再回来,模拟老式霓虹灯管的接触不良感。
5.3 逐字弹跳和波浪文字
逐字弹跳要把每个字包一层 <span>,然后给每个 span 设置不同的 animation-delay。比如"你好世界"四个字,第一个字 delay 0s,第二个 0.1s,第三个 0.2s,第四个 0.3s,看起来就像多米诺骨牌一样依次弹起来。
css复制/* HTML: <div class="text-bounce"><span>你</span><span>好</span><span>世</span><span>界</span></div> */
.text-bounce span {
display: inline-block;
animation: charBounce 1.5s ease-in-out infinite;
}
.text-bounce span:nth-child(2) {
animation-delay: 0.1s;
}
.text-bounce span:nth-child(3) {
animation-delay: 0.2s;
}
.text-bounce span:nth-child(4) {
animation-delay: 0.3s;
}
@keyframes charBounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-14px);
}
}
这里有个很多人不注意的点:<span> 默认是行内元素,transform 对行内元素不生效,所以必须加 display: inline-block。波浪文字就是把 translateY 的位移改成每个 span 不同的延迟和幅度,再加上一点点 rotate,视觉上就像波浪扫过一行字。
5.4 模拟打字光标
文章页、终端风格的介绍页经常用到一个闪烁的光标。这个是纯CSS最好写的效果之一,用 ::after 伪元素画一个竖线,然后让它的透明度在 0 和 1 之间瞬间切换。
css复制.cursor::after {
content: '|';
margin-left: 2px;
color: #333;
animation: blink 1s step-end infinite;
}
@keyframes blink {
50% {
opacity: 0;
}
}
关键在 step-end,它是 steps(1, end) 的简写,表示动画不是平滑过渡,而是瞬间跳到下一个状态。用 linear 会出现光标半透明慢慢消失的"残影",用 step-end 就是干净利落的亮、灭、亮、灭,和真实终端光标一模一样。
6. 卡片与3D类拆解:翻转、玻璃拟态、立体按压
3D类和前面的特效有个本质区别:它需要"透视"才能产生纵深。CSS里的 perspective 就是干这个的。没有 perspective,rotateY 最多算压扁,加了 perspective,元素旋转时才会有近大远小的立体感。
6.1 3D翻转卡片:perspective和preserve-3d缺一不可
3D翻转卡片的结构是三层:外层容器负责提供 perspective,内层容器负责翻转并保留3D上下文,前后两个面用绝对定位叠在一起。翻转时内层转 180 度,正面翻进去、背面翻出来。
css复制.flip-card {
width: 240px;
height: 320px;
perspective: 1000px;
}
.flip-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.6s ease;
transform-style: preserve-3d;
}
.flip-card:hover .flip-inner {
transform: rotateY(180deg);
}
.flip-front,
.flip-back {
position: absolute;
inset: 0;
backface-visibility: hidden;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.flip-front {
background: linear-gradient(135deg, #667eea, #764ba2);
color: #fff;
}
.flip-back {
background: #fff;
transform: rotateY(180deg);
}
三个属性缺一个都会翻车。perspective 决定立体感的强弱,值越小透视越强,一般 600px 到 1200px 之间比较自然。transform-style: preserve-3d 是让内层容器里的两个面能保持3D位置关系,不加它,子元素会被压平。backface-visibility: hidden 是翻转过程中让背面不可见,否则你会看到文字镜像。背面那块必须自己也转 180 度,不然它是反着朝里的。
6.2 玻璃拟态悬浮卡片
玻璃拟态从2022年前后开始流行,到现在依然很能打。它不需要 JS 库,核心是 backdrop-filter: blur() 这行,把背景里的内容模糊化,配合半透明背景和细边框,模拟磨砂玻璃。
css复制.glass-card {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 16px;
padding: 40px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
transition: transform 0.4s ease, box-shadow 0.4s ease;
}
.glass-card:hover {
transform: translateY(-6px) scale(1.02);
box-shadow: 0 20px 48px rgba(0, 0, 0, 0.2);
}
backdrop-filter 有个使用前提:它模糊的是"元素背后的内容",所以这个元素后面必须有其他内容(比如一张彩色背景图)才有意义。如果页面是白底,玻璃拟态的效果会很弱。想让效果明显,背景图尽量用色彩丰富的。另外 backdrop-filter 目前仍建议带上 -webkit- 前缀。
6.3 按压反馈:手感和视觉同步
按压反馈是所有可点击元素都应该有的基本功。它不需要花哨,但非常重要,因为用户点击按钮时,如果页面没有任何物理反馈,会怀疑自己到底点没点上。原理是常态下按钮有一个向下的阴影,active 时把阴影去掉,同时把按钮往下压 4px。
css复制.btn-press {
padding: 12px 32px;
background: #667eea;
color: #fff;
border: none;
border-radius: 8px;
box-shadow: 0 4px 0 #4a5ac8;
transition: transform 0.08s ease, box-shadow 0.08s ease;
}
.btn-press:active {
transform: translateY(4px);
box-shadow: 0 0 0 #4a5ac8;
}
注意 transition 的时长我设成了 0.08s,几乎是瞬时的。按压反馈的核心是"立刻响应",如果加 0.2s 的过渡,用户按下去反馈慢半拍,体验反而更差。这类"手感类"动画和"氛围类"动画的节奏完全不同,刚开始做的人很容易把两个搞混。
6.4 剩余特效的极简速查
前面完整的代码已经覆盖了 14 个左右,剩下的我用极简版补充在这里,思路清晰、复制即用。
图片悬停缩放:
css复制.card-img {
overflow: hidden;
}
.card-img img {
transition: transform 0.4s ease;
}
.card-img:hover img {
transform: scale(1.1);
}
链接下划线滑动:
css复制.nav-link {
position: relative;
text-decoration: none;
}
.nav-link::after {
content: '';
position: absolute;
left: 0;
bottom: -2px;
width: 100%;
height: 2px;
background: currentColor;
transform: scaleX(0);
transform-origin: right;
transition: transform 0.3s ease;
}
.nav-link:hover::after {
transform: scaleX(1);
transform-origin: left;
}
双色渐变转圈:在基础转圈基础上,把 border 换成四条不同颜色,或者用 border-top-color 和 border-bottom-color 两色交替。
心跳动效:
css复制.heart {
animation: heartbeat 1.2s ease-in-out infinite;
}
@keyframes heartbeat {
0%, 100% { transform: scale(1); }
25% { transform: scale(1.15); }
40% { transform: scale(0.95); }
60% { transform: scale(1.15); }
}
旋转渐变色环:用 conic-gradient 做背景,旋转整个元素,适合头像边框。
css复制.ring {
padding: 4px;
border-radius: 50%;
background: conic-gradient(#ff6b6b, #feca57, #48dbfb, #ff6b6b);
animation: spin 3s linear infinite;
}
.ring > img {
display: block;
border-radius: 50%;
}
跑马灯边框:这个稍微复杂,思路是给卡片一个比自身大一圈的旋转渐变层,然后让内层遮住中间只露出边缘。我用一个简化但能用的版本:外层的伪元素 background: conic-gradient(...) 旋转,内层盖一块更大的背景遮住中心,只保留边缘一圈颜色滚动。
7. 看懂这几行才能改得动:CSS动画的核心机制
抄代码一时爽,改代码全靠机制。如果你不想每次都在微调参数上碰运气,这节值得细看。我把CSS动画最核心的几个知识点拆开讲明白。
7.1 transition的四个参数和两种写法
一条完整的 transition 有四个属性:transition-property(过渡哪个属性)、transition-duration(时长)、transition-timing-function(缓动函数)、transition-delay(延迟)。简写顺序是:
css复制transition: property duration timing-function delay;
比如 transition: all 0.3s ease 0.2s,表示所有属性变化都过渡 0.3 秒、缓动为 ease、延迟 0.2 秒开始。多个属性要分别控制,用逗号分隔:
css复制transition:
transform 0.3s ease,
box-shadow 0.3s ease 0.1s;
这样可以让阴影比位移慢半拍出现,视觉层次更丰富。注意,实际开发里我几乎不用 all,因为 all 会把 opacity、color、margin 全部纳入过渡,浏览器要监听一堆属性,性能和可控性都不好。
7.2 @keyframes的本质:关键帧补间动画
@keyframes 的工作机制和传统动画软件完全一样:你定义几个关键帧(比如 0% 和 100%),中间的状态由浏览器自动补间计算。它和 transition 的核心区别是,transition 只能管"两个状态之间的一次变化",而 keyframes 可以定义任意多个状态,还能循环播放。
css复制@keyframes example {
0% {
transform: scale(1);
opacity: 0;
}
50% {
transform: scale(1.2);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0;
}
}
动画的所有播放参数通过 animation 简写控制:animation: name duration timing-function delay iteration-count direction fill-mode。比如 animation: example 2s ease 1s infinite alternate both,意思是播放2秒、缓动、延迟1秒、无限循环、每次反向交替、播放前和结束后都应用第一帧和最后一帧的样式。后面这几个参数新手经常用错,尤其是 fill-mode,它是解决"动画一闪而过"的关键。
7.3 transform三兄弟和transform-origin
transform 家族里最常用的就是 translate(位移)、scale(缩放)、rotate(旋转)这三个,合称"三兄弟"。它们都作用在元素的坐标系上,可以组合使用:
css复制transform: translateY(-50%) scale(1.1) rotate(5deg);
多个函数从左到右生效,顺序会影响最终效果,这一点和矩阵运算的先后有关。实际使用中,如果你 hover 要改比例、又要位移,建议和初始状态的函数顺序保持一致,否则会出现位置跳变。
transform-origin 控制变形的中心点,默认是元素中心(50% 50%)。改成 transform-origin: left 后,缩放会从左边开始往右长;改成 transform-origin: top left 后,旋转会以左上角为轴。很多"从中间向两边生长"的效果,靠的就是它。
7.4 缓动函数:ease、cubic-bezier和step
缓动函数决定动画加速和减速的节奏。linear 是匀速,ease 是慢快慢,ease-in 是越来越快,ease-out 是越来越慢,ease-in-out 是两端都慢。真实项目里我常用的就两个方向:
- 元素进入场景(弹窗出现、卡片滑入):用
ease-out,起手快、收尾慢,有"嗖一下进来再稳稳停住"的感觉。 - 元素离开场景(关闭、移除):用
ease-in,起手慢、越来越快,有"加速退场"的感觉。
想要更精确的手感,用 cubic-bezier 自定义。cubic-bezier(0.34, 1.56, 0.64, 1) 是"回弹"型曲线,末尾会微微超过目标值再回来,很多弹性效果就这么来的。steps 则是离散跳变,前面打字光标用的 step-end 就是它。调试贝塞尔曲线我一般不手算,直接用浏览器 DevTools 的动画面板拖控制点,所见即所得。
8. 踩坑实录:这些经典问题我劝你直接记笔记
最后这部分是我这些年帮同事修动画 bug 攒下来的高频问题。每一个我都见过不下三回,你记住这些问题,至少能省下一个月的排查时间。
8.1 display:none让transition当场失效
最经典的坑:
