蒙版、形状与剪切

本章概要

  • 利用滤镜(filters)来控制元素及其背景的外观
  • 蒙版图片在遮盖元素局部区域中的用法
  • 使用剪切路径(clip paths)重塑元素
  • 设置元素左右浮动的方法
  • 让文字沿图形边缘对齐

上一章介绍了一些有助于增强页面视觉趣味性、激发创意灵感的实用技巧。本章将继续探讨这个话题,首先利用 CSS 提供的多种内置函数(例如模糊设置、颜色去饱和等)来演示几个视觉效果的实现过程;然后介绍蒙版(masks)和剪切路径(clip paths)的用法,并利用它们能够选择性隐藏元素局部区域的强大功能,创建几个有趣的形状;最后是浮动和属性 shape-outside 相关的知识,了解它们在文字环绕中的具体应用,看看文字是怎样紧密围绕某个形状来排列的。

鉴于这些页面特效仅适用于某些特定场景,若要让它们在一个页面内同时生效可能会有些凌乱,因此本章将通过彼此独立的小型案例来逐一演示说明。

14.1 滤镜 Filters

CSS 中的 filter 滤镜属性可以实现元素的模糊设置、色彩偏移或者去饱和等页面特效,其中好几种特效都是现成的;但为了演示 filter 的工作原理,本节先从元素的模糊设置开始介绍。

通过属性 filter 设置的滤镜将对整个元素生效,包括元素内的文字、边框及背景样式等。图 14.1 为某内容板块(类似第 12 章中演示的版本)在设置模糊滤镜前后的效果对比图。其中左侧未设置滤镜,而右侧声明了 filter: blur(3px),即对该板块设置了 3px 的模糊滤镜。

【图 14.1 滤镜将对包括其边框、子元素以及背景样式在内的整个元素生效】

尽管图 14.1 形象地展示了滤镜的效果,但由于它让文字难以阅读,显然并不能算作滤镜的一个特别理想的应用场景。为此,我将为您演示滤镜在图片中的实际应用。

先新建一个 HTML 文档与样式表,用于存放本章将要介绍的各种示例代码。然后将代码清单 14.1 中的 HTML 标记添加到示例页面。与前面的章节一样,关联的小鸟图片可从示例代码库中获取,详见:https://github.com/cssindepth/css-in-depth-2。

代码清单 14.1 带小鸟图片的页面 HTML 标记

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <img src="images/bird.jpg"
      alt="Blue and orange bird perched on a branch"
      width="568" height="379">
  </body>
</html>

应用模糊滤镜后,图片效果将如图 14.2 所示。

【图 14.2 模糊滤镜生效后的图片效果】

函数 blur() 接受一个参数,即用于描述模糊程度的一个长度值。它表示屏幕上会有多少个像素混合到一起(除像素单位 px 外,em、rem 及其他长度单位也是有效的)。若长度值为 0px,则表示没有模糊;只有当参数值大于 0 时才会看到模糊特效,且值越大,模糊效果越显著。

请根据代码清单 14.2 所示的 filter 声明同步更新本地样式表,为图片添加 5px 的模糊特效。

img {
  filter: blur(5px);
}

利用浏览器开发者工具 DevTools 对 5px 的像素值做上下调整,看看模糊设置的强弱对图片效果的影响究竟如何。当参数值较小时,图片内容尚且可以读懂;但参数值越高,图片就越模糊,并逐渐变为一团模糊的色斑(a vague smudge of color)。这对于只需给出图片的大致印象而不必过分关注细节的场合也许会比较有用,稍后我会举例说明。在此之前,先来看看 CSS 都提供了哪些现成的滤镜功能。

14.1.1 滤镜的类型 Types of filters
目前可用的 CSS 滤镜功能共有 10 种,它们中的大多数都是以某种方式操纵颜色:要么通过调整对比度或色彩饱和度,要么通过添加阴影或变更元素的不透明度。

所有现成的滤镜功能描述如表 14.1 所示。与 blur() 类似,表中的滤镜函数都会通过传入的参数调整其特定行为,其中大部分的值均为百分数;当然也支持百分比形式(如 50%)或者小数形式(0.5)。表中所有的百分比参数其实都是可选项,如果不设置该参数,则默认为 100%。建议对照下表在您的示例页上试验每一个滤镜函数,看看都有哪些具体的特效。

表 14.1 滤镜函数用法一览表

函数示例 描述 参数说明
blur(10px) 应用高斯模糊特效 长度值越大,模糊效果越显著。
brightness(150%) 增加或减少亮度 参数值低于 100% 降低亮度;高于 100% 则增加亮度。
contrast(150%) 增加或减少对比度 参数值低于 100% 则降低对比度(使图像变淡);高于 100% 则增加对比度。
drop-shadow(10px 10px 15px black) 新增一个阴影特效,用法与 drop-shadow 属性类似 前两个参数分别表示 x 和 y 偏移量;第三个为模糊程度(可选);第四个为颜色值(可选)。与 drop-shadow 属性不同,该函数不支持扩展半径值和关键字 inset
grayscale(50%) 降低色彩饱和度 参数值介于 0% 到 100% 之间,可生成一个全灰度图像。
hue-rotate(30deg) 偏移每个像素的色调值 参数可以是色轮上代表颜色偏移量的任意角度值。
invert(100%) 用于反转颜色 参数值为 100% 时颜色完全反转;介于 0% 到 100% 之间则以指定强度设置反转效果。
opacity(50%) 设置元素透明效果,用法与 opacity 属性类似 参数值为 0% 时元素完全透明;值为 100% 时则完全不透明。
saturate(150%) 增加或减少色彩饱和度 参数值高于 100% 则增加图片色彩饱和度;低于 100% 则降低饱和度。saturate(25%) 等效于 grayscale(75%)
sepia(100%) 将当前色彩替换为深褐色色调效果 参数值介于 0%100% 之间,用于调节深褐色色调的强度。
此外,还可以同时设置多个滤镜,滤镜函数间用空格进行分隔。例如:filter: blur(5px) sepia(20%),此时各滤镜将按从左至右的顺序依次生效。

当需要针对性地设置一些细节特效时,filter 属性尤为适用。比如说,当鼠标悬停在某个图片上时,想要对图库中的其他所有图片设置一个略带模糊的、或者适当降低色彩饱和度的页面特效,则可以通过代码清单 14.3 给出的样式代码,在 :hover 状态下添加相关滤镜来实现。至于该样式对应的 HTML 页面,如果您愿意尝试的话,我就把它留作练手项目交给您来完成了。

代码清单 14.3:对图库中的图片设置些许滤镜效果

.gallery:hover img {
  filter: blur(2px) grayscale(50%); /* 鼠标悬停到图库上时,给其中的图片添加滤镜特效 */
}

.gallery img:hover {
  filter: none; /* 移除当前悬停图片上的滤镜效果 */
}

.gallery img {
  transition: filter 1s; /* 在 1 秒内逐步开启或关闭滤镜特效 */
}

根据上述代码,只要鼠标悬停到图库上方,滤镜特效就会对图库中的所有图片生效,但当前悬停的那张图片除外。这些滤镜只是出于演示目的才这样设定,在实际的生产应用环境下,可能会做进一步微调:模糊特效可能仅为 1px、灰度滤镜的值也会更低,不一而足。

提示

上述特效还有另一个比较巧妙的实现方案:利用 :has() 选择器在鼠标悬停到某张图片时,选中该图库内不在悬停状态下的所有图片,比如写作:.gallery z:has(img:hover) img:not(:hover)。但是 Firefox 浏览器直到 2023 年底才添加对 :has() 特性的支持,在没有考虑浏览器最新的相关兼容性的前提下,我可能不会采用这种写法。

本例中还用到了一个 transition 属性,以实现滤镜特效的延时开启或关闭,否则特效的切换会在一瞬间触发。因为我发现如果不设置过渡,突如其来的样式变化会令我分心,无法专注于当前图片——这正是我竭力避免的情况。过渡相关的知识我们还没学,下一章会重点介绍。

14.1.2 背景滤镜 Backdrop filter

偶尔也需要在某些内容后设置滤镜,而不是直接将其作用于内容本身。要实现这样的效果,可以使用另一个属性:backdrop-filter。该属性的值可以是前面介绍过的所有滤镜函数。

当需要将文字放到某个背景图片前方时,背景滤镜(backdrop filter) 会非常有用。选用恰当的滤镜可以在增强文字可读性的同时,又不致于完全遮挡住后面的图片内容。背景滤镜的一个典型应用如图 14.3 所示。

【图 14.3 背景滤镜可用于遮挡半透明元素后面的内容】

在本例中,背景图片位于某元素内。该元素又包含一个添加了模糊特效 blur() 的背景滤镜、且背景色为半透明白色的 div 元素。与前面讲的 filter 属性不同的是,本例中的模糊特效并没有对文字内容生效,即便 div 容器带有边框,该特效也不会对容器边框生效;相反,它只会影响到容器背后的内容,让图片可以透过 div 元素渲染出来。

该页面的 HTML 标记如代码清单 14.4 所示。请根据提供的示例代码同步更新本地示例页面。

代码清单 14.4:背景滤镜效果的示例页 HTML 标记

<div class="box">
  <div class="box__content">
    <h1>Common Kingfisher</h1>
    <p>
      The Common Kingfisher is a bird native found in Europe, Asia, and
      north Africa. It has bright blue plumage with an orange belly.
    </p>
  </div>
</div>

样式方面,背景图片将设置在 box 元素上,而背景滤镜样式 backdrop-filter 则在容器 box__content 上声明。此外还需要一些内边距、外边距来排列元素。具体样式代码详见代码清单 14.5。请将它们同步更新到本地样式表中。

代码清单 14.5 背景滤镜示例样式代码

.box {
  padding: 30px;
  background-image: url(images/bird.jpg);
  background-size: cover;
  background-position: center 30%;
}
.box__content {
  max-inline-size: 500px;
  margin-inline: auto;
  padding: 50px 30px 70px;
  border-radius: 10px;
  background-color: oklch(100% none none / 0.3); /* 即半透明的白色 */
  -webkit-backdrop-filter: blur(10px); /* 供应商前缀(用于 Safari 浏览器) */
  backdrop-filter: blur(10px); /* 启用背景滤镜的模糊特效 */
}

注意,上述代码声明了两次背景滤镜:第一次添加了 -webkit 浏览器前缀(vendor prefix),接着不带该前缀又声明了一次。鉴于 Safari 浏览器只支持 backdrop-filter 带前缀的写法,这里也只能包含两种写法。

关于浏览器前缀 Vendor prefixes

浏览器前缀是浏览器过去在实现新的或实验性的 CSS 特性时采用的一种技术。现在浏览器已经不再通过该技术来添加新的功能特性了,取而代之的是启用 Web 实验性平台特性标记(即 Experimental Web Platform Features flags,具体操作详见第 1 章内容)。该标记可以有效防止开发者在开发过程中依赖一些或将引入重大变更的、尚不稳定的新特性。

浏览器前缀现在很少使用了。不过一些浏览器尚未提供个别样式属性非前缀版本的支持,比如本章涉及的这几个属性:backdrop-filter 和某些 mask-* 开头的属性。它们在 Safari 或 Chrome 浏览器中解析时都需要添加 -webkit 前缀。您可能还会遇到其他类似的浏览器前缀,例如 -moz(用于 Firefox 浏览器)、-o(用于旧版的 Opera 浏览器)以及 -ms(用于 Internet Explorer 以及旧版的 Edge 浏览器)等。

如果确实需要引入浏览器前缀的写法,可以考虑通过一些自动化工具来实现,例如 Autoprefixer,可从 https://autoprefixer.github.io/ 获取。此外,CSS 预处理器 Lightning CSS(详见:https://lightningcss.dev/)也提供了类似功能。我现在都不怎么用这些工具了,毕竟当前需要添加浏览器前缀屈指可数。但如果必须考虑前缀、却又不想死记硬背这些属性对应的特定浏览器前缀,使用这些工具就会大有帮助。

译注

不得不说,像 Safari 浏览器这样在示例代码中被作者点名批评的情况还是比较罕见的。带着几分幸灾乐祸到 Can I Use 网站一查,才发现这个当时饱受作者诟病的不兼容问题,在本书出版不到三个月的时间内就收到了来自 Safari 官方立行立改的深刻 “检讨”(还得是这种方式整改起来最为彻底,哈哈)。如下图所示,backdrop-filter 特性功能已于今年九月在 Safari 的 v18.0 版本中正式移除了浏览器前缀写法,目前已得到各主流浏览器厂商的全面支持:

【补图:目前各大浏览器厂商已经对 backdrop-filter 背景滤镜提供了全面支持(截至 2024 年 12 月 18 日)】

背景滤镜在小型界面元素的开发中可能会非常有用,例如通知类消息组件或者应用级控制按钮等。

以上示例用的都是静态定位(static positioning),此时父元素的背景内容可以透过滤镜渲染出来。同样的背景特效也可以应用在设置了固定定位或绝对定位、彼此间相互堆叠的页面元素场景中。