记录-有意思的气泡 Loading 效果

news/2024/11/17 10:40:13/

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

今日,群友提问,如何实现这么一个 Loading 效果:

这个确实有点意思,但是这是 CSS 能够完成的?

没错,这个效果中的核心气泡效果,其实借助 CSS 中的滤镜,能够比较轻松的实现,就是所需的元素可能多点。参考我们之前的:

  • 使用纯 CSS 实现超酷炫的粘性气泡效果
  • 巧用 CSS 实现酷炫的充电动画

圆弧的实现

首先,我们可能需要实现这样一段圆弧:

这里需要用到的技术是:

角向渐变 conic-gradient() + mask 以及两个伪元素。图片示意如下:

 核心代码如下图:

HTML:

<div class="g-container"><div class="g-circle"></div>
</div>

CSS:

:root {--headColor: hsl(130, 75%, 75%);--endColor: hsl(60, 75%, 40%);
}
.g-container {position: relative;background: #000;
}
.g-circle {position: relative;width: 300px;height: 300px;border-radius: 50%;background: conic-gradient(var(--headColor) 0, var(--headColor) 10%,hsl(120, 75%, 70%), hsl(110, 75%, 65%), hsl(100, 75%, 60%),hsl(90, 75%, 55%), hsl(80, 75%, 50%),hsl(70, 75%, 45%),var(--endColor) 30%,var(--endColor) 35%,transparent 35%);mask: radial-gradient(transparent, transparent 119px, #000 120px, #000 120px, #000 100%);&::before,&::after {content: "";position: absolute;inset: 0;width: 30px;height: 30px;background: var(--headColor);top: 0;left: 135px;border-radius: 50%;}&::after {background: var(--endColor);left: unset;top: 214px;right: 26px;}
}

这样,我们就得到了这样一个图形:

气泡的实现

接下来,我们来实现尾部气泡向外扩散的效果。

由于这里涉及了多个气泡的不同运动动画,多个标签元素肯定是少不了了。

因此,接下来我们要做的事情:

  1. 我们需要多一组元素,将其绝对定位到上述圆环的头部或者尾部
  2. 给每个子元素随机设置多个大小不一的圆,颜色保持一致
  3. 给每个子元素随机设置不同方向的,向外扩散的位移动画
  4. 给每个子元素随机设置负的 animation-delay,造成动画上的先后顺序,并以此形成整个无限循环的气泡扩散动画

这里,由于有许多小气泡的动画,这个数量,我设置成了 100。那肯定是不能一个一个手写它们的动画代码,需要借助 SASS/LESS 等预处理器的循环、随机等函数。

核心代码如下:

HTML:

<div class="g-container"><div class="g-circle"></div><ul class="g-bubbles"><li class="g-bubble"></li>// ... 共 100 个 bubble 元素 <li class="g-bubble"></li></ul>
</div>

CSS:

// 上面圆环的代码,保持一致,下面只补充气泡动画的代码
.g-bubbles {position: absolute;width: 30px;height: 30px;border-radius: 50px;top: 100px;left: 235px;background: var(--headColor);
}.g-bubble {position: absolute;border-radius: 50%;background-color: inherit;
}@for $i from 1 through 100 { .g-bubble:nth-child(#{$i}) {--rotate: calc(#{random(360)} * 1deg);--dis: calc(#{random(100)} * 1px);--width: calc(3px + #{random(25)} * 1px);top: 50%;left: 50%;transform: translate(-50%, -50%);width: var(--width);height: var(--width);animation: move #{(random(1500) + 1500) / 1000}s ease-in-out -#{random(3000) / 1000}s infinite;}
}@keyframes move {0% {transform: translate(-50%, -50%) rotate(0deg);}75% {opacity: .9;}100% {transform: rotateZ(var(--rotate)) translate(-50%, var(--dis));opacity: .4;}
}

核心在于 @for $i from 1 through 100 { } 这段 SASS 代码内部,我们实现了上面说的 (2)(3)(4) 的功能点!

这样,我们就得到了这样一个效果,在尾部有大量气泡动画,不断向外扩散的效果:

借助滤镜实现粘性气泡效果

OK,到这里整个效果基本就做完了。当然,也是剩下最后最重要的一步,需要让多个气泡之间产生一种粘性融合的效果。

这个技巧在此前非常多篇文章中,也频繁提及过,就是利用 filter: contrast() 滤镜与 filter: blur() 滤镜。

如果你还不了解这个技巧,可以戳我的这篇文章看看:你所不知道的 CSS 滤镜技巧与细节

简述下该技巧:

单独将两个滤镜拿出来,它们的作用分别是:

  1. filter: blur(): 给图像设置高斯模糊效果。
  2. filter: contrast(): 调整图像的对比度。

但是,当他们“合体”的时候,产生了奇妙的融合现象。

仔细看两圆相交的过程,在边与边接触的时候,会产生一种边界融合的效果,通过对比度滤镜把高斯模糊的模糊边缘给干掉,利用高斯模糊实现融合效果。

基于此,我们再简单改造下我们的 CSS 代码,所需要加的代码量非常少:

  1. 加上滤镜 blur() 和 contrast() ,形成融合粘性效果
  2. 加上整个圆环的旋转即可效果
  3. 加上滤镜 hue-rotate(),实现色彩的变换动画
.g-container {// ... 保持一致background: #000;filter: blur(3px) contrast(5);animation: rotate 4s infinite linear;
}
@keyframes rotate {100% {transform: rotate(360deg);filter: blur(3px) contrast(5) hue-rotate(360deg);}
}

就这样,我们就大致还原了题图的效果:

完整的代码,你可以戳这里:CodePen Demo -- Pure CSS Loading Animation

修复违和感

当然,上面的效果,乍一看还行,仔细看,违和感很重。

原因在于,扩散出来的小球也跟着半圆环一起进行了旋转动画,看上去就有点奇怪。

正确的做法应该是,圆环尾部的气泡应该是原地发散消失的。

那么,怎么能够做到气泡效果,一直发生在圆环的尾部,同时消失的时候又不跟着整个圆环一起进行旋转呢?我们想要的最终效果,应该是这样:

这里,我们可以拆解一下。想象,如果去掉圆环的旋转,其实我们只需要实现这样一个效果即可:

整个动画的核心就转变成了如何实现这么一个效果。看似复杂,其实也很好做。

首先,我们重新改造一下上述的 .g-bubbles

  1. 生成 N 个一样大小的小球元素,定位在整个容器的中间
HTML:
<div class="g-container"><div class="g-circle"></div><ul class="g-bubbles"><li class="g-bubble"></li>// ... 共 200 个 bubble 元素 <li class="g-bubble"></li></ul>
</div>

CSS:

.g-bubbles {position: absolute;width: 30px;height: 30px;transform: translate(-50%, -50%);left: 50%;top: 50%;border-radius: 50px;
}
.g-bubble {position: absolute;inset: 0;border-radius: 50%;background: hsl(60, 75%, 40%);
}

得到这么一个效果,所有圆形小点,都暂时汇聚在容器的中心:

这里需要简单解释一下:

其次,我们借助 SASS,按照元素的顺序,把它们顺序排列到圆环轨迹之上:

$count: 200;
@for $i from 1 through $count { .g-bubble:nth-child(#{$i}) {--rotate: calc(#{360 / $count} * #{$i} * 1deg);transform: rotateZ(var(--rotate)) translate(135px, 0);opacity: 1;}
}

由于我们设置了 div 小球的个数为 200 个,这样,我们就得到了一圈由 200 个圆形小球形成的圆环:

接下来这一步非常重要,我们设定一个动画:

  1. 让每个小球在动画的 75% ~ 100% 阶段做透明度从 1 到 0 的变换,而 0% ~ 75% 的阶段保持透明度为 0
  2. 让 200 个 div 依次进行这个动画效果(利用负的 animation-delay,从 -0 到 -4000ms),整体上就能形成逐渐消失的效果
@for $i from 1 through $count { .g-bubble:nth-child(#{$i}) {--rotate: calc(#{360 / $count} * #{$i} * 1deg);--delayTime: calc(4000 * #{$i / $count} * -1ms);transform: rotateZ(var(--rotate)) translate(135px, 0);opacity: 1;animation: showAndHide 4000ms linear var(--delayTime) infinite;}
}
@keyframes showAndHide {0% {opacity: 0;}75% {opacity: 0;}75.1% {opacity: 1;}100% {opacity: 0;}
}

这样,我们就得到了一个圆形小球气泡围绕圆环渐次消失的效果:

配合上整个圆环,效果就会是这样:

很接近了,但是没有随机的感觉,气泡也没有散开的动画。解决的方案:

  1. 所以我们需要让气泡在执行透明度变化的同时,进行一个随机的发散位移
  2. 小圆形气泡的大小也可以带上一点随机,同时,在动画过程逐渐缩小

当然,整个动画的基础,还是在容器设置了 滤镜 blur() 和 contrast() 的加持之下的,这样,我们给气泡再补上随机动画散开及缩放的动画:

@for $i from 1 through $count { .g-bubble:nth-child(#{$i}) {--rotate: calc(#{360 / $count} * #{$i} * 1deg);--delayTime: calc(4000 * #{$i / $count} * -1ms);--scale: #{0.4 + random(10) / 10};--x: #{-100 + random(200)}px;--y: #{-100 + random(200)}px;transform: rotateZ(var(--rotate)) translate(135px, 0);opacity: 1;animation: showAndHide 4000ms linear var(--delayTime) infinite;}
}@keyframes showAndHide {0% {transform: rotateZ(var(--rotate)) translate(135px, 0);opacity: 0;}75% {opacity: 0;}75.1% {transform: rotateZ(var(--rotate)) translate(135px, 0)scale(var(--scale));opacity: 1;}100% {transform: rotateZ(var(--rotate)) translate(calc(135px + var(--x)), var(--y))scale(.2);opacity: 0;}
}

只看一圈的气泡圆形,我们能得到了这样的效果:

配合上圆环的效果:

配合上父容器的 filter: hue-rotate() 动画,就能实现颜色的动态变换,得到我们最终想要的效果:

这样,没有了第一版本的违和感,整个效果也显得比较自然。

整体代码:

HTML:

<div class="g-container"><div class="g-circle"></div><ul class="g-bubbles"><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li><li class="g-bubble"></li></ul>
</div>

CSS:

body, html {width: 100%;height: 100%;display: grid;place-content: center;background: #000;overflow: hidden;
}
$count: 200;:root {--headColor: hsl(130, 75%, 75%);--endColor: hsl(60, 75%, 40%);
}.g-container {position: relative;width: 300px;height: 300px;padding: 100px;filter: blur(3px) contrast(3);background: #000;animation: hueRotate 8s infinite linear;
}.g-circle {position: relative;width: 300px;height: 300px;border-radius: 50%;background: conic-gradient(var(--headColor) 0, var(--headColor) 2%,hsl(120, 75%, 70%), hsl(110, 75%, 65%), hsl(100, 75%, 60%),hsl(90, 75%, 55%), hsl(80, 75%, 50%),hsl(70, 75%, 45%),var(--endColor) 16%,var(--endColor) 18%,transparent 18%);mask: radial-gradient(transparent, transparent 119px, #000 120px, #000);-webkit-mask: radial-gradient(transparent, transparent 119px, #000 120px, #000);animation: rotate 4s infinite -700ms linear;&::before,&::after {content: "";position: absolute;inset: 0;width: 32px;height: 32px;background: var(--headColor);top: 0;left: 135px;border-radius: 50%;}&::after {background: var(--endColor);left: unset;top: 80px;right: 10px;}
}.g-bubbles {position: absolute;width: 30px;height: 30px;transform: translate(-50%, -50%);left: 50%;top: 50%;border-radius: 50px;
}.g-bubble {position: absolute;border-radius: 50%;background: var(--endColor);
}@for $i from 1 through $count { .g-bubble:nth-child(#{$i}) {--rotate: calc(#{360 / $count} * #{$i} * 1deg);--delayTime: calc(4000 * #{$i / $count} * -1ms);--width: 30px;--scale: #{0.4 + random(10) / 10};--x: #{-100 + random(200)}px;--y: #{-100 + random(200)}px;width: var(--width);height: var(--width);transform: rotateZ(var(--rotate)) translate(135px, 0);opacity: 1;animation: showAndHide 4000ms linear var(--delayTime) infinite;}
}@keyframes showAndHide {0% {transform: rotateZ(var(--rotate)) translate(135px, 0);opacity: 0;}75% {opacity: 0;}75.1% {transform: rotateZ(var(--rotate)) translate(135px, 0)scale(var(--scale));opacity: 1;}100% {transform: rotateZ(var(--rotate)) translate(calc(135px + var(--x)), var(--y))scale(.2);opacity: 0;}
}@keyframes rotate {100% {transform: rotate(-360deg);}
}@keyframes hueRotate {100% {filter: blur(3px) contrast(3) hue-rotate(360deg);}
}

本文转载于:

https://juejin.cn/post/7221320687430942781

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 


http://www.ppmy.cn/news/53179.html

相关文章

【机智云物联网低功耗转接板】+模拟无线远程控制仪表启停

GE211 是机智云自研的定制化的物联网转接板&#xff0c;使用 ESP32-C3-WROOM-02 通讯模块&#xff0c;适用于白色智能家电等设备应用。硬件设计上采用支持 WiFi 和 BLE 的双模无线通信模块&#xff0c;具备TTL电平转换串口&#xff0c;免开发快速接入&#xff0c;提高开发效率。…

前端开发之vue动态路由实现方案

前端开发之vue动态路由实现方案 前言2. 实现1、登录页面创建登录函数和watch监听路由2、调用的login方法&#xff08;1&#xff09;登录方法&#xff08;2&#xff09;存储token 3、router.push的时候调用路由守卫&#xff08;1&#xff09;创建路由守卫&#xff0c;建议路由守…

docker以外挂目录的方式部署springboot项目

前置准备&#xff1a; 清单&#xff1a; 安装有docker的Linuxspringboot打包的jar文件&#xff08;该项目只有一个返回"hello world"接口&#xff09; Linux的IP地址&#xff1a;192.168.221.129 springboot项目的接口&#xff1a; 1、上传jar文件至Linux 我上传…

Java有很多初学者常见的问题

作为一门流行的编程语言&#xff0c;Java有很多初学者常见的问题。以下是两个例子&#xff1a; 1.理解Java的数据类型 Java有不同的数据类型&#xff0c;包括整型、浮点型、布尔型等等。初学者可能会困惑如何选择合适的数据类型来存储他们的数据。例如&#xff0c;他们可能会…

thinkphp 的 whereIN() 、where IN 如何使用?

在 ThinkPHP 中&#xff0c;可以通过 whereIN 方法来构建 WHERE IN 子句。以下是 whereIN 方法的语法&#xff1a; $result Db::table(table_name)->whereIn(column, $array)->select();table_name&#xff1a;表示表名&#xff1b;column&#xff1a;表示要查询的列名…

最值得学的编程语言是哪个?

如果让我推荐的话&#xff0c;我肯定首选是python啦&#xff01; 编程语言是一个计算机的概念&#xff0c;在我们有了计算机以后&#xff0c;想让它帮助我们做事情&#xff0c;就要通过计算机语言和它进行对话、交互&#xff0c;计算机语言能够被计算机所执行&#xff0c;完成…

Java NIO原理机制详解

Java NIO原理机制 什么是NIO Java NIO&#xff08;New IO&#xff09;是Java 1.4版本引入的一个新的IO API&#xff0c;与传统的Java IO有着同样的作用和目的&#xff0c;但是使用方式完全不同。NIO支持面向缓冲区的、基于通道的IO操作&#xff0c;可以提供比传统IO更高效、更…

Git(六):基本命令(2):复位、修改、分支合并与日志

目录 9、reset 复位 9.1 描述 9.2 基本用法 9.2.1 回滚添加操作 9.2.2 回滚最近一次提交 9.2.3 回滚最近几次提交 9.2.4 回滚 pull 9.2.5 回滚 merge 9.2.6 区别 9.2.7 中断的工作流程处理 9.2.8 重置单独的文件 9.2.9 保留工作区并丢弃之前的提交 10、rm 删除 1…