前天晚上接到 boss 通知,要把官网页面置灰。这还不简单吗?
<body style="filter: grayscale(1);">分分钟搞定。无非再多考虑一下浏览器兼容问题。然而第二天,很多客户反馈,希望他们的网站、小程序、app 等也都能实现置灰效果,那么问题就来了,微信小程序页面中在
filter影响下出现了异常。
以上是背景,终于周末了,问题也暂时解决了,分享给你~
问题是什么?
有了之前的成熟经验,所以我们尝试在小程序的 <page> 标签上设置全局样式:
page {
filter: grayscale(1);
}
本以为这就搞定了,但是问题来了:页面中进行固定定位的元素消失了! 👇
设置
page{ filter: none --}
设置
page{ filter: grayscale(1) --}
没错,是灰了,但是诸如自定义底部导航、吸顶导航等页面元素都跟你说拜拜了。它们去了哪里呢?你看👇
没错,对于一个 fixed bottom0 的元素,它跑到了当前页面的最底下去了!发生了什么呢?
原因是什么?
抱着问题找答案,让我们看看 MDN 中关于 position: fixed 和 filter: xxx 的解释。
-
grayscale();
函数将改变输入图像灰度。amount 的值定义了转换的比例。值为 100% 则完全转为灰度图像,值为 0% 图像无变化。值在 0% 到 100% 之间,则是效果的线性乘数。若未设置值,默认是 0
-
position: fixed;
元素会被移出正常文档流,并不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transform, perspective 或 filter 属性非 none 时,容器由视口改为该祖先。
虽然知道了原因,但在小程序中,Android 和 iOS 系统下的表现并不一致,也不知道是谁的锅:
好在应用场景更全面的 web 页面,其表现是符合 css 标准描述的,好在为<html>、<body>等根元素设置 grayscale() 不需要考虑这一特性。
怎么做?
但说归说,吐槽归吐槽,需求还是要实现。
如果时间充裕,或许可以定义一套专门的实现灰色表现的样式表,但奈何时间紧任务急,时间成本巨大的笨办法只能抛弃。
通过上一节中引用的 grayscale() 函数释义,我们大概可以知道,它的作用其实就是为元素下的子元素加上灰度滤镜,过滤到彩色,只留下灰度值。所以不妨探寻一下,css 中是否还有其他方式可以实现只保留元素灰度值呢?借用一句广告词:有问题,就会有答案...
答案是什么呢?它就是 mix-blend-mode 属性。
来看一下它的值有哪些:
normal -- 正常
multiply -- 正片叠底
screen -- 滤色
overlay -- 叠加
darken -- 变暗
lighten --变亮
color-dodge -- 颜色减淡
color-burn -- 颜色加深
hard-light -- 强光
soft-light -- 柔光
difference -- 差值
exclusion -- 排除
hue --色相
saturation -- 饱和度
color -- 颜色
luminosity -- 明度
这不由得让我想到了 Photoshop。那么,哪些值可以起到保留灰度的效果呢?它们就是hub|saturation|color 三兄弟。
不妨先来看看 Photoshop 中的表现:
那么,放到前端工作中要怎么做呢?
需要注意的是,我们需要给 ::before 将要参与 mix 的底部元素一个不透明的背景色,否则,::before 的背景色是什么颜色,整个页面的背景色也就随它了。像这样:
最后,但是
令人沮丧的是,ios 系统下没有得到我们想要的结果,不论是浏览器环境,还是小程序:
所以最终只能通过判断系统环境,filter 结合 mix-blend-mode 勉强实现了需求。
最后,如果你有什么好的办法,可否告知一二?
END