🤔问题
当背景图太过花哨,使得内容层的文字难以阅读时,应该怎么办呢?
我的想法是常规地添加个透明背景:
黑底白字 | 白底黑子 |
---|---|
说实话,加了背景色之后虽然能看清文字了,可大大一块颜色不怎么好看呀。有什么办法能让文字和背景图更好地融合呢?毛玻璃可能是个不错的选择。
什么是毛玻璃效果?看看QQ音乐歌词的界面就知道了。歌词能很清楚地被看到,因为文本覆盖的图片区域被大大地模糊了,人在阅读时就会更专注于清晰可辨的文字。
💡思路
模糊效果可以用 filter
过滤器的 blur
方法来实现。如果你脑海中还不能马上浮现 filter
的知识图谱,不妨阅读一下MDN文档或者我整理的这篇CSS Filter 。
如果直接对元素使用blur
,文字也会变得模糊:
1 | filter:blur(2px); |
怎么才能让元素覆盖区域背景模糊,而又不影响文字的现实呢?可以在背景图元素和内容元素之间加一个中间层。它需要满足几个要求:
- 中间层渲染的图像能与背景元素图像无缝衔接
- 中间层与内容元素盒子等大
- 由中间层实现模糊效果,内容元素叠加其上
中间层应该是内容层的子元素盒子,能直接获取父元素大小。又因为是纯样式效果,比起增加一个div
子元素,使用伪元素
更利于维护DOM结构。
🎉解决方案
解决问题的关键时如何让伪元素与背景元素拥有无缝衔接的背景图。
background-attachment:fixed
的作用是让背景图相对视窗固定。当背景元素和伪元素都设置相同背景属性,并且背景相对视窗固定时,就达到视觉上的无缝衔接效果:
1 | background:url(https://cdn.pixabay.com/photo/2016/02/19/10/51/stairs-1209439__340.jpg) no-repeat; |
伪元素继承父元素的大小(背景色是为了方便调试)
1 | .content:after{ |
效果是这样的:
伪元素挡住了内容元素!这是理所当然的,因为伪元素某种意义上来说,相当于内容元素的子元素,且它绝对定位覆盖了父元素。所以需要手动调整伪元素的层叠顺序:
1 | .content:after{ |
但是没能解决问题:
伪元素的层叠顺序不仅排在了内容元素后面,也排在了背景元素后面,为什么呢?
因为背景元素、内容元素、和伪元素都处于同一层叠上下文中,z-index
为负值时,它的层叠顺序会在普通元素盒子之后。解决办法是让内容元素拥有层叠上下文,这样伪元素的层叠顺序就与外界无关了。
创建层叠上下文的方式之一,是设置z-index
为非 auto
值:
1 | .content{ |
有进展了!伪元素渲染在背景元素之前。接下来的问题:一是伪元素的模糊效果出圈了。二是边缘处模糊效果减弱,透出了背景图:
前者简单,一句 overflow:hidden
就能解决。后者是因为模糊效果的削减幅度是模糊半径的长度,只要伪元素再向外扩展至少20px
即可:
1 | .content{ |
去掉蓝色背景,最终的渲染效果:
🚧注意
上述解决方案有个明显的确定,因为background-attachment:fixed
是相对视窗固定,当出现滚动条时,背景会随着视窗固定,而内容会随着滚动条滚动:
要想内容和背景滚动一致,就需要计算调整伪元素背景的大小和位置,使得其显示部分能够尽量和被背景元素的图像衔接上:
1 | .background{ |
三个元素联系紧密,不管是初始配置还是后续修改,都需要三者兼顾来实现效果。可维护性不太友好,但是背景和内容滚动同步。