实现一个网易云音乐的3D音效动画

1,817 阅读4分钟

前言

最近在网抑云听音乐时,偶然开启了3D动效,本身我就很喜欢网易云播放时类似唱片机的播放效果,增加了3D动效之后就听歌写代码都更high了!于是就想写个小小的demo来实现这个动画效果。

先贴一下最终效果

截屏2022-08-25 下午6.16.25.png 地址如下:codepen.io/youyunzhi/p…

实现

我们可以先将动画进行拆解,分为一下三部分

截屏2022-08-25 下午6.16.25.png

  1. 图片旋转
  2. 圆圈扩张&渐淡
  3. 圆圈的随机大小点

html

先贴一下html,整体比较简单。

 <div class="container">
  <div class="circle">
    <div class="circle-item">
      <div class="circle-single">
      </div>
    </div>
    <div class="circle-item">
      <div class="circle-single one"></div>
    </div>
    <div class="circle-item">
      <div class="circle-single two"></div>
    </div>
    <div class="circle-item">
      <div class="circle-single three"></div>
    </div>
    
    <div class="circle-img">
      <img src="https://static.stereogum.com/uploads/2022/08/22UMGIM87808.rgb_-1660881639.jpg" />
    </div>
  </div>
</div>
<div>

图片旋转

图片旋转的动画比较好写,在一定的时间内平滑地转一圈即可,此处我们使用的是css的animation。

   @keyframes revolve {
  25% {
    transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
.circle {
  width: 240px;
  height: 240px;
  /*   border: 1px solid #999; */
  border-radius: 50%;
  animation: 20s revolve linear infinite;
  position: relative;
}

中心图片

我们将circle理解为整体元素的一个容器,那么中心图片应该固定在容器的中心位置,我们需要使用一个面试常考的技能,垂直水平居中。此处使用绝对定位和margin来实现

    .circle-img {
      width: 160px;
      height: 160px;
      border-radius: 50%;
      position: absolute;
      top: 50%;
      left: 50%;
      margin: -80px 0 0 -80px;
    }

圆圈扩张&变淡

扩张

圆圈扩张的动画,我们使用div加圆角扩大宽度来实现,此处为了实现“波澜”的感觉,我们使用了多个div圆圈,并为其设置不同的动画延时。

变淡

变淡的部分主要是通过增加border的透明度来实现

此处需要注意的是,为了使动画有连续不断的感觉,我们需要让第一个圆圈进入第二次动画的时间刚好落后最后一个圆圈进入第一次动画一个单位延时。

如下我们设置圆圈一次动画的时长为4s,动画延时的一个单位为1s,那么第一个圆圈进入第二次动画的时间为第4秒,刚好落后最后一个圆圈进入第一次动画的时间(第3秒)一个单位延时。

@keyframes expand {
  0% {
    width: 160px;
    height: 160px;
    margin: -80px 0 0 -80px;
    border: 1px solid rgba(255, 182, 193, 1);
  }
  25% {
    width: 190px;
    height: 190px;
    margin: -95px 0 0 -95px;
    border: 1px solid rgba(255, 182, 193, 0.8);
  }
  50% {
    width: 220px;
    height: 220px;
    margin: -110px 0 0 -110px;
    border: 1px solid rgba(255, 182, 193, 0.6);
  }
  75% {
    width: 250px;
    height: 250px;
    margin: -125px 0 0 -125px;
    border: 1px solid rgba(255, 182, 193, 0.3);
  }
  100% {
    width: 280px;
    height: 280px;
    margin: -140px 0 0 -140px;
    border: 1px solid rgba(255, 182, 193, 0);
  }
}

.circle-item {
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 50%;
  animation: 4s expand linear infinite;
  border: 1px solid #ffc0cb;
}
.circle-item:nth-child(2) {
  animation: 4s expand 1s linear infinite;
}
.circle-item:nth-child(3) {
  animation: 4s expand 2s linear infinite;
}
.circle-item:nth-child(4) {
  animation: 4s expand 3s linear infinite;
}

圆圈的随机大小点

随机大小

我们知道js中有Math.random()函数,可以生成随机数,那么如何在css中生成随机数呢。此处,我们可以使用css变量和js的生成随机数的方法来实现。

.circle-singel { 
  --width: 10px; // 在css中定义变量
  width: var(--width);
  height: var(--width);
}
const singleItems = document.getElementsByClassName("circle-single");
Array.from(singleItems).forEach((item) => {
  const random = Math.random() * 0.5 + 0.5;
  item.style.setProperty("--width", random * 10 + 'px'); // js生成随机数,并更新变量的值
});

位置

我们需要圆点出现在圆圈上,并且圆圈刚好可以从圆点中心穿过。我们需要首先确定圆点的位置。我们知道,在设置圆圈div的50%圆角后,其四条边的中心点是可以确定的,我们此处选择是top这条。

圆点初始位置距离这个中心点应该是向左偏移50%父容器的宽度,那么我们设置圆点的左侧位置为calc(50% - var(--half--width)。而要实现从圆点中心穿过,则设置圆点top位置为--half--width即可。

.circle-single {
  --width: 10px;
  --half-width: -5px;
  width: var(--width);
  height: var(--width);
  border-radius: 50%;
  position: absolute;
  left: calc(50% - var(--half-width));
  top: var(--half-width);
}

渐淡

为了和圆圈适配渐淡效果,我们需要为圆点增加一个渐淡动画。

@keyframes fade {
  0% {
    background-color: rgba(255, 182, 193, 1);
  }
  25% {
    background-color: rgba(255, 182, 193, 0.8);
  }
  50% {
    background-color: rgba(255, 182, 193, 0.6);
  }
  75% {
    background-color: rgba(255, 182, 193, 0.3);
  }
  100% {
    background-color: rgba(255, 182, 193, 0);
  }
}

而为了匹配圆圈的延时,我们需要对圆点的减淡效果也增加延时

.one {
  animation: 4s fade 1s linear infinite;
}
.two {
  animation: 4s fade 2s linear infinite;
}
.three {
  animation: 4s fade 3s linear infinite;
}

总结

至此,我们就完成了一个简单的音乐播放3D动效,动画本身不难,但是我们需要去适配不同元素的动画时长和效果,所以需要仔细核对一下细节~以上。

最后

BlackPink回归的PINK VENOM各位都听了吗~