前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库CSS filter grayscale 过渡动画 性能
CSS动画与视觉

用 transition 做 grayscale() 的去色动画,性能上要注意什么?

写法很简单:给元素加 filter: grayscale(1) 再配一条 transition: filter 0.3s,hover 时把 grayscale 改成 0 就能平滑去色。性能上要注意的是 filter 变化每帧都需要重新计算像素,图片尺寸大或者同时过渡的元素多时会掉帧。可以用 will-change: filter 提前把元素提到独立合成层来缓解。

前端进阶之旅 · 一题精讲更新于 2026.09.29
CSS#动画与视觉#性能优化#动画
先看核心答案读代码示例
理解线索

`grayscale` 过渡的三个层面

  1. 写法transition: filter 配合 hover 态切换 grayscale 值
  2. 渲染成本filter 变化触发 repaint,每帧重新计算像素颜色
  3. 优化手段will-change 提升合成层,缩小过渡元素的尺寸和数量

filter 不会触发 layout,只触发 repaint 和 composite,性能比改 width 好,但比改 opacity 差。

核心回答

先记住这个答案

给图片做灰度过渡的写法本身很简单,一行 transition: filter 0.3s 就够了。性能上要关注的是 filter 在过渡期间的渲染成本:浏览器每帧都要对这个元素的像素做一次 grayscale 计算,这是一次 repaint 操作。图片越大、同时过渡的元素越多,主线程的绘制压力越大。如果在列表页一次性对几十张卡片做去色动画,掉帧的概率很高。

  • filter 可用 transition 过渡
  • 灰度过渡需反复处理像素,可能掉帧
  • will-change 可能优化合成,但需实测
  • 大图或大量元素同时过渡时考虑限制作用范围

最基本的去色过渡写法

给一张图片加灰度效果,然后 hover 时还原成彩色,只需要两个状态的切换。filter 是可动画属性,浏览器能自动在两个值之间做插值。

hover 切换灰度CSS
.card-img {
  filter: grayscale(1);          /* 默认灰色 */
  transition: filter 0.4s ease;  /* 过渡 filter 属性 */
}

.card-img:hover {
  filter: grayscale(0);          /* hover 时还原彩色 */
}

/* 反过来也常见:默认彩色,hover 变灰 */
.card-img-alt {
  filter: grayscale(0);
  transition: filter 0.4s ease;
}
.card-img-alt:hover {
  filter: grayscale(0.8);        /* 不一定要到 1,0.8 更柔和 */
}

grayscale() 的参数是 0 到 1 之间的数值,0 表示原色,1 表示完全灰度。过渡期间浏览器会在两个值之间线性插值,视觉上就是颜色逐渐变灰或者逐渐还原。

为什么大图或多图过渡会卡

filter 变化不会触发 layout(不影响元素尺寸和位置),但会触发 repaint。repaint 阶段浏览器要对元素的每一个像素重新做颜色计算。一张 1920x1080 的图片就是两百万个像素,每帧都要处理一遍。

这和 opacity 的区别在于,opacity 只是调整整个图层的透明度,可以完全在合成阶段完成,不需要重新计算像素。而 grayscale 需要读取每个像素的 RGB 值再算出灰度值,这一步跳不过。

默认情况下,元素和它周围的其他元素共享同一个绘制层。filter 变化会让整个绘制层都标记为需要重绘。如果页面上有 20 张卡片图在同一个层里,哪怕只有一张在做过渡,整个层都可能被重绘。

用 will-change 限制重绘范围CSS
.card-img {
  filter: grayscale(1);
  transition: filter 0.4s ease;
  will-change: filter;  /* 提示浏览器提前创建独立合成层 */
}

.card-img:hover {
  filter: grayscale(0);
}

/* 动画结束后最好移除 will-change,避免长期占用内存 */

加了 will-change: filter 之后,浏览器会为这个元素单独创建一个合成层。过渡期间的重绘只影响这个层,不会波及旁边的元素。代价是多了一个层,会额外占用 GPU 内存。如果页面上几十个元素都加了 will-change,层太多反而会拖慢合成阶段。

实际项目里的优化思路

在列表页做去色效果时,一般不需要所有图片同时过渡。只对 hover 的那一张做动画就够了,其余图片保持静态灰度不动。这样同一时刻只有一个元素在做 filter 过渡,性能压力可以忽略不计。

如果设计要求页面加载时所有图片从灰到彩逐个亮起来,可以用 animation-delay 错开,让同一帧里最多只有两三张图在过渡。也可以考虑降低图片分辨率——显示尺寸 200x200 的缩略图没必要用 2000x2000 的原图,像素少了计算量成比例下降。

还有一个常见场景是整个页面变灰(比如哀悼日),这时 filter 加在 <html> 上,只需要算一次,不存在过渡期间掉帧的问题,因为根本不做动画。

回答前,多想一步

容易答错的地方

把 filter 过渡和 transform 过渡的性能画等号
transform 过渡可以完全在合成层完成,不触发 layout 也不触发 repaint。filter 过渡虽然不触发 layout,但每帧都要 repaint。两者的性能差一个量级,不能混为一谈。
以为 will-change 没有代价
will-change 会让浏览器提前为元素创建独立合成层,每个层都占用一份 GPU 纹理内存。滥用 will-change 会导致层爆炸,合成阶段反而变慢。只在真正需要过渡的元素上加,过渡结束后用 JavaScript 移除是比较稳妥的做法。
试着用自己的话回答

面试官还会怎么问?

除了 grayscale,filter 的其他函数过渡性能一样吗?

基本一样。blur()、brightness()、contrast()、saturate() 都是逐像素计算,过渡期间都要 repaint。其中 blur() 最重,因为模糊需要读取周围像素做卷积运算,半径越大越慢。

能不能用 backdrop-filter 代替 filter 来减少重绘?

不能。backdrop-filter 是对元素背后的内容做滤镜,计算量只多不少——它需要先把背后的像素合成出来,再做滤镜处理。而且 backdrop-filter 也会创建层叠上下文,副作用和 filter 一样。

怎么用 DevTools 验证 filter 过渡的性能?

打开 Chrome DevTools 的 Rendering 面板,勾选 Paint Flashing。做过渡时如果元素高亮为绿色,说明在重绘。再到 Layers 面板看有没有独立层。最后到 Performance 面板录一段,看 Paint 时间占每帧的比例。

从一道题,走向一组知识

把知识连起来

动画与视觉

filter 不是继承属性,为什么子元素也跟着模糊了?

filter 加在父元素上会影响所有子元素的视觉效果,理解这点才能判断去色动画的作用范围。

动画与视觉

给父元素加了 filter,子元素的 fixed 定位为什么失效了?

filter 会创建新的层叠上下文,和 will-change 提升合成层是两件事,容易混淆。

参考资料

  • CSS:动画与视觉参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 最基本的去色过渡写法
  3. 为什么大图或多图过渡会卡
  4. 实际项目里的优化思路
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑