Vue彈出菜單功能的實現(xiàn)代碼
言歸正傳
我們老樣子直接先上效果圖再開始今天的分享 這個項目的github可以看一看

組件分析
- 界面組成
- 邏輯分析
- 最終實現(xiàn)
界面組成
從上圖中,我們可以看出界面主要分為menu和item2塊,其中menu的動畫是自傳,item的動畫是位移,然后這里我們通過絕對布局的方式將整個控件定位在四個角落
.menu_container {
position: absolute;
z-index: 100;
border-radius: 50%;
transition-duration: 400ms;
text-align: center;
border: #efefef 3px solid;
box-shadow: aliceblue 1px 1px 1px;
}
.menu_item {
position: absolute;
border-radius: 50%;
z-index: 99;
border: #efefef 3px solid;
text-align: center;
box-shadow: aliceblue 1px 1px 1px;
}
邏輯分析
這里我將這個控件幾個屬性獨立出來,方便下次開發(fā),其中包含,menu的背景,整個控件在屏幕的哪個角落,menu的寬高,item距離menu位移的距離,menu的背景色,及item的背景色,item的相關內容則由數(shù)據(jù)來控制,具體的我們直接在下方的實現(xiàn)里來講解。
最終實現(xiàn)
這里我用代碼加注釋的方式,幫助大家理解,template我簡單的帶過一下
<div> <div class="menu_container" ref="menuHome" @click="toggleMenu"> <img :src="menuSrc"><!--menu圖--> </div> <div class="menu_item" v-for="(item,index) in menuItems" :id="item.name" @click="clickMenu(item,index)"> <img :src="item.src"><!--item圖--> </div> </div>
核心實現(xiàn) 通過分析可以得出,每個item的偏移量應該為 橫向x:基礎值 * sin(角度值) 縱向y:基礎值 * cos(角度值) 角度值:(數(shù)組的長度-1-當前的下標)* 每一塊所占的角度 * 弧度表示 弧度表示:2 * Math.PI / 360
export default {
...
props: {//開放的屬性,方便自定義
menuSrc: {
default: require('../assets/menu.png')
},
position: {
default: 'LT'//可選擇LT、LB、RT、RB4個角落
},
width: {
default: 50,
},
baseDistance: {
default: 150,
},
menuBg: {
default: 'white'
},
itemBg: {
default: 'white'
},
menuItems: {
type: Array,
}
},
data() {
return {
openFlag: false,//展開合并標志
operators: ['+', '+'],//用于記錄展開時動畫XY方向
}
},
mounted() {
//根據(jù)props初始化各內容的各種style
this.$refs.menuHome.style.width = this.width + 'px';
this.$refs.menuHome.style.height = this.width + 'px';
this.$refs.menuHome.style.lineHeight = this.width + 'px';
this.$refs.menuHome.style.background = this.menuBg;
this.menuItems.forEach((item) => {
let el = document.getElementById(item.name);
el.style.width = `${this.width * 0.8}px`;
el.style.height = `${this.width * 0.8}px`;
el.style.lineHeight = `${this.width * 0.8}px`;
el.style.background = this.itemBg;
});
//根據(jù)position,選擇不同的定位
switch (this.position) {
case 'LT':
this.$refs.menuHome.style.left = '20px';
this.$refs.menuHome.style.top = '20px';
this.menuItems.forEach((item) => {
let el = document.getElementById(item.name);
el.style.left = '26px';
el.style.top = '26px';
});
this.operators = ['+', '+'];
break;
...
}
},
methods: {
toggleMenu() {
if (!this.openFlag) {//合并時,點擊展開操作
this.menuItems.forEach((item, index) => {
this.toggleMenuTransition(item.name, index, false)
});
//menu本身轉一周
this.$refs.menuHome.style.transform = 'rotate(360deg)';
} else {
this.menuItems.forEach((item, index) => {
this.toggleMenuTransition(item.name, index, true)
});
//menu恢復
this.$refs.menuHome.style.transform = 'rotate(0)';
}
this.openFlag = !this.openFlag;
},
toggleMenuTransition(name, index, revert) {
let oneArea = 90 / (this.menuItems.length - 1);//每一塊所占的角度
let axisX = Math.sin((this.menuItems.length - 1 - index) * oneArea * 2 * Math.PI / 360);//橫坐標所偏移的比例
let axisY = Math.cos((this.menuItems.length - 1 - index) * oneArea * 2 * Math.PI / 360);//縱坐標所便宜的比例
let el = document.getElementById(name);//若所傳的name一直,會報錯。
let that = this;
if (!revert) {
setTimeout(function () {
el.style.transitionDuration = '200ms';
el.style.transform = `translate(${that.operators[0]}${that.baseDistance * axisX}px,${that.operators[1]}${that.baseDistance * axisY }px)`;//進行動畫
}, index * 100)//通過定時器的方式,達到一個一個彈出來的效果
} else {
//item恢復
el.style.transitionDuration = '200ms';
el.style.transform = `translate(0,0)`;
}
},
clickMenu(item, index) {
//暴露方法給父組件,進行點擊事件的操作
this.$emit('clickMenu', item, index)
}
}
}
再父組件中引入就可以大功告成啦,先跳一會兒吧,燃燒你的卡路里
父組件調用
引入組件
import toggleMenu from './toggleMenu'
在 components聲明
components: {
toggleMenu
},
template中使用
menuItems: [//name和src必填,且name唯一否則會報錯
{name: 'menu1', src: require('../assets/emoji.png')},
{name: 'menu2', src: require('../assets/cart.png')},
{name: 'menu3', src: require('../assets/folder.png')},
{name: 'menu4', src: require('../assets/home.png')},
{name: 'menu5', src: require('../assets/my.png')},
]
<toggle-menu :menuItems="menuItems"
@clickMenu="clickMenu"
></toggle-menu>
屬性及方法一欄
| 屬性名 | 用處 | 默認值 | 是否必須 |
|---|---|---|---|
| position | 四個方位(LT、LB、RT、RB) | LT | 否 |
| menuBg | 菜單背景 | white | 否 |
| menuSrc | 菜單圖片 | 一個菜單圖片 | 否 |
| itemBg | 按鈕背景 | white | 否 |
| width | 按鈕寬度 | 50px | 否 |
| baseDistance | 位移距離,若item很多,可適當提高 | 150px | 否 |
| menuItems | 菜單數(shù)組 | 無 | 是 |
| 方法名 | 用處 | 參數(shù) |
|---|---|---|
| clickMenu | 點擊item觸發(fā)事件 | item,index |
總結
以上所述是小編給大家介紹的Vue左側底部彈出菜單功能的實現(xiàn)代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!
相關文章
解決vite項目Uncaught Syntaxerror:Unexpected token>vue項
這篇文章主要介紹了解決vite項目Uncaught Syntaxerror:Unexpected token>vue項目上線白屏問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
element-resize-detector監(jiān)聽普通元素的實現(xiàn)示例
當涉及到網頁元素的實時尺寸變化監(jiān)測時,element-resize-detector?是一個值得推薦的開源庫,本文主要介紹了element-resize-detector監(jiān)聽普通元素的實現(xiàn)示例,感興趣的可以了解一下2024-07-07
vue?如何刪除數(shù)組中的某一條數(shù)據(jù)
這篇文章主要介紹了vue?如何刪除數(shù)組中的某一條數(shù)據(jù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08
vue.js添加一些觸摸事件以及安裝fastclick的實例
今天小編就為大家分享一篇vue.js添加一些觸摸事件以及安裝fastclick的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08

