知乎html
我的博客vue
Githubgit
源碼傳送門:Rain120/vue-studygithub
以前在外賣軟件上看到這個左右聯動的效果,以爲頗有意思,因此就嘗試使用Vue
來實現,將這個聯動抽離成爲一個單獨的組件,廢話少說,先來一張效果圖。數據結構
這個組件分爲兩個部分,一、左菜單;二、右菜單。app
動態數據結構
函數
menus: [ { name: '菜單1', data: [ { name: '1.1' }, { name: '1.2' }, { name: '1.3' }, { name: '1.4' }, { name: '1.5' }, { name: '1.6' } ] } ]
data數據是用戶自定義增長一些內容,並渲染DOM
post
左菜單的DOM
結構ui
<scroll class="left-menu" :data="menus" ref="leftMenu"> <div class="left-menu-container"> <ul> <li class="left-item" ref="leftItem" :class="{'current': currentIndex === index}" @click="selectLeft(index, $event)" v-for="(menu, index) in menus" :key="index"> <p class="text">{{menu.name}}</p> </li> </ul> </div> </scroll>
右菜單的DOM
結構this
<scroll class="right-menu" :data="menus" ref="rightMenu" @scroll="scrollHeight" :listenScroll="true" :probeType="3"> <div class="right-menu-container"> <ul> <li class="right-item" ref="rightItem" v-for="(menu, i) in menus" :key="i"> <div class="title">{{menu.name}}</div> <ul> <li v-for="(item, j) in menu.data" :key="j"> <div class="data-wrapper"> <div class="data">{{item.name}}</div> </div> </li> </ul> </li> </ul> </div> </scroll>
這裏是爲了作demo
,因此在數據上只是單純捏造。
固然由於這是個子組件,咱們將經過父組件傳遞props
,因此定義props
props: { menus: { required: true, type: Array, default () { return [] } } },
在這個業務場景中,咱們的實現方式是根據右邊菜單滾動的高度來計算左邊菜單的位置,固然左邊菜單也能夠經過點擊來肯定右邊菜單須要滾動多高的距離,那麼咱們如何得到該容器滾動的距離呢?
以前一直在使用better-scroll,經過閱讀文檔,咱們知道它有有scroll
事件,咱們能夠經過監聽這個事件來獲取滾動的pos
if (this.listenScroll) { let me = this this.scroll.on('scroll', (pos) => { me.$emit('scroll', pos) }) }
因此咱們在右邊菜單的scroll
組件上監聽scroll事件
@scroll="scrollHeight"
method
scrollHeight (pos) { console.log(pos); this.scrollY = Math.abs(Math.round(pos.y)) },
咱們將監聽獲得的pos打出來看看
咱們能夠看到控制檯打出了當前滾動的pos信息,由於在移動端開發時,座標軸和咱們數學中的座標軸相反,因此上滑時y軸的值是負數
因此咱們要獲得每一塊li
的高度,咱們能夠經過拿到他們的DOM
_calculateHeight() { let lis = this.$refs.rightItem; let height = 0 this.rightHeight.push(height) Array.prototype.slice.call(lis).forEach(li => { height += li.clientHeight this.rightHeight.push(height) }) console.log(this.rightHeight) }
咱們在created
這個hook
以後調用這個計算高度的函數
_calculateHeight() { let lis = this.$refs.rightItem; let height = 0 this.rightHeight.push(height) Array.prototype.slice.call(lis).forEach(li => { height += li.clientHeight this.rightHeight.push(height) }) console.log(this.rightHeight) }
當用戶在滾動時,咱們須要計算當前滾動距離實在那個區間內,並拿到他的index
computed: { currentIndex () { const { scrollY, rightHeight } = this const index = rightHeight.findIndex((height, index) => { return scrollY >= rightHeight[index] && scrollY < rightHeight[index + 1] }) return index > 0 ? index : 0 } }
因此當前應該是左邊菜單index = 1
的菜單項active
以上是左邊菜單根據右邊菜單的滑動聯動的實現,用戶也能夠經過點擊左邊菜單來實現右邊菜單的聯動,此時,咱們給菜單項加上click事件
@click="selectLeft(index, $event)"
這裏加上$event
是爲了區分原生點擊事件仍是better-scroll派發的事件
selectLeft (index, event) { if (!event._constructed) { return } let rightItem = this.$refs.rightItem let el = rightItem[index] this.$refs.rightMenu.scrollToElement(el, 300) },
使用
<cascad-menu :menus="menus"></cascad-menu>
到這裏咱們就基本上完成了這些需求了