vue实现扇形菜单
实现扇形菜单的思路
扇形菜单通常以圆形或扇形布局展示多个菜单项,可以通过CSS和Vue的动态渲染实现。关键在于计算每个菜单项的位置角度,并使用CSS的transform属性进行旋转定位。
基础HTML结构
在Vue组件中,使用v-for动态渲染菜单项,并为每个菜单项绑定旋转角度和位置样式。
<template>
<div class="sector-menu-container">
<div
v-for="(item, index) in menuItems"
:key="index"
class="menu-item"
:style="getItemStyle(index)"
@click="handleClick(item)"
>
{{ item.label }}
</div>
</div>
</template>
CSS样式设计
通过CSS设置菜单项的初始位置和旋转效果。核心是利用transform-origin和rotate实现扇形布局。
.sector-menu-container {
position: relative;
width: 300px;
height: 300px;
margin: 0 auto;
}
.menu-item {
position: absolute;
width: 60px;
height: 60px;
background: #42b983;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
cursor: pointer;
transform-origin: center center;
left: 50%;
top: 50%;
margin-left: -30px;
margin-top: -30px;
}
计算菜单项位置
在Vue的methods中定义计算每个菜单项旋转角度的函数,根据索引和总菜单项数均匀分配角度。
export default {
data() {
return {
menuItems: [
{ label: 'Home', action: 'home' },
{ label: 'About', action: 'about' },
{ label: 'Contact', action: 'contact' },
{ label: 'Settings', action: 'settings' },
{ label: 'Help', action: 'help' }
],
radius: 100 // 扇形半径
};
},
methods: {
getItemStyle(index) {
const angle = (360 / this.menuItems.length) * index;
const radian = (angle * Math.PI) / 180;
const x = this.radius * Math.cos(radian);
const y = this.radius * Math.sin(radian);
return {
transform: `rotate(${angle}deg) translate(${this.radius}px) rotate(-${angle}deg)`,
'z-index': this.menuItems.length - index
};
},
handleClick(item) {
console.log(`Clicked: ${item.action}`);
// 触发菜单项点击事件
}
}
};
动画效果增强
可以通过CSS过渡或Vue的过渡组件为菜单项添加悬停或点击动画效果。
.menu-item {
transition: transform 0.3s ease, background 0.2s ease;
}
.menu-item:hover {
background: #3aa876;
transform: scale(1.1) rotate(var(--rotate-angle)) translate(var(--radius)) rotate(calc(-1 * var(--rotate-angle)));
}
动态调整参数
通过Vue的响应式数据动态调整扇形半径、菜单项数量或角度偏移量,实现灵活配置。
computed: {
sectorOptions() {
return {
radius: this.radius,
itemCount: this.menuItems.length
};
}
}
完整示例代码
整合以上部分,完整的Vue扇形菜单组件代码如下:
<template>
<div class="sector-menu-container">
<div
v-for="(item, index) in menuItems"
:key="index"
class="menu-item"
:style="getItemStyle(index)"
@click="handleClick(item)"
>
{{ item.label }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
menuItems: [
{ label: 'Home', action: 'home' },
{ label: 'About', action: 'about' },
{ label: 'Contact', action: 'contact' },
{ label: 'Settings', action: 'settings' },
{ label: 'Help', action: 'help' }
],
radius: 100
};
},
methods: {
getItemStyle(index) {
const angle = (360 / this.menuItems.length) * index;
return {
transform: `rotate(${angle}deg) translate(${this.radius}px) rotate(-${angle}deg)`,
'z-index': this.menuItems.length - index
};
},
handleClick(item) {
alert(`Action: ${item.action}`);
}
}
};
</script>
<style>
.sector-menu-container {
position: relative;
width: 300px;
height: 300px;
margin: 0 auto;
}
.menu-item {
position: absolute;
width: 60px;
height: 60px;
background: #42b983;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
cursor: pointer;
transform-origin: center center;
left: 50%;
top: 50%;
margin-left: -30px;
margin-top: -30px;
transition: transform 0.3s ease, background 0.2s ease;
}
.menu-item:hover {
background: #3aa876;
transform: scale(1.1) rotate(var(--rotate-angle)) translate(var(--radius)) rotate(calc(-1 * var(--rotate-angle)));
}
</style>






