前端动画实战指南:从CSS3基础语法到性能优化与滚动联动
2026/9/9 21:43:24 网站建设 项目流程

做动画这件事,听起来简单,真正落到项目里却常常遇到“动画没效果”“动画很卡”“动画结束闪回原点”这类问题。本文整理了一套从 CSS3 基础语法到完整业务案例的前端动画实战笔记,覆盖 transition、transform、@keyframes、滚动联动、性能优化和常见排错,适合刚接触前端动画的初学者,也适合需要快速搭出一套可维护动效方案的进阶开发者。

先明确一点:本文讲的“做动画”主要指 Web 前端中的 CSS3 动画与浏览器动画实现方案,不涉及建模软件、视频剪辑或 3D 渲染。前端动画的本质是在连续的时间片内不断改变元素的视觉属性,让用户产生“动起来”的感知。理解了这一点,后面所有的代码都是在回答两个问题:改什么属性、按什么节奏改。

1. 背景与核心概念

1.1 动画的本质

无论使用哪种技术方案,前端动画的核心都是“属性随时间变化”。比如一个按钮从红色变成蓝色,一张图片从透明到完全不透明,一个弹窗从屏幕顶部滑入中间,这些都是视觉属性的连续变化。

在浏览器里,实现这种变化主要有三种手段:

方案原理适用场景
CSS transition属性值从一个状态过渡到另一个状态hover 效果、展开收起、弹窗淡入
CSS animation通过 @keyframes 定义多阶段动画loading、循环动效、复杂关键帧动画
JavaScript 动画用 JS 控制属性变化或驱动游戏循环交互复杂、数据驱动、Canvas 动画

CSS 动画的优点是写法简单、浏览器原生优化、不占用主线程逻辑;JavaScript 动画的优点是控制力强,能根据用户交互动态计算。实际项目中两者通常配合使用,而不是互相对立。

1.2 CSS 动画与 JS 动画怎么选

很多新手纠结“到底用 CSS 还是用 JS”,这里给一个相对简单的判断标准:

  • 如果动画是触发式的,比如鼠标悬停、点击切换、类名变化,优先用 CSS transition。
  • 如果动画是循环播放的,比如 loading 转圈、轮播图自动播放,优先用 CSS animation。
  • 如果动画状态依赖数据,比如进度条根据接口返回值变化,优先用 JS 配合 CSS 变量。
  • 如果动画需要帧级控制、拖拽交互、物理模拟,建议使用 JavaScript 动画库,比如 GSAP、Popmotion,或者直接操作 Canvas。

CSS 能解决的问题,不要急着引入动画库。减少依赖体积,也减少调试成本。

1.3 常见应用场景

前端动画的应用场景非常广泛,常见的有:

  1. 页面加载反馈:loading 转圈、骨架屏、进度条。
  2. 交互反馈:按钮点击波纹、菜单展开收起、弹窗淡入淡出。
  3. 视觉引导:新功能提示、滚动入场动画、图标变换。
  4. 数据可视化:图表过渡、数字滚动、排行榜变动。

本文后面的实战案例,会围绕 loading、卡片悬浮、滚动入场这三个最常用的场景展开,每一个都能直接复制到项目中使用。

2. 环境准备与项目结构

2.1 开发环境

本文的示例不需要安装任何框架依赖,只需要一个现代浏览器和任意代码编辑器即可。

  • 操作系统:Windows / macOS / Linux 均可。
  • 浏览器:建议使用 Chrome 或 Edge,方便打开 DevTools 的 Performance 面板检查动画性能。
  • 编辑器:VS Code、WebStorm 均可,本文不依赖编辑器插件。
  • 运行方式:直接双击 HTML 文件在浏览器打开,或使用 VS Code 的 Live Server 插件启动本地服务。

版本方面不需要刻意统一,CSS3 动画相关语法已经稳定多年,主流浏览器均支持。需要特别注意的是 CSS 属性前缀问题,现代浏览器已经不需要手动写-webkit-前缀,但如果你要兼容旧版本浏览器,建议通过 Autoprefixer 自动处理。

2.2 示例项目结构

为了清晰演示,本文创建一个最小的项目结构:

animation-demo/ ├── case1-loading.html ├── case2-card.html ├── case3-scroll.html └── README.md

这种方式适合零基础读者,每个案例一个独立 HTML 文件,互不干扰。实际项目开发中,建议把公共动画样式抽成独立的 CSS 文件,例如animations.css,再按业务组件拆分布局样式。

3. CSS3 动画核心语法拆解

3.1 transition 过渡:最简单的“做动画”方式

transition 是最容易上手的动画语法,它描述的是“当某个属性发生变化时,如何平滑过去”。

先看一个最简单的例子。下面这段代码让按钮在鼠标悬停时背景颜色平滑变化:

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>transition 基础示例</title> <style> .btn { width: 120px; height: 44px; background: #409eff; color: #fff; border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.3s ease; } .btn:hover { background: #337ecc; } </style> </head> <body> <button class="btn">悬停试试</button> </body> </html>

关键代码是这一行:

transition: background-color 0.3s ease;

它拆开来看包含四个部分:

  • transition-property:要过渡的属性名,这里是background-color
  • transition-duration:过渡耗时,这里是0.3s
  • transition-timing-function:速度曲线,常用easelinearease-inease-out
  • transition-delay:延迟开始时间,默认 0。

新手容易犯的一个错误是写成transition: all 0.3s,虽然能用,但all会让所有可过渡属性都参与过渡,一旦元素属性变多,可能出现意料之外的动画效果,也会增加浏览器计算压力。推荐明确写出需要过渡的属性名。

transition 只能响应属性的“开始状态”和“结束状态”,中间过程由浏览器自动插值计算。它无法实现多阶段动画,比如“先放大、再缩小、最后旋转”,这种需求要交给 animation。

3.2 transform 变换:让元素动起来的核心属性

动画如果只改颜色和透明度,表现力远远不够。真正让元素“移动”“旋转”“缩放”的是transform属性。

transform 常用函数如下:

函数作用示例
translateX / translateY平移translateX(50px)
scale缩放scale(1.2)
rotate旋转rotate(45deg)
skew倾斜skew(10deg)
translate3d / scale3d3D 变换translate3d(10px, 0, 0)

一个典型的卡片悬浮效果,就是通过transform: translateY()scale()结合完成的:

.card { transition: transform 0.3s ease, box-shadow 0.3s ease; } .card:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12); }

这里需要强调一个性能相关的点:transformopacity是浏览器专门优化的合成器属性,修改它们不会触发重排和重绘,动画过程可以交给 GPU 合成,性能远好于修改widthheighttopleft等布局属性。后面的性能章节还会详细展开。

3.3 animation 与 @keyframes:实现多阶段动画

当动画不止“开始”和“结束”两个状态时,就需要使用@keyframes定义关键帧,再用animation属性引用。

先看一个经典的多阶段动画:一个方块先向右移动,再旋转,最后变成圆形。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>keyframes 基础示例</title> <style> .box { width: 80px; height: 80px; background: #409eff; border-radius: 8px; animation: demoMove 2s ease-in-out infinite alternate; } @keyframes demoMove { 0% { transform: translateX(0); border-radius: 8px; } 50% { transform: translateX(120px) rotate(180deg); } 100% { transform: translateX(240px) rotate(360deg); border-radius: 50%; } } </style> </head> <body> <div class="box"></div> </body> </html>

@keyframes内部用百分比定义每个时间点的状态:

  • 0%代表动画起点。
  • 50%代表动画进行到一半时的状态。
  • 100%代表动画终点。

如果动画只想定义起点和终点,也可以简写成fromto

@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

3.4 animation 的 8 个子属性

animation是复合属性,由 8 个子属性组成,写作顺序自由,但推荐固定顺序便于阅读:

.element { animation: animation-name /* 动画名称,对应 @keyframes 名字 */ animation-duration /* 动画时长 */ animation-timing-function /* 速度曲线 */ animation-delay /* 延迟时间 */ animation-iteration-count /* 播放次数 */ animation-direction /* 播放方向 */ animation-fill-mode /* 结束后状态 */ animation-play-state; /* 播放/暂停 */ }

一个典型写法:

.spinner { animation: spin 1s linear infinite; }

这里省略了delaydirectionfill-modeplay-state,浏览器会取默认值。

常用子属性含义:

  • animation-iteration-count:播放次数,写成infinite表示无限循环。
  • animation-directionnormal正向播放,alternate正反交替播放。
  • animation-fill-modeforwards保持最后一帧状态,backwards在延迟期间应用第一帧状态,both同时应用前后两帧。
  • animation-play-staterunning播放中,paused暂停。

新手最常踩的坑是:动画播完后元素瞬间回到初始状态。想要让动画停在最终位置,必须显式设置animation-fill-mode: forwards或用animation: ... both

4. 完整实战:三个贴近业务的动画案例

4.1 案例一:纯 CSS Loading 加载动画

加载动画是每个前端项目几乎都会遇到的需求。用一个转圈效果举例,完整代码可以直接保存为case1-loading.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>CSS Loading 动画</title> <style> .loading { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; } .spinner { width: 48px; height: 48px; border: 4px solid rgba(64, 158, 255, 0.2); border-top-color: #409eff; border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .loading-text { margin-top: 16px; color: #666; font-size: 14px; } </style> </head> <body> <div class="loading"> <div class="spinner"></div> <p class="loading-text">加载中,请稍候...</p> </div> </body> </html>

实现思路:

  1. 创建一个宽高相等的圆角元素。
  2. 只给一个边框设置深色,其余边框设置为浅色透明。
  3. 让整个元素无限旋转,形成转圈效果。

为什么推荐用border-radius: 50%border而不是用图片素材?因为纯 CSS 实现的 loading 不依赖外部资源,静态页面也能展示,也方便通过 CSS 变量调整颜色和尺寸。

另一种常见的 loading 是圆点跳动效果,可以同时使用多个子元素,给每个子元素设置不同的animation-delay,形成波浪感:

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Loading 点阵动画</title> <style> .dots { display: flex; gap: 8px; min-height: 100vh; align-items: center; justify-content: center; } .dots span { width: 12px; height: 12px; background: #409eff; border-radius: 50%; animation: bounce 0.6s ease-in-out infinite alternate; } .dots span:nth-child(2) { animation-delay: 0.2s; } .dots span:nth-child(3) { animation-delay: 0.4s; } @keyframes bounce { from { transform: translateY(0); opacity: 0.5; } to { transform: translateY(-14px); opacity: 1; } } </style> </head> <body> <div class="dots"> <span></span> <span></span> <span></span> </div> </body> </html>

注意这里用了alternate方向,让圆点在上下之间往返,配合不同的animation-delay,三个点的节奏错开,视觉上更活泼。

4.2 案例二:hover 悬浮卡片动效

卡片交互是后台管理系统、官网展示页都高频出现的场景。下面实现一个完整可运行的卡片悬浮动效,保存为case2-card.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>悬浮卡片动效</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { display: flex; align-items: center; justify-content: center; min-height: 100vh; background: #f5f7fa; font-family: Arial, sans-serif; } .card { width: 260px; padding: 20px; background: #fff; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); transition: transform 0.3s ease, box-shadow 0.3s ease; } .card:hover { transform: translateY(-8px); box-shadow: 0 16px 32px rgba(0, 0, 0, 0.12); } .card:focus-within { transform: translateY(-8px); box-shadow: 0 16px 32px rgba(0, 0, 0, 0.12); } .card-image { width: 100%; height: 140px; background: linear-gradient(135deg, #409eff, #79bbff); border-radius: 8px; transition: transform 0.4s ease; } .card:hover .card-image { transform: scale(1.03); } .card-title { margin-top: 16px; font-size: 18px; color: #303133; } .card-desc { margin-top: 8px; font-size: 14px; line-height: 1.6; color: #909399; } .card-btn { display: inline-block; margin-top: 16px; padding: 8px 20px; font-size: 14px; color: #fff; background: #409eff; border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.2s ease; } .card-btn:hover { background: #337ecc; } </style> </head> <body> <div class="card"> <div class="card-image"></div> <h3 class="card-title">前端动画实战</h3> <p class="card-desc"> 从 CSS3 基础语法到业务场景落地,包含 loading、悬浮卡片、滚动入场等常见动效方案。 </p> <button class="card-btn">马上学习</button> </div> </body> </html>

这个案例有几个设计细节值得注意:

  1. 卡片悬浮的位移和阴影使用两个独立属性的 transition 组合,实现“浮起来”的层次感。
  2. 图片区域单独做了scale(1.03)缩放,视觉上比纯文字卡片更有反馈。
  3. 加入了:focus-within伪类,当用户通过 Tab 键聚焦到卡片的按钮时,也有同样的悬浮效果。这是容易被忽略的可访问性优化。

4.3 案例三:滚动入场动画与 JS 联动

纯 CSS 动画只能自己播放,如果想让动画“等用户滚动到某个位置才开始”,需要配合 JavaScript 判断元素是否进入视口。

这里使用浏览器原生IntersectionObserver,不需要引入任何第三方库。完整代码保存为case3-scroll.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>滚动入场动画</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #f5f7fa; font-family: Arial, sans-serif; } .section { min-height: 80vh; display: flex; align-items: center; justify-content: center; } .card { width: 300px; padding: 24px; background: #fff; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); opacity: 0; transform: translateY(40px); transition: opacity 0.6s ease, transform 0.6s ease; } .card.visible { opacity: 1; transform: translateY(0); } .card h2 { font-size: 20px; color: #303133; margin-bottom: 8px; } .card p { font-size: 14px; line-height: 1.6; color: #909399; } @media (prefers-reduced-motion: reduce) { .card { transition: none; opacity: 1; transform: none; } } </style> </head> <body> <div class="section"> <div class="card"> <h2>第一屏内容</h2> <p>往下滚动,可以看到后面的卡片依次淡入。</p> </div> </div> <div class="section"> <div class="card"> <h2>第二屏内容</h2> <p>这个卡片会在进入视口时触发入场动画。</p> </div> </div> <div class="section"> <div class="card"> <h2>第三屏内容</h2> <p>整个过程完全由原生 API 实现。</p> </div> </div> <script> const cards = document.querySelectorAll('.card'); if ('IntersectionObserver' in window) { const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add('visible'); observer.unobserve(entry.target); } }); }, { threshold: 0.2 } ); cards.forEach((card) => observer.observe(card)); } else { cards.forEach((card) => card.classList.add('visible')); } </script> </body> </html>

实现思路拆解:

  1. 卡片默认状态是opacity: 0; transform: translateY(40px),不可见且向下偏移。
  2. IntersectionObserver检测到卡片进入视口且可见比例达到 20% 时,给卡片加上visible类。
  3. .card.visible把透明度改为 1,位移改为 0,触发 transition 平滑入场。
  4. 动画触发后调用observer.unobserve(entry.target),避免重复触发。
  5. 对于不支持IntersectionObserver的旧浏览器,做降级处理:所有卡片直接可见。

这是一个非常标准的“滚动加载动效”方案,不依赖 jQuery,不依赖滚动事件的频繁计算,性能表现也很稳定。

5. 动画性能:为什么有的动画会卡顿

5.1 浏览器的渲染流程

做动画不能只看效果,还要关注性能。浏览器从代码到屏幕显示,大致经历五个阶段:

  1. JavaScript 执行:处理脚本、事件、动画逻辑。
  2. Style 计算:根据 CSS 规则计算每个元素的最终样式。
  3. Layout 布局:计算元素的位置和尺寸。
  4. Paint 绘制:把元素绘制成像素层。
  5. Composite 合成:把各层合成后输出到屏幕。

如果动画修改了widthheightlefttopmargin等布局属性,浏览器必须重新执行 Layout、Paint、Composite,开销很大。频繁触发就会导致页面掉帧、卡顿。

而修改transformopacity时,浏览器可以直接进入合成阶段,性能要好得多。所以最佳实践是:能平移的地方用translateX/translateY,不要用left/top;能缩放的地方用scale,不要改width/height

5.2 优先使用 transform 和 opacity

来看一个典型对比。假设要让一个元素从左边滑入:

/* 不推荐:修改 left 触发布局 */ .box { position: absolute; left: 0; transition: left 0.3s ease; } .box.active { left: 200px; } /* 推荐:使用 transform,不影响布局 */ .box { position: absolute; transform: translateX(0); transition: transform 0.3s ease; } .box.active { transform: translateX(200px); }

两种写法视觉上可能看不出差别,但渲染性能完全不同。第一种写法如果同一时刻有多个元素在动画,很可能出现卡顿;第二种写法可以充分利用 GPU 合成。

5.3 will-change 与 contain 的正确使用

will-change可以提前告诉浏览器某个属性即将变化,让浏览器预先分配优化资源:

.card { will-change: transform, opacity; }

但需要注意:will-change不能滥用。如果给大量元素设置will-change,浏览器要维护大量合成层,内存占用上升,反而拖慢性能。合理的做法是只在动画即将开始前添加,动画结束后移除。对于实际项目,先不加will-change测一测,确实出现性能问题再针对性地加,这是更稳妥的思路。

另外,为了减少不必要的渲染范围,还可以考虑 CSScontain属性,不过它的使用条件比较严格,在动画场景中一般不作为首选方案,这里不再展开。

6. 常见问题与排查思路

做动画过程中,难免遇到各种奇怪现象。下面把高频问题整理成表格,再对部分问题做详细说明。

问题现象常见原因解决思路
动画没有生效属性名称写错,或元素初始状态与目标状态相同检查 CSS 拼写,对比状态差异
动画播完闪回原样缺少 animation-fill-mode添加forwards或使用both
动画只播放一次未设置 infinite 或设计就是一次根据需求设置animation-iteration-count
hover 时元素抖动位移与缩放叠加导致 hover 边界变化使用稳定的容器包裹,避免 hover 改变布局尺寸
滚动入场动画提前触发threshold 设置太小调大threshold,如 0.3 或 0.5
动画卡顿掉帧动画了布局属性,或同时动画太多元素改用 transform/opacity,减少合成层数量
页面滚动时 fixed 元素动画位移错乱对 fixed 元素使用 transform 触发新的包含块将动画拆到内部子元素,避免直接 transform 父级
用户开启“减弱动态效果”后仍播放未处理 prefers-reduced-motion用媒体查询关闭或简化动画

6.1 为什么动画没有生效

动画不生效是最常见的入门问题,原因通常有三个:

第一,@keyframes的名称与animation-name不一致。CSS 是弱校验语言,名称不匹配不会报错,动画就是静默不执行。

/* 错误:名称不一致 */ .animation-name: demo; @keyframes myDemo { ... }

第二,动画属性本身没有变化空间。比如元素默认opacity: 1,动画也是从opacity: 1opacity: 1,看起来自然没有效果。

第三,元素被display: none隐藏或者被其他元素遮挡。建议先打开 DevTools,确认元素真的在页面上,并且样式计算中确实包含动画相关属性。

6.2 animation-fill-mode 造成的“闪回”

很多人第一次用 animation 时,发现动画结束后元素“跳”回初始位置,很影响体验。原因是没有设置animation-fill-mode

默认情况下,动画结束的一瞬间,元素会恢复成没有应用动画时的样式。要保留动画结束后的状态,可以这样写:

.element { animation: fadeInUp 0.6s ease-out both; }

bothforwardsbackwards的组合:延迟期间应用第一帧,结束时保持最后一帧。对于大多数入场动画,直接写both最省心。

6.3 hover 抖动问题

悬浮卡片加上translateY(-8px)scale(1.02)后,鼠标快速划过边缘时,元素可能会轻微抖动。根因是:卡片向上移动后,鼠标原本的位置不再位于卡片区域,hover 状态被取消,卡片又移回去,反复触发。

解决办法通常有两种:

  • 给卡片包一层固定大小的容器,hover 效果作用在容器上。
  • 在动画元素外围设置 padding 作为缓冲区域,避免鼠标因位移脱离 hover 区域。

从工程角度,第一种更推荐,因为它也顺便让布局更稳定。

7. 最佳实践与工程建议

7.1 用类名切换状态,而不是直接写内联动画

在项目中,建议把动画和业务逻辑解耦。JS 只负责添加或删除表示状态的类名,具体动画由 CSS 控制。这样状态可预测,样式可复用,也方便以后统一调整动画时长。

// 推荐:JS 只改类名 dialog.classList.add('dialog--open'); // 不推荐:JS 直接操作样式 // dialog.style.opacity = '1'; // dialog.style.transform = 'translateY(0)';

7.2 使用 CSS 变量统一动画参数

一个业务系统里,动画时长、缓动曲线应该全局统一,避免每个组件各自写一套值。可以抽成 CSS 变量:

:root { --duration-fast: 0.2s; --duration-normal: 0.3s; --duration-slow: 0.6s; --ease-standard: cubic-bezier(0.25, 0.1, 0.25, 1); --ease-in-out: cubic-bezier(0.42, 0, 0.58, 1); } .card { transition: transform var(--duration-normal) var(--ease-standard); }

这样后续产品要求“动画再快点”,只需要改变量,不用满项目找散落的 transition。

7.3 关注可访问性:prefers-reduced-motion

不少用户会在系统设置里开启“减少动态效果”。作为前端开发者,应当尊重这一偏好。实现方式很简单:

@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } }

这种全局兜底写法适合大多数项目,但要注意!important的权重问题。更好的做法是在具体组件里手动处理关键动画,比如前面案例三那样,在媒体查询里关闭进入动画并让内容直接可见。

7.4 动画性能检查清单

上线前如果页面包含较多动效,建议按下面清单自查:

  1. 是否只动画了 transform 和 opacity?
  2. 是否有无限循环动画停留在页面后台?切到后台标签页时应考虑暂停。
  3. 是否给过多元素加了 will-change?
  4. 移动端是否避免使用较大面积的 box-shadow 动画?
  5. 是否在 DevTools Performance 面板实际录制过,确认没有掉帧?

7.5 命名规范与维护

动画类名建议带语义前缀,便于检索。比如js-anim-表示用 JS 控制的动画类,anim-表示纯 CSS 动画类。关键帧名称也要有意义,不要叫aaatest

/* 示例命名 */ @keyframes fade-in-up { ... } @keyframes spin { ... } @keyframes pulse { ... }

当项目动画数量变多,建议按功能拆分文件,比如animations/loading.cssanimations/card.css,避免一个样式文件膨胀到上千行。

8. 总结与下一步

到这里,“做动画”从概念到实战的基本路径已经完整走了一遍:transition 负责状态过渡,transform 提供空间变换能力,@keyframes 和 animation 承担多阶段动画,IntersectionObserver 帮助我们把动画和滚动位置关联起来,性能优化则确保动画不会拖垮页面体验。

如果接下来想继续深入,可以按下面的顺序学习:

  1. 先用本文的三个案例练手,改颜色、改时长、改缓动,感受不同参数带来的视觉差异。
  2. 学习 Web Animations API,它能用 JavaScript 直接操作关键帧动画,比手写 class 更灵活。
  3. 尝试接入 GSAP 等动画库,了解社区优秀方案如何组织复杂动画。
  4. 如果需要做数据可视化、特殊图形动画,可以学习 Canvas 和 SVG 动画。
  5. 项目规模变大后,重点研究动画与组件生命周期的关系,特别是在弹窗、列表增删、路由切换这些场景中如何优雅处理动画。

动画是前端体验的重要一环,但它服务于内容和交互,不要为了炫技而堆动效。实际开发中,克制、流畅、一致,比花哨更重要。把这套基础打牢,以后无论接触什么动画库,都能快速上手。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询