◆

CSS filter 滤镜属性简介

2023-02-02 01:370

filter

属性定义了元素(通常是<img>)的可视效果(例如:模糊与饱和度)。 注意: 滤镜通常使用百分比 (如:75%), 当然也可以使用小数来表示 (如:0.75)。

虽然 MDN 上的例子基本都是图片,但 filter 对任何元素都有效——文字、边框、背景、伪元素统统会被处理,因为它作用的是元素渲染完成后的位图,不是某个具体的样式属性。这一点是理解 filter 各种行为的关键。

全部滤镜函数

.demo {
  /* 模糊,参数是高斯模糊的标准差,只能用长度单位,不接受百分比 */
  filter: blur(5px);
  /* 亮度,0 全黑,1 原样,可以大于 1 提亮 */
  filter: brightness(0.5);
  /* 对比度,0 全灰,1 原样 */
  filter: contrast(200%);
  /* 阴影,语法同 box-shadow 但不支持 inset 和扩散半径 */
  filter: drop-shadow(4px 4px 8px rgba(0, 0, 0, 0.5));
  /* 灰度,0 原样,1 完全灰白 */
  filter: grayscale(100%);
  /* 色相旋转,参数是角度 */
  filter: hue-rotate(90deg);
  /* 反色 */
  filter: invert(100%);
  /* 透明度,效果类似 opacity */
  filter: opacity(50%);
  /* 饱和度,0 等同灰度,大于 1 更艳 */
  filter: saturate(300%);
  /* 褐色老照片效果 */
  filter: sepia(100%);
  /* 引用 SVG 里定义的 <filter> */
  filter: url(#my-svg-filter);
}

多个函数可以串联,顺序会影响结果,因为它们是流水线式依次作用在上一步的输出上:

/* 先灰度再提亮,和先提亮再灰度结果不同 */
filter: grayscale(1) brightness(1.5);

blur 的两个特殊之处

blur() 的参数不接受百分比,只能是长度值。blur(50%) 是无效声明,整条 filter 会被丢弃。这是它和其他滤镜函数最不一样的地方。

另外模糊会让元素边缘的像素向外扩散,扩散区域采样到的是透明像素,所以直接给一张铺满容器的图加 blur 会看到四边发白/透明。常规做法是把元素放大一点再用 overflow: hidden 裁掉边缘:

.blur-bg {
  overflow: hidden;
}
.blur-bg img {
  /* 放大 1.1 倍,让模糊的边缘落在可视区外 */
  transform: scale(1.1);
  filter: blur(10px);
}

filter 会创建包含块和层叠上下文

这是实际开发里最容易被坑的一点:任何非 none 的 filter 值都会让元素成为固定定位元素的包含块,同时创建一个新的层叠上下文。

具体后果有两个:

  1. 元素内部的 position: fixed 子元素会相对这个元素定位,而不是相对视口——固定定位「失效」了。
  2. 元素内部的 z-index 变成局部的,子元素再怎么调 z-index 都不可能盖到这个元素外面的兄弟节点上。
/* 这个 hover 效果会让内部的 fixed 弹窗定位错乱 */
.card:hover {
  filter: brightness(1.1);
}

我排查过一个「弹窗在某个卡片里就跑位」的问题,根因就是祖先节点上挂了个 filter。同样的规则也适用于 transform、perspective、will-change、contain: paint 等属性,filter 只是其中一个触发条件。

filter 与 backdrop-filter

两者名字像但作用对象完全不同:

  • filter 处理元素自己及其子元素的渲染结果。
  • backdrop-filter 处理元素背后那一层的内容,元素自身不受影响。毛玻璃效果就靠它。
.glass {
  background: rgba(255, 255, 255, 0.2);
  /* 背景内容被模糊,卡片里的文字依然清晰 */
  backdrop-filter: blur(12px) saturate(180%);
}

backdrop-filter 有个前提:元素自身背景必须半透明或透明,否则背后的内容被挡住了,模糊了也看不见。Safari 上还需要 -webkit-backdrop-filter 前缀(较老版本)。

性能注意

filter 会强制元素进入合成层并逐帧重新做像素处理,blur 尤其昂贵——高斯模糊的开销随半径平方增长。做过渡动画时如果卡顿,两个方向:

/* 把 filter 提前提交给合成器 */
.item {
  will-change: filter;
  transition: filter 0.3s;
}

或者干脆换思路,用一个预先模糊好的伪元素做 opacity 过渡,把逐帧模糊换成一次性模糊。opacity 和 transform 的动画不需要重绘,比动画化 filter 便宜得多。

兼容性

所有滤镜函数在现代浏览器里都无需前缀。需要注意的只有两点:老版 Safari 需要 -webkit-filter(Safari 9 之前);backdrop-filter 在 Firefox 上到 103 版才默认开启。IE 完全不支持标准 filter(IE 那个 filter: progid:... 是另一套私有语法,和这里讲的没关系)。

💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVECSS filter 滤镜属性简介加载中