动画与交互 —— 过渡、关键帧、滚动驱动、视图过渡

一个让用户"哇"出来的动效

2015 年,我在一个旅游网站工作。产品经理把一个设计稿扔到我面前:首页的 banner 上,一行标题从左侧滑入,背景图片缓慢缩放,下面的卡片随着页面滚动一个一个淡入。他淡淡地说:"这种效果在别的网站上看起来很酷,我们也做吧。"

我心想:不难。用 CSS transition 做悬停效果,用 @keyframes 做入场动画,用 scroll 事件监听滚动位置,给卡片加 class...... 吭哧吭哧写了三天,JS 里堆满了 addEventListenergetBoundingClientRectrequestAnimationFrame,还要处理节流、销毁事件。上线后,用户在低端手机上滑动时明显掉帧,产品经理指着 Chrome 的 "jank" 警告,问我为什么不能"丝滑一点"。

那时候我不知道,有一种叫做 scroll-driven animations 的 CSS 特性即将出现,它可以把滚动动画完全交给浏览器合成器线程,不再占用主线程。更不知道,几年后 view-transitions 会让页面切换像原生 App 一样平滑。

如今是 2026 年,CSS 动画已经从一个"点缀"升级为交互的核心手段。这篇文章,我会带你完整梳理 CSS 动画的四大支柱:过渡关键帧动画滚动驱动动画视图过渡。既有基础概念,也有实战案例和未来展望。读完你会明白,如何用最少的代码,实现让用户"哇"出来的流畅体验。

第一章:过渡(transition)------ 动画的基石

1.1 从"突变"到"渐变"

transition 可能是 CSS 里最简单的动画工具。它让属性值的变化不是瞬间完成,而是在一段时间内平滑过渡。

css 复制代码
.button {
  background-color: blue;
  transition: background-color 0.3s ease;
}
.button:hover {
  background-color: red;
}

当你鼠标悬停时,背景色从蓝色渐变为红色,耗时 0.3 秒。如果没有 transition,它会"啪"地一下变红,毫无过渡。

1.2 可过渡的属性

不是所有属性都能过渡。只有那些可插值的属性才可以,即数值、颜色、长度、百分比等。例如:

  • 颜色:color, background-color, border-color
  • 尺寸:width, height, padding, margin, font-size
  • 变换:transform
  • 透明度:opacity
  • 阴影:box-shadow, text-shadow

不能过渡的属性包括:display(可以用 visibility 过渡)、position(但 top/left 可以,只要它们有数值变化)、z-index 等。

1.3 transition 的完整语法

css 复制代码
.el {
  transition-property: all;           /* 要过渡的属性,all 表示所有 */
  transition-duration: 0.3s;          /* 持续时间 */
  transition-timing-function: ease;    /* 缓动函数 */
  transition-delay: 0s;               /* 延迟时间 */
}
/* 简写 */
.el {
  transition: all 0.3s ease 0s;
}

缓动函数决定动画的速度曲线:

  • ease:慢-快-慢,默认
  • linear:匀速
  • ease-in:加速
  • ease-out:减速
  • ease-in-out:先加速后减速
  • cubic-bezier():自定义贝塞尔曲线

1.4 实战技巧:使用 transform 代替 top/left 做位移

transform 在合成器线程中执行,不会触发布局重排(reflow),性能远优于 top/left

css 复制代码
/* 不推荐 */
.box:hover { left: 100px; }
/* 推荐 */
.box:hover { transform: translateX(100px); }

1.5 多个属性分别过渡

css 复制代码
.btn {
  transition: transform 0.2s ease, background-color 0.3s linear, box-shadow 0.15s;
}

1.6 过渡结束事件

JavaScript 中可以监听 transitionend 事件,在动画完成后执行逻辑。

js 复制代码
element.addEventListener('transitionend', () => {
  console.log('过渡结束');
});

过渡简单、兼容性好,是现代 Web 动效的基石。但它只能实现"状态变化"的动画,无法做循环或复杂序列动画。这时就需要关键帧动画。


第二章:关键帧动画(@keyframes)------ 表达复杂时序

2.1 定义关键帧

@keyframes 让你定义动画的中间状态(关键帧),浏览器自动插值中间帧。

css 复制代码
@keyframes slideIn {
  0% {
    transform: translateX(-100%);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}

.box {
  animation: slideIn 0.5s ease-out;
}

也可以使用 fromto 替代 0%100%

2.2 animation 属性详解

css 复制代码
.box {
  animation-name: slideIn;
  animation-duration: 1s;
  animation-timing-function: ease;
  animation-delay: 0s;
  animation-iteration-count: infinite;  /* 循环次数 */
  animation-direction: alternate;       /* 方向:normal, reverse, alternate, alternate-reverse */
  animation-fill-mode: forwards;        /* 动画结束后保持最后一帧状态 */
  animation-play-state: running;        /* 播放或暂停 */
}
/* 简写 */
.box {
  animation: slideIn 1s ease 0s infinite alternate forwards;
}

2.3 多个关键帧与百分比

你可以定义任意多的关键帧,实现复杂运动。

css 复制代码
@keyframes bounce {
  0%, 100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-30px);
  }
  80% {
    transform: translateY(-10px);
  }
}

2.4 无限循环与方向

css 复制代码
.loader {
  animation: spin 1s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

2.5 动画与过渡的配合

通常用过渡做悬停、焦点等交互反馈,用关键帧做入场、循环、吸引注意的动效。


第三章:滚动驱动动画(Scroll-driven Animations)------ 新时代的杀手锏

这是 2023 年以来 CSS 最激动人心的新特性之一。过去,滚动视差、滚动进度条等效果必须用 JS 监听滚动事件,性能差、代码臃肿。现在可以用 CSS 直接声明:"当滚动到某个位置时,动画完成百分之多少"。

3.1 核心概念

滚动驱动动画分为两种:

  • 滚动进度动画(Scroll Progress):动画进度与滚动容器的滚动位置绑定。
  • 视图进度动画(View Progress):动画进度与元素在视口中的可见度绑定。

3.2 滚动进度动画

假设你有一个进度条,随着页面滚动而填充:

css 复制代码
@keyframes grow {
  from { width: 0%; }
  to { width: 100%; }
}

.progress {
  animation: grow linear;
  animation-timeline: scroll();  /* 关键!绑定滚动进度 */
  position: fixed;
  top: 0;
  left: 0;
  height: 4px;
  background: blue;
}

animation-timeline: scroll() 表示动画的进度由最近的滚动容器的滚动位置决定。scroll() 可以指定滚动轴:scroll(block)(默认,垂直滚动)或 scroll(inline)(水平滚动)。

你也可以指定滚动容器:scroll(scroller nearest)

3.3 视图进度动画

当某个元素进入视口时,动画开始;完全离开时,动画结束。

css 复制代码
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}

.card {
  animation: fadeIn linear;
  animation-timeline: view();        /* 基于元素自身的视图进度 */
  animation-range: entry 0% entry 100%;  /* 可选,定义动画范围 */
}

animation-range 进一步控制动画的起止点。例如:

  • entry 0% entry 100%:元素从开始进入视口到完全进入,动画从 0% 到 100%。
  • exit 0% exit 100%:元素从开始离开到完全离开。
  • contain 0% contain 100%:元素完全在视口内时触发。

3.4 实战:滚动视差

css 复制代码
.parallax {
  animation: parallax linear;
  animation-timeline: scroll();
  animation-range: exit 0% exit 200%;
  transform-origin: center;
}

@keyframes parallax {
  from { transform: translateY(0); }
  to { transform: translateY(200px); }
}

当元素向上滚动离开视口时,它会以较慢的速度移动,产生视差效果。

3.5 浏览器支持

截至 2026 年,滚动驱动动画已在 Chrome 115+、Edge 115+、Firefox 110+(需启用标志)、Safari 16.4+ 中得到部分支持。完整支持仍在推进,但生产环境可通过 @supports 渐进增强。

css 复制代码
@supports (animation-timeline: scroll()) {
  .progress {
    animation-timeline: scroll();
  }
}

3.6 与 GSAP 等库的对比

GSAP 的 ScrollTrigger 插件功能极其强大,但滚动驱动动画的优势是原生、高性能(运行在合成器线程),适合简单到中度的滚动联动效果。复杂的时间线、交错动画仍然需要 JS 库。


第四章:视图过渡(View Transitions)------ SPA 级别的平滑切换

视图过渡是另一个 2023--2024 年间推出的革命性特性。它让同一个 DOM 在不同状态之间切换时(例如页面跳转、列表重排),可以产生流畅的过渡动画,无需手动编写复杂的 DOM 状态管理。

4.1 核心 API

视图过渡的核心是 document.startViewTransition() 方法。当你调用它并传入一个更新 DOM 的回调时,浏览器会:

  1. 捕获当前页面的快照(截图)。
  2. 执行回调,更新 DOM。
  3. 捕获新状态的快照。
  4. 在两个快照之间创建过渡动画(默认是淡入淡出)。
js 复制代码
function spaNavigate(url) {
  if (!document.startViewTransition) {
    // 降级:直接跳转
    window.location.href = url;
    return;
  }
  document.startViewTransition(() => {
    // 更新 DOM 到新页面状态
    updateDOMForPage(url);
  });
}

4.2 CSS 定制过渡效果

你可以通过伪元素 ::view-transition-old::view-transition-new 自定义动画:

css 复制代码
/* 默认效果:淡入淡出 */
::view-transition-old(root) {
  animation: fade-out 0.3s ease;
}
::view-transition-new(root) {
  animation: fade-in 0.3s ease;
}

更高级的用法:为不同元素指定独立的过渡组。使用 view-transition-name 属性。

css 复制代码
.header {
  view-transition-name: header;
}
.sidebar {
  view-transition-name: sidebar;
}

然后就可以针对 headersidebar 分别定义过渡动画,比如让标题从左边飞入,侧边栏从右边飞入。

4.3 多页面应用(MPA)中的视图过渡

2026 年,Chrome 和 Firefox 已经支持跨文档视图过渡 (Cross-document View Transitions),即在传统刷新页面式的导航中也能使用视图过渡。只需在 <link>meta 中声明:

html 复制代码
<link rel="stylesheet" href="style.css" media="navigation" />

或通过 JS 启用:

js 复制代码
window.addEventListener('pagereveal', (e) => {
  e.viewTransition.updateCallbackDone = () => {
    // 新页面已加载但未渲染
  };
});

4.4 实战:图片画廊的平滑切换

js 复制代码
document.querySelectorAll('.thumbnail').forEach(thumb => {
  thumb.addEventListener('click', async (e) => {
    const fullImage = document.getElementById('full-image');
    const imageUrl = thumb.dataset.large;
    if (document.startViewTransition) {
      document.startViewTransition(() => {
        fullImage.src = imageUrl;
      });
    } else {
      fullImage.src = imageUrl;
    }
  });
});

配合 CSS 过渡,可以实现大图从缩略图位置展开的效果。

4.5 注意事项

  • 视图过渡会捕获页面截图,对性能有一定影响,不要滥用。
  • 某些元素(如 fixed 定位的元素)在过渡中可能表现异常,需要调整 view-transition-name 或避免。
  • 早期版本有 bug,2026 年已基本稳定。

第五章:综合实战 ------ 一个完整的交互页面

我们将使用以上所有技术,构建一个产品展示页面:

  • 头部导航条,滚动时背景模糊、阴影加深(过渡)。
  • 页面加载时,标题从上方淡入(关键帧)。
  • 滚动时,每个产品卡片依次淡入(视图进度动画)。
  • 点击卡片,展开详情视图(视图过渡 + 关键帧)。
  • 页面顶部显示阅读进度条(滚动进度动画)。

由于代码量较大,这里给出核心结构。

html 复制代码
<header class="sticky-header">...</header>
<div class="progress-bar"></div>
<main>
  <h1 class="page-title">我们的产品</h1>
  <div class="card">...</div>
  ...
</main>
css 复制代码
/* 头部滚动效果 */
.sticky-header {
  transition: background-color 0.2s, box-shadow 0.2s;
}
.sticky-header.scrolled {
  background: rgba(255,255,255,0.95);
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

/* 进度条滚动动画 */
.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  background: #f90;
  animation: grow linear;
  animation-timeline: scroll();
}
@keyframes grow {
  from { width: 0%; }
  to { width: 100%; }
}

/* 卡片视图进度动画 */
.card {
  animation: fadeUp linear;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(30px); }
  to { opacity: 1; transform: translateY(0); }
}

/* 页面标题入场关键帧 */
.page-title {
  animation: slideDown 0.5s ease-out;
}
@keyframes slideDown {
  from { opacity: 0; transform: translateY(-20px); }
  to { opacity: 1; transform: translateY(0); }
}
js 复制代码
// 视图过渡:点击卡片展开详情
document.querySelectorAll('.card').forEach(card => {
  card.addEventListener('click', () => {
    const details = card.querySelector('.details');
    if (document.startViewTransition) {
      document.startViewTransition(() => {
        details.classList.toggle('expanded');
      });
    } else {
      details.classList.toggle('expanded');
    }
  });
});

这个页面没有使用任何第三方库,却实现了现代 Web 动效的最高水平。


第六章:性能优化与最佳实践

6.1 坚持使用 transform 和 opacity

这两个属性只需合成器线程参与,不会触发重排或重绘。例如,移动元素用 translate,淡入淡出用 opacity,缩放用 scale

6.2 使用 will-change 提前告知浏览器

如果某个元素即将发生动画,可以加上 will-change: transform; 提示浏览器创建独立图层,但不要滥用,否则内存过高。

6.3 减少动画层级

动画元素应尽量少,每一层动画都会增加 GPU 合成负担。

6.4 利用 requestAnimationFrame 进行 JS 动画

如果必须用 JS 控制动画,使用 requestAnimationFrame 而非 setTimeout,保证与屏幕刷新率同步。

6.5 优先使用 CSS 动画而非 JS

只要能用 CSS 实现,绝不写 JS。维护成本低,性能好。

6.6 避免动画导致页面跳字(jank)

使用 Chrome DevTools 的 Performance 面板,查看帧率、耗时长的任务。如果发现 Layout Shift,尝试改为 transform


第七章:未来展望

CSS 动画的未来不止于此。我们还将看到:

  • 更精细的滚动驱动控制:比如基于元素在视口中的位置比例,而非简单的进入/离开。
  • 动画合成(Animation Composition):多个动画可以同时控制同一个属性,类似 GSAP 的时间线。
  • 自定义贝塞尔曲线编辑:更直观的缓动函数设计。
  • 视图过渡的进一步普及:跨文档过渡成为默认,甚至支持返回手势动画。

让交互成为体验的灵魂

从简单的悬停过渡,到复杂的关键帧序列,再到滚动驱动动画和视图过渡,CSS 已经从一个"样式语言"进化为一个完整的交互设计平台。如今,我们不再需要为了一个滚动视差而引入几十 KB 的 JS 库,不再需要为了页面切换的流畅而手写状态管理。

你可以用两行 CSS 实现滚动进度条,用 view() 让卡片随着滚动渐入,用 startViewTransition 实现原生 App 般的页面切换。

动效不只是"花哨",它是用户理解界面的向导,是品牌个性的表达,是响应性的确认。在你的下一个项目中,试着多给按钮加一个微妙的过渡,给加载加一个旋转的动画,给卡片加一个优雅的入场动效。你会发现,用户会下意识地觉得"这个网站真流畅"。

CSS 动画的世界,值得我们不断探索。希望这篇文章能成为你进入这个世界的完整地图。

相关推荐
UXbot2 小时前
AI应用原型平台核心能力:界面自动生成、交互流程编辑、多格式代码导出详解
前端·低代码·交互·软件构建·原型模式·web app
cy_cy00219 小时前
解析活跃氛围的互动屏幕应用
大数据·科技·人机交互·交互·软件构建
byte轻骑兵20 小时前
【HID】规范精讲[11]: 蓝牙HID设备信号交互流程深度拆解
人工智能·交互·hid·蓝牙键盘·蓝牙鼠标
lzfshub1 天前
ConvexOffset3D — 凸多面体(棱柱/棱台/棱锥)精确偏移与交互可视化工具
3d·交互
生信之灵3 天前
追踪17只果蝇、7只线虫、10只小鼠,全程无需人工标注:这个无监督跟踪器如何颠覆动物行为研究?
人工智能·深度学习·神经网络·microsoft·交互
星河耀银海4 天前
C语言与数据库交互:SQLite实战与数据持久化
c语言·数据库·sqlite·交互
李李李勃谦5 天前
鸿蒙PCBI 报表工具:连接数据库与可视化报表生成
数据库·华为·交互·harmonyos
生成论实验室5 天前
《事件关系阴阳博弈动力学:识势应势之道》第七篇:社会与情感关系——连接、表达与共鸣
人工智能·算法·架构·交互·创业创新