想象你开着一辆车:车从静止提速到 60 码,不会瞬间弹射出去,而是有一段平滑的加速过程——你踩下油门,车速无缝攀升,这就是 transition(过渡)。如果你打开定速巡航,车子会沿着预设路线自动跑完全程,中间经过一个个导航打卡点,还能一圈圈循环——这就是 animation(动画)。你踩油门的轻重缓急,决定了乘客是舒舒服服还是晕头转向——这是 缓动函数(easing)。有的路是市区小道,红绿灯多、走走停停;有的路是高速,一路畅通——这对应浏览器里的渲染流水线与 硬件加速。开车前你提前告诉导航“我要走这条路”,导航才能提前规划——这就是 will-change。 这篇文章会用“开车”这条主线,把 CSS 动画与过渡的每个环节讲明白:什么时候该用 transition、什么时候该用 animation,缓动曲线怎么选,为什么有些动画丝滑、有些却卡成幻灯片,以及怎样在 16.7 毫秒的帧预算内把动效做到又快又稳。读完你会发现,页面动效和开车一样,本质是“怎么踩油门、走哪条路、提前做多少准备”的问题。
对 AI 从业者的价值:无论你在做 AI 对话应用、模型训练看板还是 Agent 交互界面,掌握这套动画机制都能用最少成本交付专业交互体验,让 AI 产品真正“活”起来。
目录
可视化演示
对比transition状态触发与animation关键帧动画,展示缓动函数曲线及合成器线程性能优化策略。
本节配有交互动效(进入视口后自动加载,约 3KB)。
过渡transition:踩油门的平滑加速
你开车出小区门口,轻踩油门,车速不会“0 瞬间跳到 60”,而是平滑攀升,乘客也不会因为突然推背而难受。这种“你给我一个目标,我平滑地走过去”的能力,就是 CSS 的 transition(过渡)。它的性格特点很鲜明:绝不主动出发,必须有外部指令——鼠标悬停(hover)、点击,或者用 JS 给元素换了一个 class,属性值从旧值变为新值的那一刻,过渡才开始工作。
用代码来理解,当你给元素设置 transition,浏览器就像给这个元素装了一套“油门系统”。它并不关心你最终开到哪里,只负责在“当前值”和“新值”之间按你设定的节奏补出中间帧。你只需要写清楚四件事:哪个属性要动(property)、动多久(duration)、用什么节奏(timing-function)、延迟多久开始(delay)。
transition 是一个简写属性,等价于把下面四项写在一行里。这里有个容易踩的小坑:简写中只能出现一个 duration 和一个 delay,浏览器识别规则是“第一个时间值是 duration,第二个才是 delay”。属性值可以写 all 表示所有可过渡属性都生效,但出于性能考虑,更推荐精确列出属性名。
回到开车场景,transition 就像你请了一位“脚替”:你只下达“加速到 60 码”的指令,剩下的加速节奏他全包了。它最适合的场合正是状态切换类交互:按钮 hover 变色、抽屉展开收起、卡片 hover 上浮。一次指令,一次平滑变化,干净利落。
transition: opacity 0.3s ease-out 0.1s。- property(变化的属性)告诉浏览器“我要让哪个属性平滑变化”,比如 opacity 或 transform。只列真正会变的属性,别图省事写 all,避免多余开销。
- duration(持续时间)整个过渡从开始到结束的时长,单位是秒(s)或毫秒(ms),例如 0.3s。时间越短,变化越急促。
- timing-function(节奏曲线)决定过渡过程中快慢如何变化,默认是 ease。可以写缓动关键字,也可以自定义贝塞尔曲线,后面会展开讲。
- delay(延迟开始)属性值改变后,等多久才开始过渡。注意简写里第一个时间是 duration,第二个才是 delay,顺序写反动画就废了。
动画animation:自动驾驶巡演
transition 需要一个“踩油门的人”,那 animation 呢?它更像打开了定速巡航:你设定好完整路线,车子自己从起点出发,经过每一个打卡点驶向终点——甚至能一圈圈循环下去,完全不需要二次干预。这也是它与 transition 最本质的区别:一个被动、一个主动。
animation 的核心是 @keyframes,它定义一份“路书”:在动画时间线的 0%、50%、100% 这些关键位置,元素应该长什么样。比如 0% 时在左边、透明度为 0,100% 时移到右边、透明度为 1。然后通过 animation 这个简写属性,把路书“接”到元素上,并设定各种播放参数。
相比 transition,animation 多了几个控制旋钮:iteration-count 控制播放次数(infinite 表示无限循环)、direction 控制方向(normal 正放、reverse 倒放、alternate 正反交替)、fill-mode 决定动画开始前和结束后元素停在哪个状态。这让它可以编排“先左移、再上跳、最后淡出”这样的多阶段表演,表现力远超单一过渡。
回到开车:transition 是“点对点”的接驳车,animation 是“多景点巡游大巴”。巡游的优势是表现力强,但代价是你得先把路书写清楚。它最适合加载等待动画、轮播图、骨架屏扫光这类“固定剧本”的场景——剧本不变,循环播放,用户一眼就知道“程序在正常工作”。
- name(关键帧名)与 @keyframes 定义的名字一一对应,浏览器凭这个名字找到“路书”。名字写错,动画直接不播放。
- duration(时长)完整播放一遍路书需要的时间。比如 2s 表示从 0% 到 100% 走一遍花两秒。
- timing-function(节奏)控制关键帧之间的插值节奏,可以整体设置,也可以在某个关键帧内部单独覆盖,形成变速效果。
- delay(延迟)等多久才开始播放第一帧。注意它和 duration 一样遵循“先时长后延迟”的规则。
- iteration-count(播放次数)路书循环几遍,写 infinite 就是无限巡游。配合 direction 可以做出往返、闪烁等效果。
- direction(播放方向)normal 从头到尾、reverse 从尾到头、alternate 一正一反交替。比如加载转圈就用 infinite + normal。
缓动函数:油门的艺术
同样是加速到 60 码,老司机轻踩油门缓缓提速、快到路口提前松油;新手一脚地板油,乘客前仰后合。缓动函数(timing-function)就是用来定义这条“油门曲线”的。它决定了动画在每一瞬间的推进速度,是动效手感的灵魂。
CSS 内置了几个常见节奏:linear 是全程匀速巡航,机械感强;ease 是“起步快、收尾慢”的默认手感,最百搭;ease-in 是缓缓加速,起步肉;ease-out 是快速起步、优雅减速,像电梯到站;ease-in-out 是两头缓、中间快,最接近人眼的舒适区。UI 交互里,ease-out 和 ease-in-out 出镜率最高。
内置节奏不够用时,可以用 cubic-bezier(x1, y1, x2, y2) 自定义三次贝塞尔曲线。这条曲线有四个点:P0 是起点 (0,0),P3 是终点 (1,1),P1 和 P2 是两个控制点。你只需拖动两个控制点,就能捏出“橡皮筋回弹”“重物下坠”等各种手感。浏览器开发者工具里自带可视化编辑器,拖一拖就能看到曲线变化。
缓动函数决定“推背感”。UI 里最舒服的是 ease-out 或 ease-in-out——它们模拟了物理世界的惯性:物体不会瞬间停下,也不会瞬间启动。线性匀速只适合进度条这类“机器感”场景。记住一句话:好的过渡让用户“无感”,奇怪的缓动让用户“晕车”。
- 参数 t(时间进度)t 从 0 取到 1,代表动画时间从开始走到结束。它是这条曲线唯一的自变量。
- 端点 P0 与 P3P0 是曲线起点,固定为 (0,0);P3 是终点,固定为 (1,1)。缓动曲线永远从“开始”走向“完成”。
- 控制点 P1 与 P2P1、P2 是可以拖动的两个“锚点”,它们的坐标 (x1,y1) 和 (x2,y2) 就是写在 cubic-bezier(x1,y1,x2,y2) 里的四个数字。
- 加权合成四项分别乘以伯恩斯坦基函数 (1-t)³、3(1-t)²t、3(1-t)t²、t³ 再相加。每个 t 时刻的曲线位置,是这四个点的加权平均。
- 控制点位置决定手感控制点 y 值偏大,曲线前段陡峭,动画“起步猛”;控制点 x 值偏大,变化被推迟,动画“后程发力”。
硬件加速:从市区走上高速
同样一段路,开市区和开高速,体感完全不同。浏览器渲染页面也分“路况”:它有一条渲染流水线,依次是布局(Layout)、绘制(Paint)、合成(Composite)。不同属性做动画,走的路线不同,成本天差地别——这就是“硬件加速”背后的原理。
当你动画 width、height、left、top 这类属性,浏览器必须重新计算元素在页面中的几何位置(Layout),然后重新画一遍像素(Paint),最后才合成输出。每一步都在 CPU 主线程上排队,就像在市区每个路口等红绿灯,走走停停。而 transform(位移/缩放/旋转)和 opacity(透明度)这两个属性很特殊:浏览器可以直接把它们交给 GPU 合成器处理,跳过布局和绘制,这就是俗称的“硬件加速”。
CSS 动画性能优化的第一原则由此而来:能用 transform/opacity 表达的动画,绝不用 width/height/left/top。位移用 transform: translate,缩放用 transform: scale,旋转用 transform: rotate,淡入淡出用 opacity。让动画走上高速路,把市区小路留给静态内容。
下次看到“为什么我的动画卡成幻灯片”的求助帖,十有八九是动画里写了 width 或 left。记住这句话:transform 和 opacity 是动画界的“高速路专属入口”,走对了路,流畅度立刻上一个台阶。
- Layout(布局/重排)浏览器重新计算每个元素的位置和尺寸。动画涉及 width、height、margin 等几何属性时,整棵布局树都要重算,成本最高。
- Paint(绘制/重绘)把元素的颜色、边框、阴影等画成像素。动画 color、background 等属性会触发这一步,成本中等,且高频重绘同样卡顿。
- Composite(合成)把已经画好的多个图层像叠照片一样合并输出。transform/opacity 动画只走这一步,由 GPU 完成,成本最低、最流畅。
性能优化:守住帧预算
汽车的转速表不能一直飙红,浏览器的“红线”是 60fps:主流屏幕每秒最多刷新 60 帧。换算下来,每一帧只有约 16.7 毫秒,要在这段时间内完成 JS 脚本、样式计算、布局、绘制、合成所有工作,超时画面就掉帧、卡顿。
动画卡顿的本质,是主线程被压垮。主线程就像司机的大脑,一次只能专注一件事:长时间运行的 JS 任务、强制同步布局(比如在 JS 里读了 offsetHeight 又马上改样式)、大量元素同时做动画,都会把 16.7ms 的预算瞬间吃光。尤其是滚动监听里同步改样式这种写法,一帧里反复触发布局,浏览器直接“手忙脚乱”。
优化策略其实就三招:第一,只动画化合成层友好的属性(transform/opacity);第二,把高频操作去抖或交给 requestAnimationFrame,避免一帧内重复计算;第三,减少同时动画的元素数量,能用一层解决的别铺满全屏图层。打开浏览器开发者工具的性能面板,可以直观看到每帧的时间花在哪里。
- 1000ms(一秒的毫秒数)这是时间的基本换算,1 秒等于 1000 毫秒,是计算帧预算的分子。
- 60fps(屏幕刷新率)主流屏幕每秒刷新 60 次画面,每次刷新就是一帧。超过 60 帧人眼难以感知,低于 60 帧就能察觉卡顿。
- 相除得到帧预算把 1000ms 平均分给 60 帧,每帧只有约 16.7ms 完成全部渲染工作,这是性能优化的硬指标。
- 超支的后果任何一帧超过预算,浏览器就赶不上屏幕刷新,表现为跳帧、卡顿,也就是用户口中的“PPT 动画”。
will-change:提前报备
长途出行前,你会提前告诉导航目的地,导航才能规划路线、避开拥堵。will-change 就是给浏览器的“出行预报”:你提前告诉它“这个元素接下来要用 transform 做动画”,浏览器就会先一步把这个元素提升到独立合成层,动画开始时直接走高速,不用临时开道。
用法很直白:在 animation 或高频 transition 开始之前,给元素加上 will-change: transform 或 will-change: opacity;动画结束(比如 transitionend 事件触发)后,再把这个声明移除。意思是“我已经到站了,可以解除预订,把 GPU 内存还给别人”。
最大的坑是滥用。只要写了 will-change,浏览器就会为该元素单独开辟一个图层,而图层是要吃 GPU 内存的。一个页面几百个元素全都 will-change,等于给每辆车都预留一条专属车道——高速反而被堵死,滚动都变卡,移动端甚至可能崩溃。所以它只用在少数真正要动的元素上,且用完即删。
will-change 是“提前报备”,不是“多多益善”。就像导航报备只对真正要出发的路线有意义,对停在车库的车报备路线毫无价值,还浪费资源。正确姿势是:少数关键元素、动画前设置、动画后清理。
- will-change 声明(提前报备)在动画开始前,用 will-change 声明即将变化的属性,相当于提前告诉导航“我要走这条路”。
- 合成层提升(预留车道)浏览器收到报备后,把该元素单独提到一个合成层,与普通页面内容分离,方便 GPU 单独处理。
- 跳过布局与绘制(全程高速)动画期间该图层由 GPU 直接合成,不再触发主线程的 layout 和 paint,所以更流畅,但内存占用也更高。
实战组合:出行动效决策
现在你已经会开车、认路、选路、看仪表盘、提前报备了,最后来做一次完整的出行决策。一个动效需求摆在面前,先问三个问题:它是“状态切换”还是“固定表演”?它动了哪些属性?它持续多久、会不会和其他动画打架?
决策表大致是这样:hover、点击、面板开关这类“一次状态改变”,用 transition + ease-out,300ms 左右是黄金手感;加载动画、轮播、骨架屏这类“固定剧本多阶段表演”,用 animation + 关键帧;位移、缩放、透明一律走 transform/opacity;确实需要 will-change 的元素,动画前加上、动画后移除。
组合使用时要注意细节:transition 写在默认状态,hover 状态只改属性值;animation 定义在元素本身;两者可以共存,但别对同一个属性同时生效,否则就像两个司机抢方向盘,动画表现会变得不可预测。上线前打开开发者工具的“渲染”面板,开启 FPS 监测,确认动画期间稳定在 60fps 再交付。
动效设计没有银弹,它是一连串“开车决策”:该点对点就用 transition,该巡游就用 animation,油门要柔、路要走高速、预算要盯表、报备要克制。把这套决策练成肌肉记忆,你就能像老司机一样,让每一帧都又快又稳。
- transform/opacity(高速属性)优先用这两个合成友好属性表达位移、缩放、淡入淡出,把动画成本压到最低。
- 合适缓动(油门节奏)默认交互选 ease-out 或 ease-in-out,避免机械感强的 linear,让动效符合物理直觉。
- 16.7ms 预算(不超时)控制动画元素数量与 JS 工作量,保证每帧在预算内完成,这是流畅的底线。
- 克制 will-change(不滥升层)只为真正长动画的元素提前报备,动画结束即解除,避免 GPU 内存被无谓占用。
AI 应用场景
AI 对话应用的流式输出动效
阶段:前端交互体验优化
解决的问题:大模型流式返回 token 时,如果直接替换文本,容器高度频繁变化会造成布局抖动和文字闪烁。传统 innerHTML 直更不仅卡顿,还让用户难以追踪阅读位置。
具体案例:一个 AI 客服助手逐字输出回答,每追加一段文字,先用 opacity: 0 插入新块,再通过 transition 过渡到 opacity: 1,同时用 transform: translateY 让新块从下方 8px 处平滑滑入。输出过程中容器高度变化也由 transition 平滑过渡,用户全程无感。
模型训练看板的指标刷新动效
阶段:数据可视化渲染
解决的问题:训练指标(loss/accuracy)每几秒刷新一次,直接更新数字会闪跳,用户难以感知“有新数据来了”,频繁跳动还会分散注意力。
具体案例:大模型训练监控面板中,loss 曲线每 5 秒追加一个点。新点用 animation 关键帧做一次 scale 脉冲(0.8 → 1.2 → 1.0)提示“这是新数据”,曲线尾段用 transition 平滑延伸,指标数字用 opacity 过渡淡入。训练过程一目了然,且全程 60fps。
模型加载时的骨架屏等待动画
阶段:应用启动与加载反馈
解决的问题:本地模型推理耗时不可控,页面长时间空白会让人误以为程序崩溃。静态占位图又显得死板,无法传递“正在准备”的进度感。
具体案例:一个部署在网页端的生成式 AI 工具,在解析模型阶段显示灰白条纹扫动的骨架屏(animation 关键帧实现 shimmer 效果),模型就绪后用 transition 以 300ms 淡入 + 上移的方式切换到真实内容,等待体验从焦虑变成期待。
Python 演示
"""模拟 CSS 动画时间轴:对比不同缓动函数的速度特征""" import math def linear(p): return p def ease_in_out(p): if p < 0.5: return 4 * p * p * p return 1 - math.pow(-2 * p + 2, 3) / 2 def ease_out_bounce(p): n1 = 7.5625 d1 = 2.75 if p < 1 / d1: return n1 * p * p if p < 2 / d1: p -= 1.5 / d1 return n1 * p * p + 0.75 if p < 2.5 / d1: p -= 2.25 / d1 return n1 * p * p + 0.9375 p -= 2.625 / d1 return n1 * p * p + 0.984375 def build_timeline(duration_ms, fps, easing): frames = max(2, int(duration_ms / 1000 * fps)) start, end = 0.0, 200.0 timeline = [] for i in range(frames): t = i / (frames - 1) pos = start + (end - start) * easing(t) time_ms = int(round(i * 1000 / fps)) timeline.append((time_ms, round(pos, 2))) return timeline def report(name, timeline): speeds = [] for i in range(1, len(timeline)): dt_ms = timeline[i][0] - timeline[i - 1][0] ds_px = abs(timeline[i][1] - timeline[i - 1][1]) if dt_ms > 0: speeds.append(ds_px / (dt_ms / 1000.0)) avg = sum(speeds) / len(speeds) peak = max(speeds) harsh = sum(1 for s in speeds if s > 800) / len(speeds) * 100 print("{}: 平均速度 {:6.1f}px/s | 峰值 {:6.1f}px/s | 过猛帧占比 {:3.0f}%".format( name, avg, peak, harsh)) if __name__ == "__main__": print("300ms 把元素从 0px 推到 200px 的驾驶风格对比:") for fn in (linear, ease_in_out, ease_out_bounce): report(fn.__name__, build_timeline(300, 60, fn))
def ease_in_out(p):用三次多项式实现“慢-快-慢”曲线,对应 CSS 的 ease-in-out 缓动节奏。frames = max(2, int(duration_ms / 1000 * fps))由时长和帧率算出总帧数,相当于浏览器把 300ms 切成 18 帧的时间片。pos = start + (end - start) * easing(t)每帧位置 = 起点 + 总距离 × 缓动函数输出,这就是 CSS 动画求值的本质。time_ms = int(round(i * 1000 / fps))把帧序号换算成毫秒时间戳,模拟浏览器动画时间线。speeds.append(ds_px / (dt_ms / 1000.0))相邻两帧位移除以时间差得到瞬时速度,用来量化“推背感”。harsh = sum(1 for s in speeds if s > 800) / len(speeds) * 100统计超过 800px/s 的帧占比,超过这个速度人眼会感觉“太猛”。
常见误区
误区我明明写了 `transition: all 0.5s`,为什么页面一加载元素没有自己动?是不是我写错了?初学者常以为 transition 和 animation 一样会自动播放。
正解transition 本身不知道“目标状态”是什么,它只是一套“油门系统”,必须有人踩油门才会工作。这个“人”就是状态改变:hover、active、JS 修改 class 或内联样式。没有触发,元素就永远静止。就像车子不会因为你装了油门踏板就自己跑,得有人踩下去。
为何常见:因为 animation 设置后会主动播放,初学者很容易把两者的触发机制混淆,这也是 transition 和 animation 最核心的区别。
误区既然 will-change 能提升动画性能,那我把页面上所有要动的元素都加上 `will-change: transform`,这样不就全都流畅了吗?
正解will-change 会让每个元素单独占据一个合成层,而合成层是要吃 GPU 内存的。大量元素同时提升图层,反而增加合成开销、拖慢滚动,移动端甚至可能因此崩溃。正确做法是:只对真正要动画的元素、在动画开始前添加,动画结束后立刻移除。
为何常见:人们容易把“能提升性能”理解成“多多益善”,忽略了任何优化手段都有资源成本,就像给每辆车都预留专属车道反而会堵死高速。
误区反正都是 CSS 动画,浏览器都会自动处理,动画 width、height、left、top 和动画 transform 应该一样流畅吧?
正解完全不一样。width/height/left/top 变化会触发布局重排(Layout)和重绘(Paint),每一步都要在 CPU 主线程重新计算几何并绘制像素;而 transform/opacity 走合成层,由 GPU 直接处理,不需要布局。前者像在市区每个路口等红灯,后者像上了高速一路畅通。
为何常见:从用户视角看,两者都只是“位置或大小变了”,看不到浏览器内部渲染流水线的巨大差异,所以容易低估属性选择的代价。
误区学会了 animation 就觉得它是 transition 的升级版,于是所有动画需求一律用 @keyframes 实现,认为这样才“专业”。
正解两者适用场景完全不同。简单状态切换(hover 变色、面板开合、class 切换)用 transition 更简洁自然,而且不需要预先知道目标值——它天然适配“从当前状态到新状态”。用 animation 实现状态切换,得为每个状态定义关键帧,代码又长又难维护。复杂多阶段表演(加载动画、轮播)才轮到 animation 出场。
为何常见:学会新工具后容易陷入“一招鲜”心态,忽略了工程上“合适才是最好”的原则,就像有了巡航定速就不用踩刹车一样荒谬。
面试题
Q1. 想让按钮在鼠标悬停时背景色平滑变化,最合适用以下哪个?
- A.transition(状态触发平滑过渡)← 正确答案
- B.animation(关键帧循环播放)
- C.will-change(图层提前提升)
- D.requestAnimationFrame(JS 绘制循环)
答案:A。hover 属于典型的“一次状态改变”,transition 专为此设计:属性值变化时自动补中间帧。animation 适合固定剧本的多阶段表演;will-change 只是性能优化声明;requestAnimationFrame 是 JS 层面的手工方案,杀鸡不用牛刀。
Q2. transition 简写中四个值按什么顺序书写?
- A.属性 时长 节奏 延迟← 正确答案
- B.属性 延迟 节奏 时长
- C.时长 属性 延迟 节奏
- D.节奏 时长 属性 延迟
答案:A。transition 的标准顺序是 property duration timing-function delay。特别注意两个时间值:第一个是 duration,第二个才是 delay,写反了动画时长和延迟就会互换,效果完全不对。
Q3. 关于 transition 与 animation 的区别,正确的是?
- A.transition 需触发且只播一次,animation 可循环← 正确答案
- B.animation 需外部触发,transition 自动播放
- C.transition 可自定义循环次数,animation 不可
- D.两者都必须定义 @keyframes 关键帧才能工作
答案:A。transition 是“被动”的,必须由状态改变触发,且默认只执行一次;animation 是“主动”的,设置后自动播放,可通过 iteration-count 控制循环。只有 animation 需要 @keyframes。
Q4. @keyframes 中每个关键帧主要定义什么?
- A.该时刻元素应处的样式状态← 正确答案
- B.动画播放的次数和方向
- C.动画开始前的延迟时间
- D.缓动函数的贝塞尔参数
答案:A。@keyframes 的作用是描述时间线上各节点的样式快照,比如 0% 时 opacity 为 0、100% 时为 1。播放次数、延迟、方向都属于 animation 简写属性的配置,贝塞尔参数属于 timing-function,都不写在关键帧里。
Q5. 下列哪一个属性做动画时可获得硬件加速?
- A.transform(合成层直接处理)← 正确答案
- B.width(触发重新布局)
- C.margin-left(触发几何计算)
- D.background-color(触发重绘)
答案:A。transform 和 opacity 是浏览器中唯二可以跳过布局与绘制、直接由 GPU 合成输出的属性,也就是“高速路”。width、margin-left 会触发 Layout,background-color 会触发 Paint,都在 CPU 主线程上执行,成本更高。
Q6. 关于动画性能优化,正确的做法有哪些?
- A.优先动画化 transform 与 opacity 属性← 正确答案
- B.尽量让动画避免触发 layout 重排← 正确答案
- C.对大量元素同时做 width 动画
- D.动画结束后移除多余的 will-change 声明← 正确答案
答案:ABD。A 和 B 都是把动画引向“合成层高速路”的正确策略;D 及时清理 will-change 避免 GPU 内存泄漏。C 是典型反面教材:大量元素同时动画 width 会反复触发布局重排,主线程直接超预算。
Q7. 关于 will-change 的说法正确的是?
- A.提前告知浏览器要变化的属性← 正确答案
- B.在页面中使用越多动画越流畅
- C.动画结束后应及时解除声明← 正确答案
- D.只在动画进行中才临时生效
答案:AC。will-change 的作用是提前声明属性变化意图,让浏览器预先分配合成层,A 正确;动画结束移除是标准实践,C 正确。B 错在滥用会耗尽 GPU 内存;D 错在 will-change 必须在动画开始前设置,而不是动画中。
Q8. 帧率为 60fps 时,浏览器每一帧的渲染预算约为多少?
- A.约 16.7 毫秒← 正确答案
- B.约 60 毫秒
- C.约 100 毫秒
- D.约 1 毫秒
答案:A。1 秒等于 1000 毫秒,除以 60 帧得到约 16.7ms。这是浏览器完成 JS、样式、布局、绘制、合成的总预算,超过就会掉帧。60ms 是 16fps 的节奏,100ms 是 10fps,肉眼已明显卡顿。
Q9. 动画播放结束后想保持最后一帧的样式,应使用哪个属性?
- A.animation-fill-mode: forwards← 正确答案
- B.animation-direction: reverse
- C.animation-play-state: paused
- D.animation-timing-function: ease-out
答案:A。fill-mode 控制动画结束后的填充状态,forwards 表示保留最后一帧样式;backwards 是在动画开始前应用第一帧。direction 控制播放方向,play-state 控制暂停,timing-function 控制节奏,都与“结束后保留样式”无关。
Q10. 以下哪些情况会导致 CSS 动画出现卡顿掉帧?
- A.动画触发频繁的 layout 重排← 正确答案
- B.主线程被长时 JS 任务阻塞← 正确答案
- C.使用 transform 实现位移动画
- D.页面同时存在大量合成层动画元素← 正确答案
答案:ABD。A 会反复走最贵的 Layout 环节;B 会挤占 16.7ms 帧预算;D 会让 GPU 内存与合成开销双双飙升。C 恰恰是性能优化推荐做法,transform 走高速合成层,不会导致卡顿,反而应该多用。