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 效果
4 改进
为了使得选中菜单并与菜单icon在一条水平线上,可以进行如下方法改进:
- 获取背景曲线的半径(假设是圆周的一部分),改变transform-origin参数,计算菜单选中时需要偏转的度数
- 直接以贝塞尔三次曲线拟合背景曲线,修改相关参数