在理清制作固定背景的反相效果后,我脑海中自然而然地想到了,使用 Sass 让不支持滤镜的浏览器实现这种效果。Sass 本身存在一个 invert
函数,但是只能模拟出 filter: invert(100%)
的效果。我们的目标是适用于任意比例。
下面的代码,是上次用 JavaScript 实现的反相滤镜效果:
var box = document.querySelector('.box'),
styles = window.getComputedStyle(box),
filter = (styles.webkitFilter || styles.filter),
invert_arg = filter.match(/(0\.\d+)|\d+/)[0],
upper = ((filter.indexOf('%') > -1)?(invert_arg/100):invert_arg)*255,
lower = 255 - upper,
original = styles.backgroundColor.split('('),
channels = original[1].match(/(0\.\d+)|\d+/g),
alpha = (channels.length > 3)?(1*channels.splice(3, 1)[0]):1,
inverted_channels = channels.map(function(ch) {
return 255 - Math.round(lower + ch*(upper - lower)/255);
}),
inverted;
if(alpha !== 1) {
inverted_channels.splice(3, 0, alpha);
}
inverted = original[0] + '(' + inverted_channels.join(', ') + ')';
所以第一步是,设置 red
, green
, blue
参数(区间为 0 ~ 255
),以及反相函数的比例参数,该参数值在 0
到 1
之间,或者是百分数 0%
到 100%
之间(允许使用范围之外的值,但表现效果会被限制在规定的区间内)。然后,我们会计算传入的比例参数,得出每个颜色值的限制范围并将其反相。
代码演示如下:
$red: 255;
$green: 165;
$blue: 0;
$percentage: 65%;
$original: rgb($red, $green, $blue);
$upper: ($percentage/100%)*255;
$lower: 255 - $upper;
$inverted-red: 255 - round($lower + $red*($upper - $lower)/255);
$inverted-green: 255 - round($lower + $red*($upper - $lower)/255);
$inverted-blue: 255 - round($lower + $red*($upper - $lower)/255);
$inverted: rgb($inverted-red, $inverted-green, $inverted-blue);
显而易见,上面的代码有重复的方程式,所以可以将其放入一个函数中:
@function invert-channel($channel, $upper, $lower) {
@return 255 - round($lower + $red*($upper - $lower)/255);
}
接着,我们就可以向下面这样传参了:
$inverted: rgb(invert-channel($red, $upper, $lower),
invert-channel(green, $upper, $lower),
invert-channel(blue, $upper, $lower));
但我们真的需要多次调用 invert-channel
吗?当然不是,所以我们继续创建另一个函数,用来处理相关计算并调用 invert-channel
:
@function _invert($red, $green, $blue, $percentage) {
$upper: ($percentage/100%)*255;
$lower: 255 - $upper;
@return rgb(invert-channel($red, $upper, $lower),
invert-channel($green, $upper, $lower),
invert-channel($blue, $upper, $lower));
}
干得不错。但是我们并不总是使用 rgb()
值,有时还会使用颜色关键字或者 hsl()
值。当然我们可以使用一些工具,在使用颜色前转换为恰当的格式。但是否存在一种方式,无论我们传入任何值都可以获得正确格式呢?当然有!Sass 自带的 red
,green
和 blue
方法就是为此而生的。
继续简化 _invert
函数:
@function _invert($original, $percentage) {
$upper: ($percentage/100%)*255;
$lower: 255 - $upper;
@return rgb(invert-channel(red($original), $upper, $lower),
invert-channel(green($original), $upper, $lower),
invert-channel(blue($original), $upper, $lower));
}
整体代码:
$original: orange;
$percentage: 65%;
@function invert-channel($channel, $upper, $lower) {
@return 255 - round($lower + $red*($upper - $lower)/255);
}
@function _invert($original, $percentage) {
$upper: ($percentage/100%)*255;
$lower: 255 - $upper;
@return rgb(invert-channel(red($original), $upper, $lower),
invert-channel(green($original), $upper, $lower),
invert-channel(blue($original), $upper, $lower));
}
$inverted: _invert($original, $percentage);
这看起来好多了!不过,我们可以使用动态调用继续简化 invert-channel
。重写后的 _invert
函数如下所示:
@function _invert($original, $percentage) {
$upper: ($percentage/100%)*255;
$lower: 255 - $upper;
$inverted-channels: ();
@each $channel-name in 'red' 'green' 'blue' {
$channel: call($channel-name, $original);
$inverted-channel: invert-channel($channel, $upper, $lower);
$inverted-channels: append($inverted-channels, $inverted-channel);
}
@return rgb($inverted-channels...);
}
通过消除重复的地方,代码整体非常优雅简洁。最后还有一件事需要处理:原始值为半透明的处理方式。这其实非常简单,我们可以取出使用alpha
函数取出透明通道,最后将其追加到返回值即可。所以,最后的代码就像下面这样:
@function invert-channel($channel, $upper, $lower) {
@return 255 - round($lower + $channel*($upper - $lower)/255);
}
@function _invert($original, $percentage) {
$upper: ($percentage/100%)*255;
$lower: 255 - $upper;
$alpha: alpha($original);
$inverted-channels: ();
@each $channel-name in 'red' 'green' 'blue' {
$channel: call($channel-name, $original);
$inverted-channel: invert-channel($channel, $upper, $lower);
$inverted-channels: append($inverted-channels, $inverted-channel);
}
$inverted-channels: append($inverted-channels, $alpha);
@return rgba($inverted-channels...);
}
$inverted: _invert($original, $percentage);
关于 rgba
函数有趣的一点是,只有透明值小于 1
,才会在最终的 CSS 中生成 rgba()
值。
本文根据@Ana Tudor的《Emulate the Invert Filter Effect with Sass》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://davidwalsh.name/invert-color-sass。
出处:http://www.w3cplus.com/preprocessor/invert-color-sass.html