😎实现一个炫酷的霓虹灯轮盘展开菜单!

2,127 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

今天我们要来实现一个酷炫的霓虹灯轮盘展开菜单,先来看看它的效果吧

酷炫展开菜单动画.gif

实现思路

子菜单动画延迟

通过上面的效果图我们能够观察到,子菜单项是依次出现的,所以在动画上有一个延迟的效果,比如第一个子菜单项不延迟,第二个子菜单项延迟0.1s,第三个延迟0.2s...

子菜单旋转

可以看到的是每个子菜单在出现的时候是有一个旋转动画的,具体来说就是将子菜单容器元素旋转即可,但是为了保证图标的正常显示,我们应当把图标往容器元素旋转方向的反方向旋转相同角度,才能让图标的角度是正确的

图标颜色

每个图标都有自己的颜色,这个我们可以通过在元素上绑定css变量的方式实现

通过以上分析可以知道,为了给每个子菜单添加和他们顺序有关的动画延迟以及子菜单容器旋转角度和图标反向旋转角度,我们可以每个子菜单项在菜单容器中的下标来标记它们的顺序

记录菜单展开状态

在点击了菜单按钮后,此时菜单处于一个展开的状态,我们需要将这个状态记录下来,然后通过css中去处理展开状态下的样式,因此需要用到js去处理,展开的时候给菜单容器元素添加一个.expanded的类名

html结构

根据上面的分析,我们首先将菜单的页面结构写出来如下:

<ul id="menu" class="menu-container">
  <div id="toggler" class="toggler">
    <ion-icon name="add-outline"></ion-icon>
  </div>

  <li style="--idx: 0; --neon-color: #42b883">
    <a href="#">
      <ion-icon name="logo-vue"></ion-icon>
    </a>
  </li>

  <li style="--idx: 1; --neon-color: #61dafb">
    <a href="#">
      <ion-icon name="logo-react"></ion-icon>
    </a>
  </li>

  <li style="--idx: 2; --neon-color: #fcdc00">
    <a href="#">
      <ion-icon name="logo-javascript"></ion-icon>
    </a>
  </li>

  <li style="--idx: 3; --neon-color: #d6d6d6">
    <a href="#">
      <ion-icon name="logo-apple"></ion-icon>
    </a>
  </li>

  <li style="--idx: 4; --neon-color: #ff449f">
    <a href="#">
      <ion-icon name="logo-google"></ion-icon>
    </a>
  </li>

  <li style="--idx: 5; --neon-color: #07c160">
    <a href="#">
      <ion-icon name="logo-wechat"></ion-icon>
    </a>
  </li>

  <li style="--idx: 6; --neon-color: #7d88e3">
    <a href="#">
      <ion-icon name="logo-tiktok"></ion-icon>
    </a>
  </li>

  <li style="--idx: 7; --neon-color: #fff6cd">
    <a href="#">
      <ion-icon name="game-controller-outline"></ion-icon>
    </a>
  </li>
</ul>

这里我使用的图标来自ionicons,直接cdn导入后即可使用

通过在每个菜单项上绑定一个名为idxcss变量,就可以根据这个下标来控制每个元素的动画延迟以及旋转角度了

neon-color则是控制每个菜单的颜色的

js记录菜单展开状态

就如前面所说的,在点击展开菜单按钮后我们需要给菜单容器一个.expanded类名,表示当前处于展开状态

(() => {
  const oMenu = document.getElementById("menu");
  const oToggler = document.getElementById("toggler");

  const init = () => {
    bindEvent();
  };

  const bindEvent = () => {
    oToggler.addEventListener("click", () => {
      oMenu.classList.toggle("expanded");
    });
  };

  init();
})();

菜单按钮图标旋转动画

接下来就到了我们的css部分了,这个特效的核心在于css,首先我们来看一下菜单点击后,由加号变成叉号是怎么做到的

这个是利用rotate去实现的,在菜单expanded状态下让加号图标旋转一定角度即可

.menu-container .toggler {
  ...
  transition: transform 0.5s;
}

.menu-container.expanded .toggler {
  transform: rotate(315deg);
}

酷炫展开菜单动画.gif

子菜单旋转动画

每个子菜单项依次旋转出现,这个动画则主要是利用了rotate以及translateX结合时形成的曲线动画效果实现的,具体代码如下

/* 菜单未展开时 */
.menu-container li {
  position: absolute;
  list-style: none;
  transition: transform 0.5s;
  transition-delay: calc(0.1s * var(--idx));
}

/* 让图标反向旋转相同角度回正 */
.menu-container li a {
  transform: rotate(calc(360deg / -8 * var(--idx)));
}

/* 菜单展开时 */
.menu-container.expanded li {
  transform: rotate(calc(360deg / 8 * var(--idx))) translateX(120px);
}

translateX让子菜单项往外运动,而rotate又让子菜单项旋转起来,这两个效果结合在一起就形成了环形运动的效果,再配合上transition-delay的动画延迟,最终效果如下

酷炫展开菜单动画.gif

子菜单霓虹灯特效

这个则主要是通过box-shadow实现,通过多层阴影的叠加实现

.menu-container.expanded li a:hover {
  color: #06283d;
  background-color: var(--neon-color);
  box-shadow: 0 0 30px var(--neon-color), 0 0 50px var(--neon-color),
    0 0 100px var(--neon-color);
}

效果如下:

酷炫展开菜单动画.gif

但是好像和封面的效果图不一样,少了点什么呢?

目前我们鼠标快速在这些子菜单上移动的时候,会出现霓虹灯还没亮起来,鼠标就移到下一个菜单项,导致看不到封面图中的那种跑马灯一样的效果,那这个问题要怎么解决呢?

很明显,我们想让霓虹灯亮起来的话应当是鼠标放上去就立刻亮起来,而鼠标离开时则慢慢变暗才对,那么我们就在hover的时候将transition变为0即可,这样鼠标一放上来,霓虹灯就立刻亮起

.menu-container.expanded li a:hover {
  color: #06283d;
  background-color: var(--neon-color);
  box-shadow: 0 0 30px var(--neon-color), 0 0 50px var(--neon-color),
    0 0 100px var(--neon-color);
+ transition: 0s;
}

酷炫展开菜单动画.gif

到此为止我们的霓虹灯轮盘展开菜单就实现完啦!