最近业务场景需要实现一个扫描状态的动效(波纹渐变动效),顺利上线后,在这里将完整的实现过程和大家分享一下。 下面是最终实现:

下面让我们一起看下,这样的动效是怎样实现的吧~
一、实现中间的动效

这部分由5个圆构成,主要利用css的animation、Transform、scale、opacity等属性实现。
具体实现
5个圆以及动效
1.核心圆渐变
最中间的橙色圆背景使用渐变色,linear-gradient() 函数用于创建一个表示两种或多种颜色的线性渐变
background: linear-gradient(direction, color-stop1, color-stop2, ...);
| 值 | 描述 |
|---|---|
| direction | 用角度值指定渐变的方向(或角度)。 |
| color-stop1, color-stop2, ... | 指定渐变的起止颜色 |
实现橙色渐变:linear-gradient(90deg,rgba(255,195,139,1.000) ,rgba(255,148,86,1.000))

2.圆的动态扩散
外层的四个圆,使用animation、Transform、scale、opacity来共同实现扩散、渐隐的动画效果。
1.animation-name 作为CSS 属性,可以指定一个或多个 @keyframes at-rule 的名称,这些 at-rule 描述了要应用于元素的动画。多个 @keyframes at-rule 以逗号分隔的名称列表的形式指定。如果指定的名称不匹配任何 @keyframes at-rule,则不会对任何属性进行动画处理。
animation: name duration timing-function delay iteration-count direction;
| 值 | 描述 |
|---|---|
| animation-name | 规定需要绑定到选择器的 keyframe 名称。 |
| animation-duration | 规定完成动画所花费的时间,以秒或毫秒计。 |
| animation-timing-function | 规定动画的速度曲线。 |
| animation-delay | 规定在动画开始之前的延迟。 |
| animation-iteration-count | 规定动画应该播放的次数。 |
| animation-direction | 规定是否应该轮流反向播放动画。 |
| animation-fill-mode | 规定动画在执行时间之外应用的值。 |
| animation-play-state | 规定动画是正在运行还是暂停。 |
animation-name: circleChange1;
@keyframes circleChange1{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1);opacity: 0.75;}
50%{transform: scale(1);opacity: 0.5;}
75%{transform: scale(1);opacity: 0.25;}
100%{transform: scale(1);opacity: 0.75;}
}
通过 @keyframes 规则,可以创建动画。原理是将一套 CSS 样式逐渐变化为另一套样式。 在动画过程中,可以多次改变这套 CSS 样式。 以百分比来规定改变发生的时间,或者通过关键词 "from" 和 "to",等价于 0% 和 100%。 0% 是动画的开始时间,100% 动画的结束时间。
2.Transform属性应用于元素的2D或3D转换。这个属性允许你将元素旋转,缩放,移动,倾斜等。 transform: none|transform-functions;
| 值 | 描述 |
|---|---|
| none | 定义不进行转换。 |
| translate(x,y) | 定义 2D 转换。 |
| translate3d(x,y,z) | 定义 3D 转换。 |
| translateX(x) | 定义转换,只是用 X 轴的值。 |
| translateY(y) | 定义转换,只是用 Y 轴的值。 |
| translateZ(z) | 定义 3D 转换,只是用 Z 轴的值。 |
| scale(x[,y]?) | 定义 2D 缩放转换。 |
| scaleX(x) | 通过设置 X 轴的值来定义缩放转换。 |
| scaleY(y) | 通过设置 Y 轴的值来定义缩放转换。 |
| scaleZ(z) | 通过设置 Z 轴的值来定义 3D 缩放转换。 |
想要实现圆若隐如现的效果,则需要控制圆的透明度opacity。

整体代码:
html部分
<div class="circleBox">
<div class="circle">
<div class="scanning">
<div>扫描中...</div>
<div>80%</div>
</div>
</div>
<div class="circleOne"></div>
<div class="circleTwo"></div>
<div class="circleThree"></div>
<div class="circleFour"></div>
</div>
css部分
.circleBox {
position: relative;
width: 250px;
height: 180px;
}
.circle {
background: linear-gradient(90deg,rgba(255,195,139,1.000) ,rgba(255,148,86,1.000));
border-radius: 50%;
position: absolute;
z-index: 99;
width: 140px;
height: 140px;
top: 26px;
left: 27px;
color: #ffffff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
}
.circleOne {
background: #fbe4d4;;
border-radius: 50%;
position: absolute;
z-index: 88;
animation-name: circleChange1;
width: 190px;
height: 190px;
animation-delay: 1s;
}
.circleTwo {
border: 1px solid #f19b5d ;
border-radius: 50%;
position: absolute;
z-index: 77;
animation-delay: 2s;
animation-name: circleChange2;
}
.circleThree {
border: 1px solid #f8b283 ;
border-radius: 50%;
position: absolute;
z-index: 66;
animation-delay: 3s;
animation-name: circleChange3;
}
.circleFour {
border: 2px dotted #f3ac79 ;
border-radius: 50%;
position: absolute;
animation-delay: 4s;
z-index: 55;
animation-name: circleChange4;
}
.circleTwo, .circleThree, .circleFour {
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
width: 190px;
height: 190px;
}
@keyframes circleChange{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1.2);opacity: 0.75;}
50%{transform: scale(1.4);opacity: 0.5;}
75%{transform: scale(1.6);opacity: 0.25;}
100%{transform: scale(1.8);opacity: 0.05;}
}
@keyframes circleChange1{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1);opacity: 0.75;}
50%{transform: scale(1);opacity: 0.5;}
75%{transform: scale(1);opacity: 0.25;}
100%{transform: scale(1);opacity: 0.75;}
}
@keyframes circleChange2{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1.2);opacity: 0.75;}
50%{transform: scale(1.4);opacity: 0.5;}
75%{transform: scale(1.6);opacity: 0.25;}
100%{transform: scale(1.8);opacity: 0.05;}
}
@keyframes circleChange3{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1.2);opacity: 0.75;}
50%{transform: scale(1.4);opacity: 0.5;}
75%{transform: scale(1.6);opacity: 0.25;}
100%{transform: scale(1.8);opacity: 0.05;}
}
@keyframes circleChange4{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1.2);opacity: 0.75;}
50%{transform: scale(1.4);opacity: 0.5;}
75%{transform: scale(1.6);opacity: 0.25;}
100%{transform: scale(1.8);opacity: 0.05;}
}
二、左右两侧扩散动效
1.左右两侧,分别由两个半圆组成
这个视觉上的半圆,本质上是一个圆的一半(控制width,height的值来决定弧度),通过控制background,让圆从左到右粉色渐变为白色,需要特别注意的是,如果我们的扫描动效背景不是白色,那么需要处理渐变为从粉色到透明:
linear-gradient(90deg, rgb(255, 237, 227, 0.6), rgb(255, 255, 255, 0.01), rgb(255, 255, 255, 0.01));

如果不处理的话,会出现这种情况:

2.控制半圆左右扩散和渐隐
和核心圆的实现类似,通过animation实现动画效果,animation-delay设置不同的值,控制动态波动
animation-name: outLeftAnimation;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-delay: 3
@keyframes outLeftAnimation{
0%{left: 30px;opacity: 0.05;}
50%{left: -5px;opacity: 1;}
100%{left: -30px;opacity: 0.05;}
}
到目前为止,我们可以实现这样的效果啦

我们也可以在边线上,挂上一些扫描文案:
<div class="ballAll ballEight">
<div class="ballEightText">扫描点一</div>
</div>
.ballEight{
top: 31px;
position: absolute;
left: 30px;
}
.ballEightText{
width: 70px;
margin-top: 10px;
margin-left: -23px;
}
这就是最终效果啦~

看看动起来的效果

三、全部代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>扫描动效</title>
<style type="text/css">
.circleBox {
position: relative;
width: 250px;
height: 180px;
}
/* 扩散动画 */
.circle {
background: linear-gradient(90deg,rgba(255,195,139,1.000) ,rgba(255,148,86,1.000));
border-radius: 50%;
position: absolute;
z-index: 99;
width: 140px;
height: 140px;
top: 26px;
left: 27px;
color: #ffffff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
}
.circleOne {
background: #fbe4d4;;
border-radius: 50%;
position: absolute;
z-index: 88;
animation-name: circleChange1;
width: 190px;
height: 190px;
animation-delay: 1s;
}
.circleTwo {
border: 1px solid #f19b5d ;
border-radius: 50%;
position: absolute;
z-index: 77;
animation-delay: 2s;
animation-name: circleChange2;
}
.circleThree {
border: 1px solid #f8b283 ;
border-radius: 50%;
position: absolute;
z-index: 66;
animation-delay: 3s;
animation-name: circleChange3;
}
.circleFour {
border: 2px dotted #f3ac79 ;
border-radius: 50%;
position: absolute;
animation-delay: 4s;
z-index: 55;
animation-name: circleChange4;
}
.circleTwo, .circleThree, .circleFour {
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
width: 190px;
height: 190px;
}
@keyframes circleChange{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1.2);opacity: 0.75;}
50%{transform: scale(1.4);opacity: 0.5;}
75%{transform: scale(1.6);opacity: 0.25;}
100%{transform: scale(1.8);opacity: 0.05;}
}
@keyframes circleChange1{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1);opacity: 0.75;}
50%{transform: scale(1);opacity: 0.5;}
75%{transform: scale(1);opacity: 0.25;}
100%{transform: scale(1);opacity: 0.75;}
}
@keyframes circleChange2{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1.2);opacity: 0.75;}
50%{transform: scale(1.4);opacity: 0.5;}
75%{transform: scale(1.6);opacity: 0.25;}
100%{transform: scale(1.8);opacity: 0.05;}
}
@keyframes circleChange3{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1.2);opacity: 0.75;}
50%{transform: scale(1.4);opacity: 0.5;}
75%{transform: scale(1.6);opacity: 0.25;}
100%{transform: scale(1.8);opacity: 0.05;}
}
@keyframes circleChange4{
0%{transform: scale(1);opacity: 1;}
25%{transform: scale(1.2);opacity: 0.75;}
50%{transform: scale(1.4);opacity: 0.5;}
75%{transform: scale(1.6);opacity: 0.25;}
100%{transform: scale(1.8);opacity: 0.05;}
}
.circleLeft{
width: 250px;
height: 259px;
position: relative;
left: 60px;
border-left: 1px solid #FFB87F;
border-radius: 50%;
background: linear-gradient(90deg, rgb(255, 237, 227,1), rgb(255, 255, 255, 0.01),rgb(255, 255, 255, 0.01));
}
.circleRight{
width: 250px;
height: 259px;
right: 125px;
position: relative;
border-right: 1px solid #FFB87F;
border-radius: 50%;
background: linear-gradient(90deg,rgba(255,255,255,0.01),rgba(255,255,255,0.01), rgba(255,237,227,1));
}
.circle-all{
display: flex;
align-items: center;
justify-content: space-between;
height: 400px;
width: 600px;
}
.ballAll{
height: 10px;
width: 10px;
background-color: #FF9456;
border-radius: 50%;
z-index: 6;
color: #999;
}
.ballOne{
top: 58px;
position: absolute;
left: 73px;
}
.ballTwo{
top: 122px;
position: absolute;
left: 56px;
}
.ballThree{
top: 195px;
position: absolute;
left: 75px;
}
.ballAndCircleLeft{
position: relative;
z-index: 9;
width: 200px;
}
.ballAndCircleLeftActive{
animation-name: ballAndCircleLeftAnimation;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-delay: 3s;
}
.ballAndCircleRight{
position: relative;
z-index: 9;
width: 200px;
}
.ballAndCircleRightActive{
animation-name: ballAndCircleRightAnimation;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-delay: 3s;
}
.ballFour{
top: 14px;
position: absolute;
right: 128px;
}
.ballFive{
top: 71px;
position: absolute;
right: 81px;
}
.ballSix{
top: 140px;
position: absolute;
right: 71px;
}
.ballSeven{
top: 222px;
position: absolute;
right: 111px;
}
.ballEight{
top: 31px;
position: absolute;
left: 30px;
}
.ballNine{
top: 172px;
position: absolute;
left: 3px;
}
@keyframes ballAndCircleLeftAnimation{
0%{left: 20px;opacity: 0.05;}
50%{left: 0px;opacity: 1;}
100%{left: -20px;opacity: 0.05;}
}
@keyframes ballAndCircleRightAnimation{
0%{right: 20px;opacity: 0.05;}
50%{right: 0px;opacity: 1;}
100%{right: -20px;opacity: 0.05;}
}
.outLeft{
width: 250px;
height: 259px;
position: absolute;
border-left: 1px solid #FFB87F;
border-radius: 50%;
z-index: 5;
left: -5px;
background: linear-gradient(90deg, rgb(255, 237, 227, 0.6), rgb(255, 255, 255, 0.01), rgb(255, 255, 255, 0.01));
}
.outLeftActive{
animation-name: outLeftAnimation;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-delay: 3s;
}
.outRight{
width: 250px;
height: 259px;
z-index: 5;
right: 14px;
position: absolute;
border-right: 1px solid #FFB87F;
border-radius: 50%;
background: linear-gradient(90deg,rgba(255,255,255,0.01),rgba(255,255,255,0.01), rgba(255,237,227,0.6) );
}
.outRightActive{
animation-name: outRightAnimation;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-delay: 3s;
}
@keyframes outLeftAnimation{
0%{left: 30px;opacity: 0.05;}
50%{left: -5px;opacity: 1;}
100%{left: -30px;opacity: 0.05;}
}
@keyframes outRightAnimation{
0%{right: 40px;opacity: 0.05;}
50%{right: 20px;opacity: 1;}
100%{right: -30px;opacity: 0.05;}
}
.check-inner{
height: 100px;
width: 100px;
border-radius: 50%;
border: 1px solid #FFC49A;
color: #FF8040;
font-size: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fff !important;
}
.check-outer{
height: 120px;
width: 120px;
border-radius: 50%;
border: 1px dashed #E0E0E0;
display: flex;
align-items: center;
justify-content: center;
margin-left: -25px;
margin-top: -10px;
}
.check-text-account{
color:#999999;
position: absolute;
top: -52px;
}
.check-text-risk{
color:#999999;
position:absolute;
left: 17px;
top: -13px;
}
.check-text-analysis{
color:#999999;
position: absolute;
top: 25px;
}
.ballOneText{
width: 70px;
margin-top: 10px;
margin-left: -28px;
}
.ballTwoText{
width: 130px;
margin-top: 10px;
margin-left: -52px;
}
.ballThreeText{
width: 70px;
margin-top: 10px;
margin-left: -28px;
}
.ballFourText{
width: 72px;
margin-top: 10px;
margin-left: -20px;
}
.ballFiveText{
width: 113px;
margin-top: 10px;
margin-left: -53px;
}
.ballSixText{
width: 70px;
margin-top: 10px;
margin-left: -23px;
}
.ballSevenText{
width: 120px;
margin-top: 10px;
margin-left: -38px;
}
.ballEightText{
width: 70px;
margin-top: 10px;
margin-left: -23px;
}
.ballNineText{
width: 110px;
margin-top: 10px;
margin-left: -23px;
}
.scanning-mask{
display: flex;
position: absolute;
flex-direction: column;
justify-content: center;
align-items: center;
top: 1px;
width: 100%;
height: calc(100vh - 160px);
background: rgba(255,255,255,0.9);
margin-top: 30px;
}
.settleAccountsClassNameScan{
width: 100px;
height: 100px;
border-radius: 50px;
border: 0px;
}
.unSettleAccountsClassNameScan{
width: 100px;
height: 100px;
border-radius: 50px;
border: 0px;
}
.scanning{
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
</style>
</head>
<body>
<div style="margin-left: 200px;">
<div class="circle-all">
<div class="ballAndCircleLeft ballAndCircleLeftActive" >
<div class="outLeft outLeftActive">
<div class="ballAll ballEight">
<div class="ballEightText">扫描点一</div>
</div>
<div class="ballAll ballNine">
<div class="ballNineText">扫描点二</div>
</div>
</div>
<div class="ballAll ballOne">
<div class="ballOneText">扫描点三</div>
</div>
<div class="ballAll ballTwo">
<div class="ballTwoText">扫描点四</div>
</div>
<div class="ballAll ballThree">
<div class="ballThreeText">扫描点五</div>
</div>
<div class="circleLeft"></div>
</div>
<div class="circleBox">
<div class="circle">
<div class="scanning">
<div>扫描中...</div>
<div>80%</div>
</div>
</div>
<div class="circleOne"></div>
<div class="circleTwo"></div>
<div class="circleThree"></div>
<div class="circleFour"></div>
</div>
<div class="ballAndCircleRight ballAndCircleRightActive">
<div class="outRight outRightActive"></div>
<div class="ballAll ballFour">
<div class="ballFourText">扫描点六</div>
</div>
<div class="ballAll ballFive">
<div class="ballFiveText">扫描点七扫描点</div>
</div>
<div class="ballAll ballSix">
<div class="ballSixText">扫描点八</div>
</div>
<div class="ballAll ballSeven">
<div class="ballSevenText">扫描点九</div>
</div>
<div class="circleRight"></div>
</div>
</div>
</div>
</body>
</html>
希望本文对你有所帮助~~~
CSS实现扫描动态波效的方法
1315





