必须统一使用grayscale(0)/grayscale(1)起止值、显式声明transition和filter初始状态、添加will-change:filter,否则因浏览器无法插值不同函数类型或缺少重绘触发导致过渡失效。

直接用 filter: grayscale() 配合 transition: filter 就能实现,但必须写对初始态、过渡声明和 hover 目标值,否则动画不触发或闪一下就结束。
为什么 hover 时图片没过渡效果?
最常见原因是没在非交互状态下声明 transition,或者用了 filter: none 这种非法起始值。
-
filter: none不是可动画的合法值,浏览器会跳过过渡,必须改用grayscale(1)(全灰)或grayscale(0)(全彩) -
transition必须写在默认状态规则里,比如img { filter: grayscale(1); transition: filter .4s ease; },不能只写在:hover里 - 如果图片是
background-image,filter完全无效——必须作用在<img>标签或包含内容的容器上 - Safari ≤15.6 需要前缀:
-webkit-filter,真机测试不能跳过
如何写一个稳定可用的 hover 切换?
核心是「默认灰度 + hover 清除灰度」,不是反着来。这样兼容性更好,也避免 Safari 的插值 bug。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 显式设置宽高或父容器最小尺寸,防止
:hover区域塌陷失效 - 用
cubic-bezier(0.25, 0.46, 0.45, 0.94)替代ease,过渡更自然 - 多个滤镜要写在一条
filter声明里,比如filter: grayscale(1) brightness(0.9),不要拆成两条 - 避免用
!important强制覆盖,容易和框架样式冲突
img {
filter: grayscale(1);
transition: filter .4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
img:hover {
filter: grayscale(0);
}
SVG 图片怎么处理?
外部引用的 SVG(<img src="logo.svg">)能正常响应 filter;但 inline SVG 内部的 <image></image> 或 <use></use> 元素不支持,因为它们被隔离在 SVG 渲染上下文中。
- 对整个 inline SVG 加灰度:给外层
<svg></svg>标签加filter - 需要精确控制内部位图时,得用
<fecolormatrix></fecolormatrix>定义滤镜,再通过 CSS 的filter: url(#id)切换,且必须配合transition - Chrome 115+ 支持
<image></image>上直接过渡,Firefox 仍不支持——生产环境建议降级为 JS 控制 class 切换
JS 控制切换时要注意什么?
用 element.style.filter 设置时,格式必须严格,单位和小数点都不能错。
- 数值必须带小数点,比如
grayscale(0.7)✅,grayscale(70%)✅,但grayscale(70)❌(会被当作 0) -
+=会覆盖已有滤镜,比如原先是brightness(1.2),再+= 'grayscale(1)'就只剩灰度了 - 想叠加多个效果,必须写全:
element.style.filter = 'brightness(1.1) contrast(1.05) grayscale(0.3)' - 频繁读取当前值(如
offsetHeight)会强制重排,应改用getComputedStyle(img).filter
真正难的是混合了 will-change、GPU 加速和高 DPR 屏幕的场景:安卓 WebView 中可能闪帧,加 transform: translateZ(0) 能缓解,但又可能引发文字锯齿——这些没法靠一套规则覆盖,得实机测。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










