frosted glass 毛玻璃效果

🤔问题

当背景图太过花哨,使得内容层的文字难以阅读时,应该怎么办呢?

不做处理,文字看不清

我的想法是常规地添加个透明背景:

黑底白字 白底黑子
给文字添加透明度为0.3的白色背景色 给文字添加透明度为0.8的白色背景色

说实话,加了背景色之后虽然能看清文字了,可大大一块颜色不怎么好看呀。有什么办法能让文字和背景图更好地融合呢?毛玻璃可能是个不错的选择。

什么是毛玻璃效果?看看QQ音乐歌词的界面就知道了。歌词能很清楚地被看到,因为文本覆盖的图片区域被大大地模糊了,人在阅读时就会更专注于清晰可辨的文字。

QQ音乐歌词界面截图

💡思路

模糊效果可以用 filter过滤器的 blur 方法来实现。如果你脑海中还不能马上浮现 filter 的知识图谱,不妨阅读一下MDN文档或者我整理的这篇CSS Filter

如果直接对元素使用blur,文字也会变得模糊:

1
filter:blur(2px);

添加blur后模糊了的文字

怎么才能让元素覆盖区域背景模糊,而又不影响文字的现实呢?可以在背景图元素和内容元素之间加一个中间层。它需要满足几个要求:

  • 中间层渲染的图像能与背景元素图像无缝衔接
  • 中间层与内容元素盒子等大
  • 由中间层实现模糊效果,内容元素叠加其上

中间层应该是内容层的子元素盒子,能直接获取父元素大小。又因为是纯样式效果,比起增加一个div子元素,使用伪元素更利于维护DOM结构。

🎉解决方案

解决问题的关键时如何让伪元素与背景元素拥有无缝衔接的背景图。

background-attachment:fixed 的作用是让背景图相对视窗固定。当背景元素和伪元素都设置相同背景属性,并且背景相对视窗固定时,就达到视觉上的无缝衔接效果:

1
2
3
4
5
background:url(https://cdn.pixabay.com/photo/2016/02/19/10/51/stairs-1209439__340.jpg) no-repeat;
background-size:50% auto;
background-position:50% 0%;
background-attachment:fixed;
border:solid 1px black;

伪元素继承父元素的大小(背景色是为了方便调试)

1
2
3
4
5
6
7
.content:after{
content:'';
position:absolute;
top:0;right:0;bottom:0;left:0;
filter:blur(20px);
background:blue;
}

效果是这样的:

添加伪元素

伪元素挡住了内容元素!这是理所当然的,因为伪元素某种意义上来说,相当于内容元素的子元素,且它绝对定位覆盖了父元素。所以需要手动调整伪元素的层叠顺序:

1
2
3
4
.content:after{
...
index:-1;
}

但是没能解决问题:

调整伪元素层叠顺序为-1

伪元素的层叠顺序不仅排在了内容元素后面,也排在了背景元素后面,为什么呢?

因为背景元素、内容元素、和伪元素都处于同一层叠上下文中,z-index为负值时,它的层叠顺序会在普通元素盒子之后。解决办法是让内容元素拥有层叠上下文,这样伪元素的层叠顺序就与外界无关了。

创建层叠上下文的方式之一,是设置z-index为非 auto值:

1
2
3
4
.content{
...
z-index:0;
}

有进展了!伪元素渲染在背景元素之前。接下来的问题:一是伪元素的模糊效果出圈了。二是边缘处模糊效果减弱,透出了背景图:

设置父子层叠上下文

前者简单,一句 overflow:hidden 就能解决。后者是因为模糊效果的削减幅度是模糊半径的长度,只要伪元素再向外扩展至少20px即可:

1
2
3
4
5
6
7
8
9
.content{
...
overflow:hidden;
}

.content:before{
...
margin:-50px;//保险起见,设置一个比20px更大的值
}

去掉蓝色背景,最终的渲染效果:

设置父子层叠上下文

🚧注意

上述解决方案有个明显的确定,因为background-attachment:fixed 是相对视窗固定,当出现滚动条时,背景会随着视窗固定,而内容会随着滚动条滚动:

背景和内容滚动不一致

要想内容和背景滚动一致,就需要计算调整伪元素背景的大小和位置,使得其显示部分能够尽量和被背景元素的图像衔接上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.background{
background-image: url(https://cdn.pixabay.com/photo/2016/02/19/10/51/stairs-1209439__340.jpg);
background-size:100%;
background-position:0 0;
width:50%;
...
}

.content{
width:80%;
padding:1em;
margin:2em auto;
box-sizing:border-box;
...
}
.content2:before{
background-size:125%;
background-position:50% -2em;
...
}

三个元素联系紧密,不管是初始配置还是后续修改,都需要三者兼顾来实现效果。可维护性不太友好,但是背景和内容滚动同步。

🌵最后

  • 这是 本例代码,以供参考
  • 本问题摘自《CSS揭秘》–Lea Verou的第十八章,致谢
  • 📖 查看更多过滤器标准内容
0%