參考地址:https://www.html5tricks.com/html5-css3-circle-menu.html
一、效果

二、參考
上面有一個鏈接地址,我參考了這個插件的源碼,沒有做太多的改變,只是修改為了適合我當前的需求,並且把步驟和思路大概整理了一下。
因為我本人是個前端小白,對於美僅限於欣賞。
三、第一步
首先構建一個樣子出來。

<template> <div class="wrapper"> <a href="#" class="show">START</a> <ul> <!-- 一級列表 --> <li> <a href="#">A</a> <!-- 二級列表 --> <ul> <li><a href="#">A-1</a></li> <li><a href="#">A-2</a></li> <li><a href="#">A-3</a></li> <li><a href="#">A-4</a></li> <li><a href="#">A-5</a></li> </ul> </li> <li> <a href="#">B</a> <ul> <li><a href="#">B-1</a></li> <li><a href="#">B-2</a></li> <li><a href="#">B-3</a></li> <li><a href="#">B-4</a></li> <li><a href="#">B-5</a></li> </ul> </li> </ul> </div> </template>

<style scoped> ul{ list-style: none; } a { width: 120px; height: 120px; position: absolute; background: rgba(255, 255, 255, 0.9); text-align: center; text-decoration: none; align-items: center; justify-content: center; border-radius: 120px; display: none; /*默認所有的a都不顯示*/ text-decoration: none; color: #333; transition: all 1s ease; box-shadow: 0 0 15px #222; font-family: "segoe ui"; font-weight: 200; font-size: 16px; } .wrapper { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; position: absolute; } a.show { display: flex !important; /*因為開始設置了a標簽為none,這里重置display可以讓他顯示*/ } </style>
四、第二步
做環繞效果。

<template> <div class="wrapper"> <a href="#" class="">START</a> <ul> <!-- 一級列表 --> <li> <a href="#" class="selected">A</a> <!-- 二級列表 --> <ul> <li> <a href="#">A-1</a> </li> <li> <a href="#">A-2</a> </li> <li> <a href="#">A-3</a> </li> <li> <a href="#">A-4</a> </li> <li> <a href="#">A-5</a> </li> </ul> </li> <li> <a href="#">B</a> <ul> <li> <a href="#">B-1</a> </li> <li> <a href="#">B-2</a> </li> <li> <a href="#">B-3</a> </li> <li> <a href="#">B-4</a> </li> <li> <a href="#">B-5</a> </li> </ul> </li> </ul> </div> </template>

<style scoped> ul { list-style: none; } a { width: 120px; height: 120px; position: absolute; background: rgba(255, 255, 255, 0.9); text-align: center; text-decoration: none; align-items: center; justify-content: center; border-radius: 120px; display: none; /*默認所有的a都不顯示*/ text-decoration: none; color: #333; transition: all 1s ease; box-shadow: 0 0 15px #222; font-family: "segoe ui"; font-weight: 200; font-size: 16px; } .wrapper { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; position: absolute; } a.show { display: flex !important; /*因為開始設置了a標簽為none,這里重置display可以讓他顯示*/ } /*對所有的li標簽加動畫效果*/ .wrapper li { -webkit-transform: translate3d(0, 0, 0); /*調用GPU提升動畫性能*/ transform: translate3d(0, 0, 0); transition: all 1s ease; /*下面的方法好像也可以提升性能,具體並沒有比較*/ /* backface-visibility: hidden; perspective: 1000; */ } /*設置選中樣式*/ .selected { background: rgba(51, 51, 51, 0.9); display: flex; /*調整中心圓形的位置*/ top: calc(50% - 50px); left: calc(50% - 60px); color: #f1f1f1; animation: light 1s infinite; } /*讓當前選中的子集列表顯示*/ .selected + ul > li > a { /*讓當前選中的子選項顯示,因為默認是 display:none*/ display: flex; } /*設置 li 的位置,rotate是順/逆時針旋轉,translateX是沿x軸平移*/ .selected + ul > li:nth-child(1) { -webkit-transform: rotate(-120deg) translateX(80px); transform: rotate(-120deg) translateX(80px); } /*設置a標簽內字體的位置,應為容器旋轉后,字體傾斜了,需要調整回來*/ /*這里有一個小技巧:字體的旋轉角度正好和外部容器的旋轉角度相反,但是最后一個的位置需要調整*/ .selected + ul > li:nth-child(1) > a { -webkit-transform: rotate(120deg); transform: rotate(120deg); } .selected + ul > li:nth-child(2) { -webkit-transform: rotate(-40deg) translateX(80px); transform: rotate(-40deg) translateX(80px); } .selected + ul > li:nth-child(2) > a { -webkit-transform: rotate(40deg); transform: rotate(40deg); } .selected + ul > li:nth-child(3) { -webkit-transform: rotate(40deg) translateX(80px); transform: rotate(40deg) translateX(80px); } .selected + ul > li:nth-child(3) > a { -webkit-transform: rotate(-40deg); transform: rotate(-40deg); } .selected + ul > li:nth-child(4) { -webkit-transform: rotate(120deg) translateX(120px); transform: rotate(120deg) translateX(120px); } .selected + ul > li:nth-child(4) > a { -webkit-transform: rotate(-120deg); transform: rotate(-120deg); } .selected + ul > li:nth-child(5) { -webkit-transform: rotate(180deg) translateX(120px); transform: rotate(180deg) translateX(120px); } .selected + ul > li:nth-child(5) > a { -webkit-transform: rotate(180deg); transform: rotate(180deg); } </style>
五、第三步
加動畫,以及實現級聯。

<template> <div class="wrapper"> <a href="#" class="show" @click="Bubblings($event)">START</a> <ul> <!-- 一級列表 --> <li> <a href="#" @click="Bubblings($event)">A</a> <!-- 二級列表 --> <ul> <li> <a href="#" @click="Bubblings($event)">A-1</a> </li> <li> <a href="#" @click="Bubblings($event)">A-2</a> </li> <li> <a href="#" @click="Bubblings($event)">A-3</a> </li> <li> <a href="#" @click="Bubblings($event)">A-4</a> </li> <li> <a href="#" @click="Bubblings($event)">A-5</a> </li> </ul> </li> <li> <a href="#" @click="Bubblings($event)">B</a> <ul> <li> <a href="#" @click="Bubblings($event)">B-1</a> </li> <li> <a href="#" @click="Bubblings($event)">B-2</a> </li> <li> <a href="#" @click="Bubblings($event)">B-3</a> </li> <li> <a href="#" @click="Bubblings($event)">B-4</a> </li> <li> <a href="#" @click="Bubblings($event)">B-5</a> </li> </ul> </li> </ul> </div> </template>

<script> export default { name:'bubbling', methods:{ Bubblings(e){ //獲取點擊的DOM對象 let self = e.target //檢查點擊的是否是選中的圓 if (self.classList.contains('selected')) { //如果是移除當前選中效果,返回上級菜單 self.classList.remove('selected') //判斷它的父級元素是不是頂級元素 if (!self.parentNode.classList.contains('wrapper')) { //如果不是頂級元素,展示對應的菜單列表 self.parentNode.parentNode.parentNode .querySelector('a') .classList.add('selected') } else { //如果是頂級元素,僅顯示最頂級元素 self.classList.add('show') } } else { //如果點擊的不是選中的圓 self.classList.add('selected') //判斷是否點擊的是頂級元素 if (!self.parentNode.classList.contains('wrapper')) { //如果不是頂級元素,展開所點擊的菜單列表 // a(self) -> li(parent) -> ul(parent) -> li(parent) -> a(target) self.parentNode.parentNode.parentNode .querySelector('a') .classList.remove('selected') } else { //點擊頂級元素展開一級菜單列表 self.classList.remove('show') } } return false } } } </script>

<style scoped> ul { list-style: none; } a { width: 120px; height: 120px; position: absolute; background: rgba(255, 255, 255, 0.9); text-align: center; text-decoration: none; align-items: center; justify-content: center; border-radius: 120px; display: none; /*默認所有的a都不顯示*/ text-decoration: none; color: #333; transition: all 1s ease; box-shadow: 0 0 15px #222; font-family: "segoe ui"; font-weight: 200; font-size: 16px; } .wrapper { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; position: absolute; } a.show { display: flex !important; /*因為開始設置了a標簽為none,這里重置display可以讓他顯示*/ } /*對所有的li標簽加動畫效果*/ .wrapper li { -webkit-transform: translate3d(0, 0, 0); /*調用GPU提升動畫性能*/ transform: translate3d(0, 0, 0); transition: all 1s ease; /*下面的方法好像也可以提升性能,具體並沒有比較*/ /* backface-visibility: hidden; perspective: 1000; */ } /*設置選中樣式*/ .selected { background: rgba(51, 51, 51, 0.9); display: flex; /*調整中心圓形的位置*/ top: calc(50% - 50px); left: calc(50% - 60px); color: #f1f1f1; animation: light 1s infinite; } /*讓當前選中的子集列表顯示*/ .selected + ul > li > a { /*讓當前選中的子選項顯示,因為默認是 display:none*/ display: flex; } /*設置 li 的位置,rotate是順/逆時針旋轉,translateX是沿x軸平移*/ .selected + ul > li:nth-child(1) { -webkit-transform: rotate(-120deg) translateX(80px); transform: rotate(-120deg) translateX(80px); } /*設置a標簽內字體的位置,應為容器旋轉后,字體傾斜了,需要調整回來*/ /*這里有一個小技巧:字體的旋轉角度正好和外部容器的旋轉角度相反,但是最后一個的位置需要調整*/ .selected + ul > li:nth-child(1) > a { -webkit-transform: rotate(120deg); transform: rotate(120deg); } .selected + ul > li:nth-child(2) { -webkit-transform: rotate(-40deg) translateX(80px); transform: rotate(-40deg) translateX(80px); } .selected + ul > li:nth-child(2) > a { -webkit-transform: rotate(40deg); transform: rotate(40deg); } .selected + ul > li:nth-child(3) { -webkit-transform: rotate(40deg) translateX(80px); transform: rotate(40deg) translateX(80px); } .selected + ul > li:nth-child(3) > a { -webkit-transform: rotate(-40deg); transform: rotate(-40deg); } .selected + ul > li:nth-child(4) { -webkit-transform: rotate(120deg) translateX(120px); transform: rotate(120deg) translateX(120px); } .selected + ul > li:nth-child(4) > a { -webkit-transform: rotate(-120deg); transform: rotate(-120deg); } .selected + ul > li:nth-child(5) { -webkit-transform: rotate(180deg) translateX(120px); transform: rotate(180deg) translateX(120px); } .selected + ul > li:nth-child(5) > a { -webkit-transform: rotate(180deg); transform: rotate(180deg); } </style>
六、第四步
綁定數據,以及完整代碼。
<template> <div class="wrapper"> <a href="#" class="show" @click="Bubblings($event)">START</a> <ul> <!-- 一級列表 --> <li v-for="(item,index) of list" :key="index"> <a href="#" @click="Bubblings($event)">{{ item.name }}</a> <!-- 二級列表 --> <ul> <li v-for="(child,index) of item.childs" :key="index"> <a href="#" @click="Bubblings($event)">{{ child.name }}</a> </li> </ul> </li> </ul> </div> </template>
<script> export default { name:'bubbling', data(){ return { list:[ { name:'A', childs:[ { name:'A-1' }, { name:'A-2' }, { name:'A-3' } ] }, { name:'B', childs:[ { name:'B-1' }, { name:'B-2' }, { name:'B-3' } ] } ] } }, methods:{ Bubblings(e){ //獲取點擊的DOM對象 let self = e.target //檢查點擊的是否是選中的圓 if (self.classList.contains('selected')) { //如果是移除當前選中效果,返回上級菜單 self.classList.remove('selected') //判斷它的父級元素是不是頂級元素 if (!self.parentNode.classList.contains('wrapper')) { //如果不是頂級元素,展示對應的菜單列表 self.parentNode.parentNode.parentNode .querySelector('a') .classList.add('selected') } else { //如果是頂級元素,僅顯示最頂級元素 self.classList.add('show') } } else { //如果點擊的不是選中的圓 self.classList.add('selected') //判斷是否點擊的是頂級元素 if (!self.parentNode.classList.contains('wrapper')) { //如果不是頂級元素,展開所點擊的菜單列表 // a(self) -> li(parent) -> ul(parent) -> li(parent) -> a(target) self.parentNode.parentNode.parentNode .querySelector('a') .classList.remove('selected') } else { //點擊頂級元素展開一級菜單列表 self.classList.remove('show') } } return false } } } </script>
<style scoped> ul { list-style: none; } a { width: 120px; height: 120px; position: absolute; background: rgba(255, 255, 255, 0.9); text-align: center; text-decoration: none; align-items: center; justify-content: center; border-radius: 120px; display: none; /*默認所有的a都不顯示*/ text-decoration: none; color: #333; transition: all 1s ease; box-shadow: 0 0 15px #222; font-family: "segoe ui"; font-weight: 200; font-size: 16px; } .wrapper { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; position: absolute; } a.show { display: flex !important; /*因為開始設置了a標簽為none,這里重置display可以讓他顯示*/ } /*對所有的li標簽加動畫效果*/ .wrapper li { -webkit-transform: translate3d(0, 0, 0); /*調用GPU提升動畫性能*/ transform: translate3d(0, 0, 0); transition: all 1s ease; /*下面的方法好像也可以提升性能,具體並沒有比較*/ /* backface-visibility: hidden; perspective: 1000; */ } /*設置選中樣式*/ .selected { background: rgba(51, 51, 51, 0.9); display: flex; /*調整中心圓形的位置*/ top: calc(50% - 50px); left: calc(50% - 60px); color: #f1f1f1; animation: light 1s infinite; } /*讓當前選中的子集列表顯示*/ .selected + ul > li > a { /*讓當前選中的子選項顯示,因為默認是 display:none*/ display: flex; } /*設置 li 的位置,rotate是順/逆時針旋轉,translateX是沿x軸平移*/ .selected + ul > li:nth-child(1) { -webkit-transform: rotate(-120deg) translateX(80px); transform: rotate(-120deg) translateX(80px); } /*設置a標簽內字體的位置,應為容器旋轉后,字體傾斜了,需要調整回來*/ /*這里有一個小技巧:字體的旋轉角度正好和外部容器的旋轉角度相反,但是最后一個的位置需要調整*/ .selected + ul > li:nth-child(1) > a { -webkit-transform: rotate(120deg); transform: rotate(120deg); } .selected + ul > li:nth-child(2) { -webkit-transform: rotate(-40deg) translateX(80px); transform: rotate(-40deg) translateX(80px); } .selected + ul > li:nth-child(2) > a { -webkit-transform: rotate(40deg); transform: rotate(40deg); } .selected + ul > li:nth-child(3) { -webkit-transform: rotate(40deg) translateX(80px); transform: rotate(40deg) translateX(80px); } .selected + ul > li:nth-child(3) > a { -webkit-transform: rotate(-40deg); transform: rotate(-40deg); } .selected + ul > li:nth-child(4) { -webkit-transform: rotate(120deg) translateX(120px); transform: rotate(120deg) translateX(120px); } .selected + ul > li:nth-child(4) > a { -webkit-transform: rotate(-120deg); transform: rotate(-120deg); } .selected + ul > li:nth-child(5) { -webkit-transform: rotate(180deg) translateX(120px); transform: rotate(180deg) translateX(120px); } .selected + ul > li:nth-child(5) > a { -webkit-transform: rotate(180deg); transform: rotate(180deg); } </style>
七、總結
更多的是對CSS動畫的操作,以及DOM的結構和布局。歡迎留言討論。
謙良恭卑,信誠實至;
生活不易,共勉同求。