草庐IT

现代 CSS 之高阶图片渐隐消失术

ChokCoco 2023-03-28 原文

在过往,我们想要实现一个图片的渐隐消失。最常见的莫过于整体透明度的变化,像是这样:

<div class="img"></div>

div {
    width: 300px;
    height: 300px;
    background: url(image.jpg);
    transition: .4s;
}
.img:hover {
    opacity: 0;
}

但是,CSS 的功能如此强大的今天。我们可以利用 CSS 实现的渐隐效果已经不再是如此的简单。

想想看,下面这样一个效果,是 CSS 能够实现的么?

答案是肯定的!本文就将一步一步,从零开始,仅仅使用一个标签,实现上述的图片渐隐效果。

这里,有两个核心的点:

  1. 如何将一张图片切割的这么细,切割成这么多块?
  2. 基于上述 (1)的基础上,又该如何分别控制这些小块的独立隐藏和展示呢?

莫慌,让我们一步一步来解决他们。

强大的 Mask

首先,我们需要用到 Mask。

在 CSS 中,mask 属性允许使用者通过遮罩或者裁切特定区域的图片的方式来隐藏一个元素的部分或者全部可见区域。

语法

最基本,使用 mask 的方式是借助图片,类似这样:

{
    /* Image values */
    mask: url(mask.png);                       /* 使用位图来做遮罩 */
    mask: url(masks.svg#star);                 /* 使用 SVG 图形中的形状来做遮罩 */
}

当然,使用图片的方式后文会再讲。借助图片的方式其实比较繁琐,因为我们首先还得准备相应的图片素材,除了图片,mask 还可以接受一个类似 background 的参数,也就是渐变。

类似如下使用方法:

{
    mask: linear-gradient(#000, transparent)                      /* 使用渐变来做遮罩 */
}

那该具体怎么使用呢?一个非常简单的例子,上述我们创造了一个从黑色到透明渐变色,我们将它运用到实际中,代码类似这样:

下面这样一张图片,叠加上一个从透明到黑色的渐变,

{
    background: url(image.png) ;
    mask: linear-gradient(90deg, transparent, #fff);
}

应用了 mask 之后,就会变成这样:

这个 DEMO,可以先简单了解到 mask 的基本用法。

这里得到了使用 mask 最重要结论:图片与 mask 生成的渐变的 transparent 的重叠部分,将会变得透明。

值得注意的是,上面的渐变使用的是 linear-gradient(90deg, transparent, #fff),这里的 #fff 纯色部分其实换成任意颜色都可以,不影响效果。

CodePen Demo -- 使用 MASK 的基本使用

使用 mask 实现 hover 隐藏图片

了解了 mask 的简单用法后,我们来看这样一个非常简单的例子,我们改造下上述的第一个 DEMO。

<div class="img"></div>
div {
    width: 300px;
    height: 300px;
    background: url(image.jpg);
}
.img:hover {
    mask: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));
}

是的,利用 Mask,我们同样也可以得到近似的消失效果:

如果对于 Mask 你还不了解,你需要首先看看这篇:奇妙的 CSS MASK

当然,对于现在这个效果,有个很大的缺陷,那就是缺少了动画。图片是瞬间消失的。所以,我们还需要给上述的借助 mask 实现的图片消失效果添加上动画。

而这,就需要用上 CSS @property 了。

强大的 CSS @property

CSS @property,大家应该不那么陌生了。

@property CSS at-rule 是 CSS Houdini API 的一部分, 它允许开发者显式地定义他们的 CSS 自定义属性,允许进行属性类型检查、设定默认值以及定义该自定义属性是否可以被继承。

如果你对 CSS @property 还有所疑惑,建议你先快速读一读这篇文章 -- CSS @property,让不可能变可能

回到我们的正题,如果我们想给上述使用 Mask 的代码,添加上动画,我们期望代码大概是这样:

div {
    width: 300px;
    height: 300px;
    background: url(image.jpg);
    mask: linear-gradient(rgba(0, 0, 0, 1), rgba(0, 0, 0, 1));
}
.img:hover {
    mask: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));
}

这里,mask 的是从 mask: linear-gradient(rgba(0, 0, 0, 1), rgba(0, 0, 0, 1))mask: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)) 变化的。

但是实际上,这样并不会产生任何的动画效果。

原因在于,我们 Mask 属性本身是不支持过渡动画的!

但是,利用上 CSS @property,整个效果就不一样了。借助,CSS @property,我们改造一下代码:

@property --m-0 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
div {
    width: 300px;
    height: 300px;
    background: url(image.jpg);
    mask: linear-gradient(90deg, rgba(0, 0, 0, var(--m-0)), rgba(0, 0, 0, var(--m-0)));
    transition: --m-0 0.5s;
}
div:hover {
    --m-0: 0;
}

我们利用 CSS @property 定义了一个名为 --m-0 的变量,然后,我们将整个动画过渡效果赋予了这个变量,而不是整个 mask。

利用这个小技巧,我们就可以成功的实现基于 mask 属性的动画效果:

借助多重 mask 分割图片

到了这一步,后面的步骤其实就很明朗了。

由于 mask 拥有和 background 一样的特性。因此,mask 是可以有多重 mask 的。也就是说,我们可以设置多个不同的 mask 效果给同一个元素。

什么意思呢?上面的效果只有一重 mask,我们稍微添加一些 mask 代码,让它变成 2 重 mask:

@property --m-0 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
@property --m-1 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
div {
    mask: 
        linear-gradient(90deg, rgba(0, 0, 0, var(--m-0)), rgba(0, 0, 0, var(--m-0))),
        linear-gradient(90deg, rgba(0, 0, 0, var(--m-1)), rgba(0, 0, 0, var(--m-1)));
    mask-size: 50% 100%;
    mask-position: left, right;
    mask-repeat: no-repeat;
    transition: 
        --m-0 0.3s,
        --m-1 0.25s 0.15s;
}
div:hover {
    --m-0: 0;
    --m-1: 0;
}

这样,我们的步骤大概是:

  1. 首先将 mask 一分为二,左右两边各一个
  2. 然后,设置了两个基于 CSS @property 的变量,--m-0--m-0
  3. 然后,给它们设置了不同的过渡时间和过渡延迟时间
  4. 在 hover 的一瞬间,再将这两个变量的值,都置为 0,也就是实现 linear-gradient(90deg, rgba(0, 0, 0, 1), rgba(0, 0, 0, 1))linear-gradient(90deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)) 的变化,用于隐藏对应 mask 块
  5. 由于设置了不同的过渡时间和延迟时间,整体上看上去,整个动画就分成了两部分

看看效果:

继续切割为 4 重 mask

好,既然 2 重 mask 效果没问题,那么我们可以再进一步,将整个效果切割为 4 个 mask。代码还是如法炮制,这里我再贴上核心代码:

@property --m-0 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
@property --m-1 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
@property --m-2 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
@property --m-3 {
   syntax: "<number>";
   initial-value: 1;
   inherits: false;
}
div {
    mask: 
        linear-gradient(90deg, rgba(0, 0, 0, var(--m-0)), rgba(0, 0, 0, var(--m-0))),
        linear-gradient(90deg, rgba(0, 0, 0, var(--m-1)), rgba(0, 0, 0, var(--m-1))),
        linear-gradient(90deg, rgba(0, 0, 0, var(--m-2)), rgba(0, 0, 0, var(--m-2))),
        linear-gradient(90deg, rgba(0, 0, 0, var(--m-3)), rgba(0, 0, 0, var(--m-3)));
    mask-size: 50% 50%;
    mask-repeat: no-repeat;
    mask-position: left top, right top, left bottom, bottom right;
    transition: 
        --m-0 0.3s,
        --m-1 0.15s 0.1s,
        --m-2 0.25s 0.21s,
        --m-3 0.19s 0.15s;
}
div:hover {
    --m-0: 0;
    --m-1: 0;
    --m-2: 0;
    --m-3: 0;
}

这样,我们就可以得到 4 块分割图片的 mask 消失效果:

好,再依次类推,我们就可以得到分割为 9 块的,分割为 16 块的。由于代码太多,就简单看看效果:

CodePen Demo -- 基于 @property 和 mask 的图片渐隐消失术

基于 SCSS 简化代码

那么,如果我们要分割为 100 块呢?或者 400 块呢?还要手写这些代码吗?

当然不需要,由于上面的代码的规律非常的明显,我们可以借助预处理器很好的封装整个效果。从而快速的实现切割成任意规则块数的效果。

完整的代码如下:

$count: 400;
$sqrt: 20;
$per: 100% / $sqrt;
$width: 300px;
$perWid: 15;

@for $i from 1 to ($count + 1) {
    @property --m-#{$i} {
       syntax: "<number>";
       initial-value: 1;
       inherits: false;
    }
}
@function bgSet($n) {
    $bg : radial-gradient(rgba(0, 0, 0, var(--m-1)), rgba(0, 0, 0, var(--m-1)));
    
    @for $i from 2 through $n {         
        $bg: $bg, radial-gradient(rgba(0, 0, 0, var(--m-#{$i})), rgba(0, 0, 0, var(--m-#{$i})));
    }
    
    @return $bg;
}
@function positionSet($n) {
    $bgPosition: ();

    @for $i from 0 through ($n) {   
        @for $j from 0 through ($n - 1) {  
            $bgPosition: $bgPosition, #{$i * $perWid}px #{$j * $perWid}px;
        }
    }
    
    @return $bgPosition;
}
@function transitionSet($n) {
    $transition: --m-1 0.1s 0.1s;

    @for $i from 1 through $n {   
        $transition: $transition, --m-#{$i} #{random(500)}ms #{random(500)}ms;
    }
    
    @return $transition;
}
div {
    width: $width;
    height: $width;
    background: url(image.jpg);
    mask: bgSet($count);
    mask-size: $per $per;
    mask-repeat: no-repeat;
    mask-position: positionSet($sqrt); 
    transition: transitionSet($count);
}
div:hover {
    @for $i from 1 through $count {         
        --m-#{$i}: 0;
    }
}

这里,简单解释一下,以生成 400 块小块为例子:

  1. 最上面的 SCSS 变量定义中,
    • $count 是我们最终生成的块数
    • $sqrt 是每行以及每列会拥有的块数
    • $per 是每一块占整体图片元素的百分比值
    • $width 是整个图片的宽高值
    • $perWid 是每一块的宽高值
  2. 利用了最上面的一段循环函数,批量的生成 CSS @property 变量,从 --m-0--m-400
  3. @function bgSet($n) {} 是生成 400 块 mask 片段
  4. @function positionSet($n) 是生成 400 块 mask 的 mask-position,也就是生成 400 段不同定位,让 400 块 mask 刚好覆盖整个图片
  5. @function transitionSet($n) {} 是随机设置每个块的动画时间和延迟时间
  6. 代码最下面,还有一段循环函数,生成 400 个 CSS @property 变量的 hover 值,当 hover 的时候,全部变成 0

这样,我们就实现了 400 分块的渐隐效果。效果如下:

CodePen Demo -- 基于 @property 和 mask 的图片渐隐消失术

调整过渡变量,控制方向

当然,上面我们的对每一个小块的 transition 的过渡时间和过渡延迟时间的设置,都是随机的:

@function transitionSet($n) {
    $transition: --m-1 0.1s 0.1s;

    @for $i from 1 through $n {   
        $transition: $transition, --m-#{$i} #{random(500)}ms #{random(500)}ms;
    }
    
    @return $transition;
}

我们完全可以通过一定的控制,让过渡效果不那么随机,譬如有一定的方向感。

下面,我们通过让动画的延迟时间与 $i,也就是 mask 小块的 index 挂钩:

@function transitionSet($n) {
    $transition: --m-1 0.1s 0.1s;

    @for $i from 1 through $n {   
        $transition: $transition, --m-#{$i} #{100 + random(500)}ms #{($i / 50) * random(100)}ms;
    }
    
    @return $transition;
}

那么,整个动画的方向就是从左往右逐渐消失:

CodePen Demo -- 基于 @property 和 mask 的图片渐隐消失术 2

当然,有意思的是,这个效果,不仅仅能够运用在图片上,它其实可以作用在任何元素之上!

譬如,我们有的只是一段纯文本,同样适用这个效果:

CodePen Demo -- 基于 @property 和 mask 的文本渐隐消失术

总结

到这里,简单总结一下。本文,我们核心利用了 CSS @propery 和 mask,实现了一些原本看上去需要非常多 div 才能实现或者是需要借助 Canvas 才能实现的效果。同时,我们借助了 SCSS 预处理器,在寻找到规律后,极大的简化了 CSS 代码的书写量。

到今天,强大的 CSS 已经允许我们去做越来越多更有意思的动效,CSS @propery 和 mask 这两个属性在现代 CSS 发挥了非常重要的作用,非常建议大家认真掌握以下这两个属性。

最后

好了,本文到此结束,希望本文对你有所帮助 ?

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

有关现代 CSS 之高阶图片渐隐消失术的更多相关文章

  1. ruby - capybara field.has_css?匹配器 - 2

    我在MiniTest::Spec和Capybara中使用以下规范:find_field('Email').must_have_css('[autofocus]')检查名为“电子邮件”的字段是否具有autofocus属性。doc说如下:has_css?(path,options={})ChecksifagivenCSSselectorisonthepageorcurrentnode.据我了解,字段“Email”是一个节点,因此调用must_have_css绝对有效!我做错了什么? 最佳答案 通过JonasNicklas得到了答案:No

  2. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

  3. css - 用 watir 检查标签类? - 2

    我有一个div,它根据表单是否正确提交而改变。我想知道是否可以检查类的特定元素?开始元素看起来像这样。如果输入不正确,添加错误类。 最佳答案 试试这个:browser.div(:id=>"myerrortest").class_name更多信息:http://watir.github.com/watir-webdriver/doc/Watir/HTMLElement.html#class_name-instance_method另一种选择是只查看具有您期望的类的div是否存在browser.div((:id=>"myerrortes

  4. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  5. ruby-on-rails - Rails 3,在RAILS_ROOT上方显示来自本地文件系统的jpg图片 - 2

    我正在尝试找出一种方法来显示来自不在RAILS_ROOT下(在RedHat或Ubuntu环境中)的已安装文件系统的图像。我不想使用符号链接(symboliclink),因为这个应用程序实际上是通过Tomcat部署的,而当我关闭Tomcat时,Tomcat会尝试跟随符号链接(symboliclink)并删除挂载中的所有图像。由于这些文件的数量和大小,将图像放在public/images下也不是一种选择。我查看了send_file,但它只会显示一张图片。我需要在一个格式良好的页面中显示6个请求的图像。由于膨胀,我宁愿不使用Base64编码,但我不知道如何将图像数据与呈现的页面一起传递下去。

  6. ruby-on-rails - Assets 管道损坏 : Not compiling on the fly css and js files - 2

    我开始了一个新的Rails3.2.5项目,Assets管道不再工作了。CSS和Javascript文件不再编译。这是尝试生成Assets时日志的输出:StartedGET"/assets/application.css?body=1"for127.0.0.1at2012-06-1623:59:11-0700Servedasset/application.css-200OK(0ms)[2012-06-1623:59:11]ERRORNoMethodError:undefinedmethod`each'fornil:NilClass/Users/greg/.rbenv/versions/1

  7. python - 在 Ruby 中使用高阶函数 - 2

    在学习Python之后,我现在正在尝试学习Ruby,但我在将这段代码转换为Ruby时遇到了问题:defcompose1(f,g):"""Returnafunctionh,suchthath(x)=f(g(x))."""defh(x):returnf(g(x))returnh我必须使用block来翻译吗?或者Ruby中是否有类似的语法? 最佳答案 您可以使用Ruby中的lambda执行此操作(我在这里使用的是1.9stabby-lambda):compose=->(f,g){->(x){f.(g.(x))}}所以compose是一个返

  8. ruby-on-rails - Rails - 理解 application.js 和 application.css - 2

    rails新手。只是想了解\assests目录中的这两个文件。例如,application.js文件有如下行://=requirejquery//=requirejquery_ujs//=require_tree.我理解require_tree。只是将所有JS文件添加到当前目录中。根据上下文,我可以看出requirejquery添加了jQuery库。但是它从哪里得到这些jQuery库呢?我没有在我的Assets文件夹中看到任何jquery.js文件——或者直接在我的整个应用程序中没有看到任何jquery.js文件?同样,我正在按照一些说明安装TwitterBootstrap(http:

  9. css - Rails 4.1 和 Bootstrap 3 字形图标不工作 - 2

    我正在尝试消除使用Bootstrap3的Rails4元素中的glyphicon错误。我没有使用任何Bootstrapgem将其添加到Assets管道中。我手动将bootstrap.css和bootstrap.js添加到各自的app/assets目录下,分别添加到application.css和application.js什么的我现在在网络浏览器的控制台中看到以下内容:GEThttp://localhost:3000/fonts/glyphicons-halflings-regular.woff404(NotFound)localhost/:1GEThttp://localhost:30

  10. css - rails 萨斯 : variables are not passed with @import - 2

    我有一个使用twitterbootstrap和sass的Rails元素。scss文件结构化到文件夹中,所以我有更好的概述。现在我想为包含我的颜色等的全局变量定义一个文件,并将这些值传递给其他文件,这样我就有更少的冗余代码。虽然所有代码都已正确导入和应用,变量不起作用。这是当前的设置:样式表/application.css.scss/**=require_self*=require_tree*//*stylesheets/||–base/||–_reset.scss#Reset/normalize||–_typography.scss#Typographyrules||–componen

随机推荐