文章开始,先来通过GIF动图展示一下最后的开发成果。没有JavaScript处理,没有SVG耍宝。只使用单个
<img>
和一些CSS渲染。当我们将鼠标悬停在图片上,即可看到魔法效果。
是不是很酷?好吧,我不得不承认这不仅是CSS,因为还有SCSS,但是它仍然算是纯CSS效果,因为没有涉及JS。
上述效果包含两个过程。
-
-
蒙版
蒙版有时很难概念化,并且常常与剪切相混淆。最重要的是:蒙版是图像。当将图像用作蒙版应用于元素时,图像的任何透明部分都允许我们直接通过元素看到。任何不透明的部分都会使元素完全可见。
蒙版的工作方式与不透明度(
opacity
)相同,只是它们作用在同一个元素的不同部分上。这与剪切不同,剪切是将路径之外的所有内容都隐藏起来的路径。蒙版的优点在于,在同一个元素上可以具有任意数量的图层蒙版——与在
background-image
上链接多个图像的方式类似。
由于蒙版是图像,因此我们可以使用CSS渐变来制作。下面举一个简单的例子以便于更好地理解这一技巧。
img {
mask:
linear-gradient(rgba(0,0,0,0.8) 0 0) left, /* 1 */
linear-gradient(rgba(0,0,0,0.5) 0 0) right; /* 2 */
mask-size: 50% 100%;
mask-repeat: no-repeat;
}
我们在图像上定义了两个图层蒙版,都是纯色的,但是
alpha
透明度值不同。
值得注意的是,使用何种颜色无关紧要,因为默认的
mask-mode
为
alpha
。唯一有重大关系的就是
alpha
值。渐变可以是
linear-gradient(rgba(X,Y,Z,0.8) 0 0)
,其中
X
,
Y
和
Z
都是随机值。
每个图层蒙版尺寸为
50% 100%
(或图像的一半宽度和全部高度)。一个图层蒙版覆盖左侧,另一个覆盖右侧。然后,我们得到了两个不重叠的蒙版,它们覆盖了图像的整个区域,并且正如我们前面所说,每个蒙版定义的
alpha
透明度值不同。
动画渐变
我们要做的是将动画应用于蒙版的线性渐变
alpha
值,以创建透明动画。稍后,我们再将它们制作成异步动画,以创建碎片效果。
动画渐变是我们在CSS中无法做到的。但是,现在我们有
@property
的支持,尽管支持是有限的。
简而言之,
@property
允许我们创建自定义的CSS属性,其中我们可以通过指定类型来定义语法。下面让我们创建两个属性
--c-0
和
--c-1
,初始值为
1
。
@property --c-0 {
syntax: "<number>";
initial-value: 1;
inherits: false;
}
@property --c-1 {
syntax: "<number>";
initial-value: 1;
inherits: false;
}
这些属性将代表CSS蒙版中的
alpha
值。并且由于它们都默认为完全不透明的(因为值为1),因此整个图像会通过蒙版显示。下面是我们如何使用自定义属性重写蒙版的方法:
/* Omitting the @property blocks above for brevity */
img {
mask:
linear-gradient(rgba(0,0,0,var(--c-0)) 0 0) left, /* 1 */
linear-gradient(rgba(0,0,0,var(--c-1)) 0 0) right; /* 2 */
mask-size: 50% 100%;
mask-repeat: no-repeat;
transition: --c-0 0.5s, --c-1 0.3s 0.4s;
}
img:hover {
--c-0:0;
--c-1:0;
}
我们在此所做的就是为每个自定义变量应用不同的过渡持续时间和延迟。接下来我们继续来实现鼠标悬停在图像上的效果。蒙版的第一个渐变将逐渐淡出,直到
alpha
值为0,以使图像完全透明,然后是第二个渐变。
更多蒙版!
到目前为止,我们仅在蒙版上使用了两个线性渐变,也仅处理了两个自定义属性。要创建碎片效果,这是不够的,也就是意味着需要更多的渐变和自定义属性!
SCSS让这项工作变得相当琐碎,因此现在我们要开始编写样式。如我们在第一个示例中看到的,我们有一种拼接矩阵。我们可以将它们视为行和列,因此可以定义两个SCSS变量
$x
和
$y
来表示。
自定义属性
每个属性都需要有
@property
定义。但是,没有人希望去手写完成这些内容,因此不妨使用SCSS通过循环运行属性来为我们完成这个繁重的工作:
@for $i from 0 through ($x - 1) {
@for $j from 0 through ($y - 1) {
@property --c-#{$i}-#{$j} {
syntax: "<number>";
initial-value: 1;
inherits: false;
}
}
}
然后在鼠标悬停时让它们全部变成0:
img:hover {
@for $i from 0 through ($x - 1) {
@for $j from 0 through ($y - 1) {
--c-#{$i}-#{$j}: 0;
}
}
}
渐变
我们打算编写
@mixin
为我们生成渐变:
@mixin image() {
$all_t: (); // Transition
$all_m: (); // Mask
@for $i from 0 through ($x - 1) {
@for $j from 0 through ($y - 1) {
$all_t: append($all_t, --c-#{$i}-#{$j} transition($i,$j), comma);
$all_m: append($all_m, linear-gradient(rgba(0,0,0,var(--c-#{$i}-#{$j})) 0 0) calc(#{$i}*100%/(#{$x} - 1)) calc(#{$j}*100%/(#{$y} - 1)), comma);
}
}
transition: $all_t;
mask: $all_m;
}
我们所有的图层蒙版大小均等,因此我们只需要一个属性,它依赖于
$x
和
$y
变量以及
calc()
:
mask-size: calc(100%/#{$x}) calc(100%/#{$y})
你可能也注意到了这行代码:
$all_t: append($all_t, --c-#{$i}-#{$j} transition($i,$j), comma);
我们还将生成过渡属性,过渡属性包含所有先前定义的自定义属性。
最后,得益于SCSS中的
random()
函数,为每个属性生成了不同的持续时间/延迟。
@function transition($i,$j) {
@return $s*random()+s $s*random()+s;
}
现在,我们要做的就是调整
$x
和
$y
变量,以控制碎片的粒度。
我们可以扩展代码并更改随机配置,以考虑不同种类的动画。
在上面的代码中,我定义了
transition()
函数,如下所示:
// Uncomment one to use it
@function transition($i,$j) {
// @return (($s*($i+$j))/($x+$y))+s (($s*($i+$j))/($x+$y))+s; /* diagonal */
// @return (($s*$i)/$x)+s (($s*$j)/$y)+s; /* left to right */
// @return (($s*$j)/$y)+s (($s*$i)/$x)+s; /* top to bottom */
// @return ($s*random())+s (($s*$j)/$y)+s; /* top to bottom random */
@return ($s*random())+s (($s*$i)/$y)+s; /* left to right random */
// @return ($s*random())+s (($s*($i+$j))/($x+$y))+s; /* diagonal random */
// @return ($s*random())+s ($s*random())+s; /* full random*/
}
通过调整公式,我们可以获得不同类的动画:只需对想要使用的那行代码取消注释即可。此列表并非详尽无遗——你还可以考虑更多公式进行任意组合。(想像一下,如果添加高级数学函数,例如
sin()
,
sqrt()
等,可能会发生什么。)
我们还可以通过调整渐变来处理代码,这样,我们就可以对起始颜色进行动画处理,而不是对
alpha
值进行动画处理。这时候的渐变代码是这样的:
linear-gradient(white var(--c-#{$i}-#{$j}),transparent 0)
然后,动画制作变量从100%变为0%。等等,也不必非要一定是线性渐变。为什么不试试放射状的呢?
跟之前一样,可定义的渐变组合是无限的!
让我们引入另一个变量来控制渐变蒙版之间的重叠。此变量将设置
mask-size
,如下所示:
calc(#{$o}*100%/#{$x}) calc(#{$o}*100%/#{$y})
如果等于1,则没有重叠。如果大于1,那么必然有重叠。这样我们就可以制作更多种类的动画了:
结束语
所有我们要做的是找到变量和公式之间的完美组合,就可以创建令人惊讶的超炫酷的图像碎片效果。