发布于 2026-01-06 2 阅读
0

仅使用一个元素的 CSS 碎片化效果

仅使用一个元素的 CSS 碎片化效果

稍等。以下功能在 Firefox 浏览器上不受支持。

这里有一个实际演示,展示了最终效果。无需 JavaScript 处理,也无需 SVG 特效。仅需一个元素<img>和一些 CSS。鼠标悬停即可见证奇迹。

很酷吧?嗯,我得承认这不仅仅是 CSS,因为我用到了 SCSS,但它仍然是一个纯 CSS 效果,没有涉及任何 JS 😉。

以上内容依赖于两件事。

  1. 掩盖事物
  2. 渐变动画

掩盖事物

蒙版有时难以理解,而且经常与裁剪混淆。简单来说:蒙版就是图像。当图像作为蒙版应用于某个元素时,图像中任何透明的部分都允许我们透过图像看到该元素。任何不透明的部分则会使该元素完全可见。

蒙版的工作原理与不透明度类似,但它作用于同一元素的不同部分。这与剪切不同,剪切是指简单地隐藏路径之外的所有内容。蒙版的优势在于,我们可以在同一元素上使用任意数量的蒙版图层——类似于我们可以将多个图像链接在一起的方式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;
}
Enter fullscreen mode Exit fullscreen mode

遮罩层

这里,我们在图像上定义了两个蒙版图层。它们都是纯色,但透明度值不同。

值得注意的是,我们使用的颜色无关紧要,因为默认mask-mode值为 0。alpha唯一相关的是透明度(alpha 值)。我们的渐变可以是0 到 1 的任意值linear-gradient(rgba(X,Y,Z,0.8) 0 0)其中是随机值。XYZ

每个蒙版层的大小等于50% 100%图像的一半宽度和全高。一个蒙版覆盖图像的左侧,另一个覆盖右侧。最终,我们得到两个互不重叠的蒙版,覆盖图像的整个区域。正如我们之前讨论的,每个蒙版都具有不同的透明度值(Alpha 值)。

渐变动画

我们想要做的,是将动画应用到蒙版的线性渐变 alpha 值上,从而创建透明动画。稍后,我们将把这些动画改为异步动画,以实现碎片化效果。

在 CSS 中,我们一直无法实现渐变动画。直到我们获得了有限的支持@property

更多详情请参阅我之前的帖子。

简而言之,@property它允许我们创建自定义 CSS 属性,我们可以通过指定类型来定义语法。让我们创建两个属性,`a`--c-0和 ` --c-1b`,它们接受一个初始值为 `0` 的数字1

@property --c-0 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
@property --c-1 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
Enter fullscreen mode Exit fullscreen mode

这些属性将代表我们 CSS 蒙版中的 alpha 值。由于它们的默认值均为完全不透明(即 0 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;
}
Enter fullscreen mode Exit fullscreen mode

我们在这里所做的只是为每个自定义变量应用不同的过渡持续时间和延迟。请将鼠标悬停在图像上。蒙版的第一个渐变会逐渐淡出,直到透明度降至零,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;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

然后我们让它们全部在0鼠标悬停时显示:

img:hover {
  @for $i from 0 through ($x - 1) {
    @for $j from 0 through ($y - 1) {
      --c-#{$i}-#{$j}: 0;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

梯度

我们将编写一个@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;
}
Enter fullscreen mode Exit fullscreen mode

我们所有的掩码层大小都相同,因此我们只需要一个属性,依赖于 ` $xand`$y变量和calc()`:`。

mask-size: calc(100%/#{$x}) calc(100%/#{$y})
Enter fullscreen mode Exit fullscreen mode

您可能也注意到了这一行:

$all_t: append($all_t, --c-#{$i}-#{$j} transition($i,$j), comma);
Enter fullscreen mode Exit fullscreen mode

在相同的混合过程中,我们还会生成包含所有先前定义的自定义属性的过渡属性。

最后,借助random()SCSS 中的函数,我们为每个属性生成不同的持续时间/延迟。

@function transition($i,$j) {
  @return $s*random()+s $s*random()+s;
}
Enter fullscreen mode Exit fullscreen mode

现在我们只需要调整$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*/
}
Enter fullscreen mode Exit fullscreen mode

通过调整公式,我们可以得到不同类型的动画。只需取消注释你想要使用的公式即可。此列表并非详尽无遗——通过考虑更多公式,我们可以实现任意组合。(想象一下,如果我们添加更高级的数学函数,例如 `\mathbf{x}`、`\mathbf{y} sin()`sqrt()等,将会产生怎样的效果。)

我们还可以调整代码,让渐变不再改变透明度,而是改变颜色停止点。调整后的渐变效果如下:

linear-gradient(white var(--c-#{$i}-#{$j}),transparent 0)
Enter fullscreen mode Exit fullscreen mode

然后我们对变量进行动画处理,使其从100%变为0%。而且,嘿,我们不必拘泥于线性渐变。为什么不试试径向渐变呢?

与过渡一样,我们可以定义任何我们想要的渐变——组合是无限的!

我们引入另一个变量来控制渐变蒙版之间的重叠程度。这个变量的设置方式mask-size如下:

calc(#{$o}*100%/#{$x}) calc(#{$o}*100%/#{$y})
Enter fullscreen mode Exit fullscreen mode

如果大小相等,则不会重叠1。如果大于,则会发生重叠。这使我们能够制作更多类型的动画:

就是这样!

我们只需要找到变量和公式之间的完美组合,就能创造出惊人而疯狂的图像碎片化效果。

文章来源:https://dev.to/afif/css-only-fragmentation-effect-using-one-element-18kg