使用 CSS 遮罩和自定义属性实现图像碎片效果

前言

今天我们要实现一个这样的效果:首先我们页面上有一张完整的图像,然后当我们鼠标滑过图像的时候,让它一次消失一块,就像漂浮在微小的碎片中一样,从而达到碎片消失的效果。

具体实现方式不需要JavaScript处理,也不需要SVG。只有一个img和一些SCSS。

先看一个效果:
在这里插入图片描述
很酷,对吧?当然,但问题是。您必须在Chrome、Edge或Opera中查看这一点,因为这些浏览器是目前唯一支持@property的浏览器,这是实现这一想法的关键组件。我们不会让它阻止我们,因为这是一个很好的机会,可以让我们接触到很酷的 CSS 功能,例如蒙版和借助@property。

完整代码如下:

HTML代码:

<img src="https://media.mybj123.com/wp-content/uploads/2021/08/1629093342-aa844bb3422dd98.jpg">

SCSS代码:

$x:20;  /*  number of row */
$y:20;  /*  number of columns */
$s:0.8; /* speed factor */

@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;
    }
  }
}
@function transition($i,$j) {
  @return ($s*random())+s ($s*random())+s;
}

@mixin image() {
    $all_t: ();
    $all_m: ();
    @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;
     -webkit-mask: $all_m;
     mask: $all_m;
}

img {
  @include image();
  -webkit-mask-size:calc(100%/#{$x}) calc(100%/#{$y});
  -webkit-mask-repeat:no-repeat;
  mask-size:calc(100%/#{$x}) calc(100%/#{$y});
  mask-repeat:no-repeat;
}

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

body {
  background:pink;
}

遮罩的东西

遮罩有时很难概念化,常常与剪裁混淆。底线是:遮罩是图像。当图像作为遮罩应用于元素时,图像的任何透明部分都允许我们直接看到元素。任何不透明部分都将使图元完全可见。

遮罩的工作方式与不透明度相同,但位于同一元素的不同部分。这与剪切不同,剪切是一种路径,路径之外的所有内容都被简单地隐藏。掩蔽的优点是我们可以在同一个元素上有我们想要的任意多的掩蔽层 — 类似于我们如何在背景图像上链接多个图像。

由于遮罩是图像,我们可以使用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 透明度值不同。上面的语法可能看起来很奇怪,但它是一种简化的编写方式linear-gradient(rgba(0,0,0,0.8), rgba(0,0,0,0.8))。

值得注意的是,我们使用的颜色无关紧要,因为默认遮罩模式是alpha,alpha值是唯一相关的东西。我们的梯度可以是linear-gradient(rgba(X,Y,Z,0.8) 0 0),其中 X,Y和Z是随机值。

每个遮罩层等于50% 100%(或图像的半宽和全高)。一个面罩覆盖左侧,另一个覆盖右侧。最后,我们有两个不重叠的遮罩覆盖了图像的整个区域,正如我们之前讨论的,每个遮罩都有一个不同定义的 alpha 透明度值。
在这里插入图片描述
由上图可以看出使用两个线性渐变创建的两个蒙版层。左侧的第一个渐变的 alpha 值为 0.8。右侧的第二个渐变的 alpha 值为 0.5。第一个渐变更不透明,意味着更多的图像显示出来。第二个渐变更透明,意味着更多的背景显示出来。

动画线性渐变

我们想要做的是将动画应用于蒙版的线性渐变alpha值以创建透明动画。稍后,我们会将这些制作成异步动画,以创建碎片效果。

动画渐变是我们在CSS中无法做到的。也就是说,直到我们得到@property的有限支持。同样,您需要在Chrome或其他支持Blink的浏览器中查看此内容:
在这里插入图片描述
CSS代码:

* {
    box-sizing: border-box
}

:root {
    --sand: #b3994d;
    --wave-one: #fff;
    --wave-two: #d9eaf2;
    --wave-three: #69c;
    --wave-four: #0d4073
}

@property --wave {
    inherits:false;
	initial-value:0%;
	syntax:''
}

body {
    background: linear-gradient(transparent 0 calc(35% + (var(--wave) * 0.5)),var(--wave-four) calc(75% + var(--wave)) 100%),linear-gradient(transparent 0 calc(35% + (var(--wave) * 0.5)),var(--wave-three) calc(50% + var(--wave)) calc(75% + var(--wave))),linear-gradient(transparent 0 calc(20% + (var(--wave) * 0.5)),var(--wave-two) calc(35% + var(--wave)) calc(50% + var(--wave))),linear-gradient(transparent 0 calc(15% + (var(--wave) * 0.5)),var(--wave-one) calc(25% + var(--wave)) calc(35% + var(--wave))),var(--sand);
    animation: waves 5s infinite ease-in-out;
    min-height: 100vh
}

@-moz-keyframes waves {
    50% {
        --wave: 25%
    }
}

@-webkit-keyframes waves {
    50% {
        --wave: 25%
    }
}

@-o-keyframes waves {
    50% {
        --wave: 25%
    }
}

@keyframes waves {
    50% {
        --wave: 25%
    }
}

简而言之,@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,以使图像完全透明,然后是第二个渐变。

HTML代码:

<img src="https://media.mybj123.com/wp-content/uploads/2021/08/1629091395-be1cfd1ea1d697d.jpg">

CSS代码:

@property --c-0 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
@property --c-1 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
img {
  -webkit-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 */
  -webkit-mask-size:50% 100%;
  -webkit-mask-repeat:no-repeat;
  transition:--c-0 0.5s, --c-1 0.3s 0.4s;
}
img:hover {
  --c-0:0;
  --c-1:0;
}
body {
  background:pink;
}

效果如下:
在这里插入图片描述

更多的遮罩!

到目前为止,我们只使用了遮罩上的两个线性渐变和两个自定义属性。要创建平铺或碎片效果,我们需要更多的平铺,这意味着更多的渐变和许多自定义属性!

SCSS使这成为一项相当简单的任务,因此这就是我们从现在开始写作风格的方法。正如我们在第一个例子中看到的,我们有一种瓷砖矩阵。我们可以将它们视为行和列,因此让我们定义两个SCSS变量, x 和 x和 xy来表示它们。

标题自定义属性

我们需要@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 和 x和 xy变量和calc()(更多关于calc()用法):

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

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

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

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

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

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

现在我们要做的就是调整 x 和 x和 xy变量来控制我们碎片的粒度。

实例

公共HTML代码:

<img src="https://media.mybj123.com/wp-content/uploads/2021/08/1629091395-be1cfd1ea1d697d.jpg">

玩动画

我们还可以更改随机配置以考虑不同类型的动画。

SCSS代码:

$x:20;  /*  number of row */
$y:20;  /*  number of columns */
$s:0.8; /* speed factor */

@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;
    }
  }
}
@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*/
}

@mixin image() {
    $all_t: ();
    $all_m: ();
    @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;
     -webkit-mask: $all_m;
     mask: $all_m;
}

img {
  @include image();
  -webkit-mask-size:calc(100%/#{$x}) calc(100%/#{$y});
  -webkit-mask-repeat:no-repeat;
  mask-size:calc(100%/#{$x}) calc(100%/#{$y});
  mask-repeat:no-repeat;
}

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

body {
  background:pink;
}

效果如下:
在这里插入图片描述
在上面的代码中,我定义了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*/
}

通过调整公式,我们可以得到不同种类的动画。只需取消您想要使用的注释。这份清单并非详尽无遗 — 通过考虑更多的forumlas,我们可以有任何组合(我让您想象一下,如果我们添加高级数学函数,比如sin()、sqrt()等,可能会发生什么。)

玩渐变

我们仍然可以通过调整渐变来处理我们的代码,这样我们就可以为颜色停止设置动画而不是 alpha 值的动画。我们的渐变看起来像这样:

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

然后我们将变量从100%到 进行动画处理0%。而且,嘿,我们不必坚持使用线性渐变。为什么不是径向的?

SCSS代码 :

$x:20;  /*  number of row */
$y:20;  /*  number of columns */
$s:0.8; /* speed factor */

@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;
    }
  }
}
@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*/ 
}
@function gradient($c) {
  //@return linear-gradient(rgba(0,0,0,var($c)) 0 0);
  //@return linear-gradient(0deg,white calc(100%*var(#{$c})),transparent 0);
  @return radial-gradient(white calc(100%*var(#{$c})),transparent 0);
  //@return conic-gradient(white calc(100%*var(#{$c})),transparent 0);
}
@mixin image() {
    $all_t: ();
    $all_m: ();
    @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, gradient(--c-#{$i}-#{$j}) calc(#{$i}*100%/(#{$x} - 1)) calc(#{$j}*100%/(#{$y} - 1)), comma);
      }
    }
    transition: $all_t;
     -webkit-mask: $all_m;
     mask: $all_m;
}

img {
  @include image();
  -webkit-mask-size:calc(100%/#{$x}) calc(100%/#{$y});
  -webkit-mask-repeat:no-repeat;
  mask-size:calc(100%/#{$x}) calc(100%/#{$y});
  mask-repeat:no-repeat;
}

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

body {
  background:pink;
}

效果如下:
在这里插入图片描述
就像过渡一样,我们可以定义任何我们想要的渐变——组合是无限的!

玩重叠

让我们引入另一个变量来控制渐变蒙版之间的重叠。此变量将设置mask-size如下:

calc(#{$o}*100%/#{$x}) calc(#{$o}*100%/#{$y})

如果等于 ,则没有重叠1。如果它更大,那么我们确实得到了重叠。这使我们可以制作更多种类的动画:

SCSS代码:

$x:20;  /*  number of row */
$y:20;  /*  number of columns */
$s:0.8; /* speed factor */
$o:1.4; /* overlapping factor*/

@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;
    }
  }
}
@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*/ 
}
@function gradient($c) {
  //@return linear-gradient(rgba(0,0,0,var($c)) 0 0);
  //@return linear-gradient(0deg,white calc(100%*var(#{$c})),transparent 0);
  @return radial-gradient(farthest-side,white calc(100%*var(#{$c})),transparent 0);
  //@return conic-gradient(white calc(100%*var(#{$c})),transparent 0);
}
@mixin image() {
    $all_t: ();
    $all_m: ();
    @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, gradient(--c-#{$i}-#{$j}) calc(#{$i}*100%/(#{$x} - 1)) calc(#{$j}*100%/(#{$y} - 1)), comma);
      }
    }
    transition: $all_t;
     -webkit-mask: $all_m;
     mask: $all_m;
}


img {
  @include image();
  -webkit-mask-size:calc(#{$o}*100%/#{$x}) calc(#{$o}*100%/#{$y});
  -webkit-mask-repeat:no-repeat;
  mask-size:calc(#{$o}*100%/#{$x}) calc(#{$o}*100%/#{$y});
  mask-repeat:no-repeat;
}
img:hover {
  @for $i from 0 through ($x - 1) {
      @for $j from 0 through ($y - 1) {
         --c-#{$i}-#{$j}: 0;
      }
    }
}

body {
  background:pink;
}

效果如下:
在这里插入图片描述

结语

原理很简单,我们所要做的就是找到变量和公式之间的完美组合,以创造出惊人而疯狂的图像碎片效果。

原文链接:https://www.mybj123.com/12567.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值