vue3 弧形菜单构思设计

137 阅读2分钟

1 思路

  • 菜单列表纵向排列,每一项菜单文字根据背景进行旋转
  • 菜单跳转时,整体旋转

2 代码

因为背景曲线扁平,所以下面style中每一项文字旋转和右边距仅仅进行了视觉上简单拟合,并没有进行严谨的拟合,参数可以自行修改

<template>
  <img class="menu-icon" src="@/assets/image/menu.png" @click="handleMenuStatus" />
  <div class="menu-wapper" :class="{ hidden: hideMenu }">
    <div
      class="menu-list"
      :style="{ transformOrigin: '-1100px 50%', transform: `rotate(${angle}deg)` }"
    >
      <div
        v-for="(item, index) in menuList"
        :key="item.id"
        class="menu-item"
        :class="{ active: baseIndex === index }"
        :style="{
          transform: `rotate(${(index + 1 - Math.ceil(menuList.length / 2)) * 3}deg)`,
          marginRight: `${
            Math.pow(Math.abs(index + 1 - Math.ceil(menuList.length / 2)), 2) * 1.55
          }px`,
        }"
        @click="handleSwitchMenu(item)"
      >
        <span class="menu-text"> {{ item.name }}</span>
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
const router = useRouter();
const route = useRoute();
 
const menuList = ref([
  {
    id: 1,
    name: '平台管理',
    path: '/menu1',
  },
  {
    id: 2,
    name: '人员管理',
    path: '/menu2',
  },
  {
    id: 3,
    name: '信息管理',
    path: '/menu3',
  },
  {
    id: 4,
    name: '菜单管理',
    path: '/menu4',
  },
  {
    id: 5,
    name: '权限管理',
    path: '/menu5',
  },
  {
    id: 6,
    name: '资产管理',
    path: '/menu6',
  },
  {
    id: 7,
    name: '系统管理',
    path: '/menu7',
  },
]);
const angle = ref(0);
const curMenu = ref();
const hideMenu = ref(false);
const baseIndex = ref(Math.floor(menuList.value.length / 2));
 
onMounted(async () => {
  curMenu.value = menuList.value.find(item => item.path === route.path);
  handleSwitchMenu(curMenu.value);
});
 
const handleMenuStatus = () => {
  hideMenu.value = !hideMenu.value;
};
 
const handleSwitchMenu = async (menu: any) => {
  const selectedIndex = menuList.value.findIndex(item => item.id === menu.id);
  angle.value += -(selectedIndex - baseIndex.value) * 2.75;
  baseIndex.value = selectedIndex;
  router.push({
    path: menu.path,
  });
};
</script>
<style lang="scss" scoped>
.menu-icon {
  position: fixed;
  z-index: 600;
  left: 0.5%;
  top: 50%;
  cursor: pointer;
  width: 20px;
  height: 20px;
}
 
.menu-wapper {
  position: fixed;
  left: 0;
  bottom: 5%;
  width: 240px;
  height: 80%;
  background: url('@/assets/image/outline.png') center no-repeat;
  background-size: 80% 100%;
  z-index: 200;
  transition: all 0.5s ease-in-out;
  transform-origin: -1100px 50%;
 
  &.hidden {
    transform-origin: -1100px 50%;
    transform: rotate(-60deg);
    opacity: 0;
 
    &:hover {
      transform: rotate(0deg);
      opacity: 1;
    }
  }
 
  .menu-list {
    width: 100%;
    height: 100%;
    padding-right: 48px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-end;
    transition: all 0.5s ease-in-out;
 
    .menu-item {
      height: 35px;
      width: 100px;
      margin-top: 20px;
      font-size: 20px;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
 
      &.active {
        border-radius: 20px;
        border: 2px solid #f3da94;
        &:before {
          content: '';
          width: 4px;
          height: 4px;
          position: absolute;
          top: 40%;
          left: -10px;
          background-color: #f3da94;
          border-radius: 50%;
        }
        &:after {
          content: '';
          width: 4px;
          height: 4px;
          position: absolute;
          top: 40%;
          right: -10px;
          background-color: #f3da94;
          border-radius: 50%;
        }
        .menu-text {
          font-weight: bold;
        }
      }
 
      .menu-text {
        color: #d7e4f1;
      }
    }
  }
}
</style>

3 效果

image.png

4 改进

为了使得选中菜单并与菜单icon在一条水平线上,可以进行如下方法改进:

1 获取背景曲线的半径(假设是圆周的一部分),改变transform-origin参数,计算菜单选中时需要偏转的度数

2 直接以贝塞尔三次曲线拟合背景曲线,修改相关参数