⭐ 推荐:社区规则条款 V1.0

CSS 滤镜 filter 妙用:模糊/灰度/色调调整实战

runyu
runyu 正式会员正式会员认证极客认证极客
发布于 2026-10-16 07:49 ·8 浏览 ·2 回复
内容摘要

讲解如何用 CSS filter 一行代码实现模糊、灰度、亮度、饱和度、色相旋转等视觉效果,列出十个常用函数及语法,并演示模糊毛玻璃背景、加载占位、灰度悬停头像、禁用按钮和全站变灰等实战用法,提醒 blur 会造成边缘虚边且部分写法可能拖慢页面。

学完这篇,你能在不打开 Photoshop 的前提下,用 CSS 的 filter 一行代码搞定模糊、黑白、色调偏移这些常见视觉效果,并知道哪些写法会拖慢页面。

第一步:先认清 filter 的语法和 10 个函数

filter 接一串函数,用空格隔开,从左到右依次生效:

.card {
  filter: blur(4px) grayscale(100%) brightness(1.2);
}

常用函数一共 10 个,记住这几个就够日常用了:

函数作用常用值
blur(px)高斯模糊4px
grayscale(%)去色100%
brightness(%)亮度80% / 1.2
contrast(%)对比度120%
saturate(%)饱和度0% ~ 300%
hue-rotate(deg)色相旋转90deg
sepia(%)复古棕80%
invert(%)反色100%
opacity(%)透明度50%
drop-shadow()跟随轮廓的阴影0 4px 8px #0004

百分号可以省略成小数:grayscale(1) 等于 grayscale(100%);brightness(1.2) 等于 brightness(120%)。

第二步:模糊——做毛玻璃背景和加载占位

最典型的用法是给背景图加模糊,文字压在上面:

.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url(cover.jpg) center/cover;
  filter: blur(8px);
  transform: scale(1.05);      /* 关键:放大一点,遮住模糊发虚的边缘 */
  z-index: -1;
}

.hero { position: relative; overflow: hidden; }

加载占位图更简单:先把模糊小图铺上,真图加载完再淡出。

.thumb { filter: blur(20px); transition: filter .4s; }
.thumb.loaded { filter: blur(0); }

注意:blur() 会让元素四周出现一圈半透明虚边,单独用 blur 时记得配合 transform: scale(1.05) 或给父元素 overflow: hidden,否则边缘会“漏白”。

第三步:灰度——黑白头像、禁用态、纪念日全站变灰

悬停才显色的头像,鼠标移上去恢复彩色:

.avatar { filter: grayscale(100%); transition: filter .25s; }
.avatar:hover { filter: grayscale(0); }

按钮禁用态不用改颜色,一层灰度就够:

.btn:disabled { filter: grayscale(100%); opacity: .6; cursor: not-allowed; }

遇到全国哀悼日这类场景,在 html 上加一个类,整站瞬间变灰:

html.mourning { filter: grayscale(100%); }

注意:filter 会作用于元素及其所有子元素。给 html 加灰时,连 position: fixed 的弹窗一起变灰——这通常正是你要的效果;但如果你只想灰某张图,就只加在图片本身,别加在容器上。

第四步:色调调整——hue-rotate 是最省事的一招

同一套图标,靠色相旋转换成不同主题色:

.icon--blue  { filter: hue-rotate(0deg); }
.icon--green { filter: hue-rotate(90deg); }
.icon--purple{ filter: hue-rotate(220deg); }

图片做旧、做暗色调:

.photo-retro { filter: sepia(60%) saturate(120%) contrast(90%); }
.photo-dark  { filter: brightness(70%) contrast(120%) saturate(80%); }

想让 PNG 图标换个纯色(原图是纯黑时最好用),可以配合 invert 再旋转色相:

.icon-white { filter: invert(100%); }

注意:hue-rotate 是整体色相偏移,不是“换成指定颜色”。想把任意颜色精确调成某个品牌色,靠试色相角度会很难受,复杂场景建议直接用 SVG 或两套图。

第五步:组合实战——卡片悬停效果

把上面几个串起来,做一个不依赖图片切换的悬停态:

.card img {
  filter: saturate(70%) brightness(95%);
  transition: filter .3s ease, transform .3s ease;
}
.card:hover img {
  filter: saturate(120%) brightness(105%);
  transform: scale(1.03);
}

如果要做“面板本身透明、背后内容模糊”的毛玻璃,用 backdrop-filter:

.glass {
  background: rgba(255, 255, 255, .18);
  backdrop-filter: blur(12px) saturate(150%);
  -webkit-backdrop-filter: blur(12px) saturate(150%);
  border: 1px solid rgba(255, 255, 255, .3);
}

注意:backdrop-filter 和 filter 是两回事——前者模糊的是元素背后的内容,后者模糊的是元素自己。Safari 必须带 -webkit- 前缀,且元素背景要有一定透明度,否则看不出效果。

第六步:性能和兼容,这两个坑最容易翻车

  1. 别在大面积元素上做动画模糊。 filter 会触发合成层,大范围 + 频繁变化会掉帧。要动就加 will-change: filter;,并尽量缩小作用范围。
  2. 滤镜元素会成为定位参考。 加了 filter 的元素,其内部 position: fixed 的子元素会相对它定位,而不是相对视口——这是最隐蔽的坑,遇到 fixed 弹窗“跑偏”先检查祖先有没有 filter。
  3. 不用写兼容前缀。 现代浏览器对 filter 支持良好,-webkit-filter 早已不需要,只有 backdrop-filter 还建议保留 -webkit-。
  4. 滤镜不影响布局尺寸,blur 溢出的部分是视觉溢出,不会撑开父容器,必要时用 overflow: hidden 裁掉。

小结

  • filter 接收空格分隔的函数链,从左到右依次生效。
  • 模糊配 scale(1.05) 消边缘虚边;灰度做悬停显色、禁用态、全站变灰都好用。
  • hue-rotate 适合整体调色,不适合精确指定颜色。
  • filter 影响自己和子元素,backdrop-filter 只影响背后,后者需要 -webkit- 前缀和半透明背景。
  • 加了 filter 的元素会成为 fixed 子元素的定位参考;大面积动画模糊要注意性能。
本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-846.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 2

zero
zero 见习用户见习用户 1楼 2026-10-16 07:56

滤镜真正的坑不在语法,而在它会悄悄改变两件事:元素的定位包含块和渲染合成方式——很多「莫名其妙布局错位 / 滚动卡顿」都出在这儿。

先说定位。只要元素身上挂了 filter(哪怕值看起来没生效),它就会成为后代 position: fixed 的包含块。所以做全站变灰时写 html { filter: grayscale(1) },页面里所有 position: fixed 的顶栏、悬浮按钮、弹窗会瞬间改成相对 html 定位,滚动时明显「飘不对」。稳妥做法是把滤镜加在页面内容的包裹容器上,或者干脆只作用于 img。

再说性能。filter 不是 transform/opacity 那种可以走合成器的属性,给大图或整页做 transition: filter 时是逐帧重绘,低端机掉帧很明显。小元素悬停变色没问题;大面积动画优先考虑叠加一层同样内容做 opacity 过渡,或至少给元素加 will-change: filter 单独提层(但别滥用,层多了内存吃得凶)。

顺带补两点:想要真正的毛玻璃,用 backdrop-filter: blur(12px) 配半透明背景,filter: blur() 只能模糊自己——而且祖先元素一旦有 filter,后代的 backdrop-filter 会失效,Safari 还要 -webkit- 前缀;阴影方面 drop-shadow() 跟随透明轮廓(PNG 抠图、文字),box-shadow 跟盒子,做不规则图形别选错。

最后一句提醒:filter 会连带子元素里的文字一起模糊变灰,别指望只滤背景图又不影响文案,要单独处理就作用到具体元素上。

陈先生
陈先生 正式会员正式会员认证极客认证极客 #810 2楼 2026-10-16 08:01
zero:滤镜真正的坑不在语法,而在它会悄悄改变两件事:元素的定位包含块和渲染合成方式——很多「莫名其妙布局错位 / 滚动卡顿」都出在这儿。 先说定位。只要元素身上挂了…

这个补充比原文更值钱,尤其「包含块」那条,我按这个思路再补三个同源的坑。

先说层叠上下文。filter 不为 none 时会创建 stacking context,原文 .hero::before 那种 z-index: -1 的写法就会变味——负层级只能在这个上下文内部退到内容下面,退不到祖先背景之后,所以经常出现「模糊层看不见」或「盖住文字」。要么把模糊图直接做成 .hero 自己的 background,要么就用 backdrop-filter 走正路。

包含块这条再展开一点:不只 position: fixed,absolute 后代同样受影响,而且 backdrop-filter 也会创建包含块。所以往 html 或 body 上挂滤镜,基本等于给全站定位元素换了个参照系。全站变灰务实的做法是把内容区包一层加 filter,顶栏、悬浮按钮、弹窗留在容器外;站里有哀悼模式插件的话,建议先确认它是加在哪一层再开,免得后台开关一开、前台 fixed 元素全飘。

性能上补一点:will-change: filter 是长期占层的,动画结束记得撤掉,不然层数一多内存和合成开销都上去。大面积动画我还是推荐双层 opacity 过渡,别指望 transition: filter 在低端机上稳住帧。