我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第8篇文章,点击查看活动详情
AirDrop 效果介绍
用过 Mac 电脑的同学都知道 AirDrop 可以在不同电脑和手机设备之间共享文件,功能非常强大,如下图所示:
我们今天来研究一下 AirDrop 视觉效果,带领大家一步步在网页中实现它。整个背景是一圈圈的圆形轨道,类似于行星围绕着太阳旋转,当发现新设备后,就会在轨道上展示该设备,并附加波纹扩散动效:
AirDrop 效果的本质
如果把上面的图中的文案和图片全部去掉之后,你会发现剩下的全都是同心圆,所以要做 AirDrop 效果,只要能做到以下三点即可:
- 实现同心圆
- 实现切角同心圆
- 实现同心圆扩散效果
从零写一个AirDrop效果
实现同心圆
最简单的实现思路就是:有多少个圆我就写多少个 div,然后把它们给叠加起来。这种方式也没毛病,假设有 4 个同心圆,最里面的圆形半径是 100px,我们直接看代码,其中 HTML 结构很简单:
<body>
<div class="circle-center">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
</body>
CSS 代码如下:
.circle-center {
--radius: 100px;
--diameter: calc(2 * var(--radius));
position: fixed;
bottom: calc(20px + var(--radius));
left: 50%;
width: 0;
height: 0;
}
.circle {
position: absolute;
top: 0;
left: 0;
transform: translateX(-50%) translateY(-50%);
border: 1px solid #ddd;
border-radius: 50%;
box-sizing: border-box;
}
.circle:nth-child(1) {
width: var(--diameter);
height: var(--diameter);
}
.circle:nth-child(2) {
width: calc(2 * var(--diameter));
height: calc(2 *var(--diameter));
}
.circle:nth-child(3) {
width: calc(3*var(--diameter));
height: calc(3*var(--diameter));
}
.circle:nth-child(4) {
width: calc(4*var(--diameter));
height: calc(4*var(--diameter));
}
这里用到了 CSS 变量,可以非常方便的修改半径来调整同心圆间距。我们看下效果:
是不是有那么一点感觉了?
实现切角同心圆
在 airdrop 的中心有一个缺角的红蓝相间的同心圆,然后把底部大约30度的扇形区块给切掉即可。接下来我们就来实现它。
有了第一步的基础,画一个红蓝相间的同心圆已经非常简单了,只需要去掉 border,添加 background-color 即可,大家可以自己尝试一下。
但是这里介绍另外一种利用 box-shadow 画同心圆的办法,只需要一个 div 即可。我们在 circle-center 里面增加 circle-ring 和 circle-text 两个节点分别用于创建切角同心圆和下面的文字:
<div class="circle-center">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle-ring"></div>
<div class="circle-text">
<div class="text-intro">”隔空投送“可让您与附近的用户立即共享</div>
<div class="text-setting">允许这些人发现我:所有人 <img src="./arrow-down.svg" /></div>
</div>
</div>
CSS 代码如下:
.circle-ring {
--ring-width: 90px;
--border-width: 6px;
--gutter-width: 7px;
--color: #286ad3;
position: absolute;
width: var(--ring-width);
height: var(--ring-width);
top: calc(var(--ring-width) / -2);
left: calc(var(--ring-width) / -2);
box-sizing: border-box;
border-radius: 50%;
background: var(--color);
box-shadow: inset 0 0 0 var(--border-width) var(--color),
inset 0 0 0 calc(var(--border-width) + var(--gutter-width)) white,
inset 0 0 0 calc(2 * var(--border-width) + var(--gutter-width)) var(--color),
inset 0 0 0 calc(2 * var(--border-width) + 2 * var(--gutter-width)) white,
inset 0 0 0 calc(3 * var(--border-width) + 2 * var(--gutter-width)) var(--color),
inset 0 0 0 calc(3 * var(--border-width) + 3 * var(--gutter-width)) white;
}
这里定义了一个半径为 100px 的圆,然后利用 box-shadow 添加蓝白相间的多个内阴影,其中 --border-width
表示蓝色边框的宽度,--gutter-width
表示白色间距的宽度,做成 CSS 变量也是方便调整,看下效果吧:
接下来我们得把底部做一个豁口才行,也有两种实现思路:
- 利用 clip-path 切割图形
- 用一个 div 画一个白色的扇形遮挡圆形底部
这里选择更为强大的 clip-path 来实现,只需要一行代码即可:
clip-path: polygon(45% 60%, 20% 100%, 0% 100%, 0 0, 100% 0, 100% 100%, 80% 100%, 55% 60%);
裁剪后的效果如下:
clip-path 的语法不做详细介绍,感兴趣的读者可以自行 Google 查询,它可以裁剪矩形、圆形、椭圆、多边形甚至指定的路径:
.div {
clip-path: inset(100px 50px);
clip-path: circle(50px at 0 100px);
clip-path: ellipse(50px 60px at 0 10% 20%);
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
clip-path: path('M15,45 A30,30,0,0,1,75,45 A30,30,0,0,1,135,45 Q135,90,75,130 Q15,90,15,45 Z');
clip-path: url(#myPath);
}
最常用的使用场景就是裁剪图片,大家可以前往 css-tricks 网站体验,里面提供了一个非常好用的 demo,可以非常直观的看到 clip-path 属性的效果:
最后稍作调整,补上文字的样式:
.circle-text {
position: absolute;
top: 70px;
left: 0;
width: 400px;
transform: translateX(-50%);
text-align: center;
font-size: 13px;
}
最终实现的页面已经跟 Mac 自带的非常接近了:
实现同心圆扩散效果
当 airdrop 发现设备的时候,会把设备放在同心圆的轨道上,也显示成一个圆形,而且呈现雷达扩散效果,这个应该怎么实现呢?
首先我们完善一下 HTML 结构,增加 wave 波动容器:
<div class="circle-center">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle-ring"></div>
<div class="circle-text">
<div class="text-intro">”隔空投送“可让您与附近的用户立即共享</div>
<div class="text-setting">允许这些人发现我:所有人 <img src="./arrow-down.svg" /></div>
</div>
<div class="wave active">
<div class="wave-avatar"></div>
<div class="wave-animation">
<div class="wave-circle"></div>
<div class="wave-circle1"></div>
<div class="wave-circle2"></div>
<div class="wave-circle3"></div>
</div>
<div class="wave-text">iPhone14</div>
</div>
</div>
其中:
- wave 是设备容器
- wave-avatar 用于放置中间的头像
- wave-animation 用于实现波动效果
用 --wave-size
变量来表示设备容器的宽度,同样也是参照 circle-center 进行绝对定位,wave 容器的 CSS 代码如下:
.wave {
--wave-size: 90px;
position: absolute;
top: -300px;
width: var(--wave-size);
height: var(--wave-size);
position: relative;
text-align: center;
overflow: visible;
transform: translate(calc(var(--wave-size) / -2), calc(var(--wave-size) / -2));
}
wave-avatar 就非常简单了,依然采用绝对定位放置头像:
.wave-avatar {
border-radius: 50%;
position: absolute;
width: var(--wave-size);
height: var(--wave-size);
top: 0;
left: 0;
z-index: 2;
display: block;
background-image: url(avatar.svg);
background-size: cover;
}
最难实现的是 wave-animation 向外扩散的效果,我们先把容器准备好,初始状态是 4 个重叠在一起的 div,默认是没有动效的,当给 wave 增加 active 属性之后再让它动起来:
.wave-animation {
position: absolute;
width: var(--wave-size);
height: var(--wave-size);
top: 0;
left: 0;
display: none;
}
.wave-animation div {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.wave.active .wave-animation {
display: block;
}
最关键的一步来了,我们嵌套了 4 个重叠的圆,其中1个是白色背景,剩下3个是灰色背景,并且加了 circleChange 扩散动效,先贴代码:
.wave-circle,
.wave-circle1,
.wave-circle2,
.wave-circle3 {
border-radius: 50%;
}
.wave-circle {
background-color: white;
z-index: 1;
}
.wave-circle1,
.wave-circle2,
.wave-circle3 {
background-color: #efefef;
animation-name: circleChange;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
.wave-circle1 {
animation-delay: 0s;
}
.wave-circle2 {
animation-delay: 1s;
}
.wave-circle3 {
animation-delay: 2s;
}
@keyframes circleChange {
0% {
transform: scale(1);
opacity: 0.95;
}
25% {
transform: scale(1.4);
opacity: 0.75;
}
50% {
transform: scale(1.8);
opacity: 0.5;
}
75% {
transform: scale(2.2);
opacity: 0.25;
}
100% {
transform: scale(2.6);
opacity: 0.05;
}
}
从单个圆的角度来看,是利用 scale 和 opacity 来做一个跟随时间放大且渐变的效果,如下图所示:
但是有 3 个圆同时播放这个动画,并且依次延迟 0 秒、1 秒和 2 秒,最终就形成了向外渐变扩散的效果。最后再补上文案样式:
.wave-text {
position: absolute;
bottom: -30px;
width: 100%;
}
我们最终的效果就出来啦:
动图效果如下:
怎么样,有没有一种丝滑的感觉?到此为止,我们的网页版 AirDrop 就完成啦,欢迎大家点赞、评论、分享!