CSS秘密花园:磨砂玻璃效果
《 CSS Secrets 》是 @Lea Verou 最新著作,这本书讲解了有关于CSS中一些小秘密。是一本CSSer值得一读的一本书,经过一段时间的阅读,我、@南北和@彦子一起将在W3cplus发布一系列相关的读后感,与大家一起分享。
问题
半透明颜色的一种应用是,使用它们作为背景。在照片或者颜色比较复杂的背景下,通过降低对比度,提高文本的可读性。结果非常有效,但是阅读起来仍然不是那么好,特别是非常低透明度的颜色或非常杂乱的背景的情况下。比如说,看看下图效果:
其主要元素有一个半透明的白色背景,HTML如下:
<main> <blockquote> “The only way to get rid of a temptation[…]” <footer>— <cite> Oscar Wilde, The Picture of Dorian Gray </cite> </footer> </blockquote> </main>
CSS代码如下(不相关的内容就不再赘述):
body { background: url("tiger.jpg") 0 / cover fixed; } main { background: hsla(0,0%,100%,.3); }
效果如上图所示。
正如你看到的,文本内容确实很难阅读,因为文本背后的图像非常杂乱,而且背景颜色也只是 25% 的不透明值。当然,我们可以通过增加背景颜色的 alpha 参数的值来提高可读性,但是效果可能就不是那么有趣了。
增加背景颜色的 alpha 值确实解决了可读性的问题,但是也让我们的设计变得一点都不好玩了。
在传统的印刷设计中,这个问题通常是通过模糊照片上容纳文本内容的那块区域的背景来解决的。模糊后的背景就不会那么杂乱了,所以,上边的文本可读性也提高了。因为模糊的计算成本是昂贵的,过去在网页和UI设计中使用这种技术,资源的损耗是让人望而却步的。但是,随着GPU的改进和硬件加速变得越来越司空见惯,现在对于模糊效果的使用已经是非常普遍了。过去几年里,在新版本的Microsoft Windows和Apple iOS 以及 Mac OS X中都可以看到这种技术。
带模糊背景的半透明UI在过去几年中变得非常普遍,因为模糊对资源的消耗已经变得不再昂贵(Apple iOS 8.1见图左,Apple OS X Yosemite见图右)。
在CSS中我们可以通过 blur() 滤镜做出模糊元素的效果,这本质上是SVG模糊滤镜原语的一个对应的硬件加速版本。但是,如果我们直接给我们的示例应用一个 blur() 滤镜,整个元素都会被模糊,这样可读性就更差了。
为元素应用 blur() 滤镜,结果更糟了
有没有什么办法,能让它只应用在元素的backdrop上(也就是我们元素后面的背景的一部分)?
解决方案
如果是我们的元素有一个 background-attachment 属性,值为 fixed ,可能就ok了,只是有点棘手。因为我们不能直接模糊我们的元素,我们可以将它应用到一个定位于元素背后的伪元素上,并让伪元素的背景和 <body> 上的背景无缝匹配。
首先,我们添加一个伪元素,并设置绝对定位,所有的偏移量都为 0 ,这样它就完美覆盖整个 <main> 元素:
main { position: relative; /* [Rest of styling] */ } main::before { content: ''; position: absolute; top: 0; right: 0; bottom: 0; left: 0; background: rgba(255,0,0,.5); /* for debugging */ }
背景也可以是non-fixed的,只是可能会导致混乱。
我们还应用了一个半透明的 red 背景,这样我们可以看明白我们正在弄哪块内容,否则在处理透明(也就是,invisible)元素的时候调试会比较困难。如图所示:
伪元素模糊覆盖在文本的上方
我们的伪元素就在我们的内容的上方,然后将其模糊。我们可以通过添加 z-index: -1; 来把它置于元素下方。
在用负的 z-index 值来将子元素移动到它的父元素下面时要小心:如果父元素是作为背景嵌套在其它元素中,子元素也会移到其它元素下方。
利用 z-index: -1; 将伪元素移动到父元素下方
现在是时候把半透明的红色背景去掉了,用一个和我们背景相符的图片,可以复制 <body> 的背景,或者直接拆分伪元素的规则。现在我们可以模糊了吗?试一下:
body, main::before { background: url("tiger.jpg") 0 / cover fixed; } main { position: relative; background: hsla(0,0%,100%,.3); } main::before { content: ''; position: absolute; top: 0; right: 0; bottom: 0; left: 0; filter: blur(20px); }
为什么不直接在 main::before 中使用 background: inherit ?因为这样的话它会继承 main 的背景,而不是 body 的背景,这样伪元素得到的就只是一个半透明的白色背景。
图注:伪元素的模糊是可行的,但是在边缘处模糊较少,所以磨砂玻璃的效果也受了一点影响
如上图所示,我们差不多完成了。中间的模糊效果看起来很完美,但是在接近边缘的地方模糊比较少。这是因为模糊半径(blur radius)会减少覆盖有纯色模糊的面积。为我们的伪元素应用一个红色背景,有助于我们理解这是怎么回事。
图注:添加一个红色背景可以帮助我们理解
为了规避这个问题,我们让伪元素至少比它的容器的尺寸大 20px (和模糊半径的值相等),通过应用一个 -20px 或更小的 margin 值来让它保持在一个安全的区域内,因为不同的浏览器可能会使用不同的模糊算法。如图所示:
这修复了边缘处褪色模糊的问题,但是现在在我们的容器外边也有一些模糊,这使得它看起来像污迹而不像磨砂。幸好,这个问题也容易解决:我们只要应用为 main 应用 overflow: hidden; ,把多余的模糊剪掉即可。最后的代码如下所示:
body, main::before { background: url("tiger.jpg") 0 / cover fixed; } main { position: relative; background: hsla(0,0%,100%,.3); overflow: hidden; } main::before { content: ''; position: absolute; top: 0; right: 0; bottom: 0; left: 0; filter: blur(20px); margin: -30px; }
效果如下所示:
修复边缘处淡去的模糊,但是我们的元素外边仍然存在模糊
现在我们页面的可读性已经变得比刚开始的时候好多了,外观也非常优雅。这种效果的降级是否优雅仍值得商榷。如果不支持滤镜,我们看到的就是本节开头的那个效果。我们可以通过调整背景颜色的透明度来让降级效果的可读性更好。
</div>来自: http://www.w3cplus.com/css3/css-secrets/frosted-glass-effect.html