title: Vue實現左右選單聯動實現 date: 2018-08-11 16:31:34 tags:
- Vue
- 左右聯動 top: 100 copyright: true
原始碼傳送門:Rain120/vue-study
根據掘金評論需求,更新了資料介面並修復了一些問題
之前在外賣軟體上看到這個左右聯動的效果,覺得很有意思,所以就嘗試使用Vue
來實現,將這個聯動抽離成為一個單獨的元件,廢話少說,先來一張效果圖。
這個元件分為兩個部分,1、左選單;2、右選單。
動態資料結構
menus: [
{
name: '選單1',
data: [
{
name: '1.1'
},
{
name: '1.2'
},
{
name: '1.3'
},
{
name: '1.4'
},
{
name: '1.5'
},
{
name: '1.6'
}
]
}
]
複製程式碼
data資料是使用者自定義增加一些內容,並渲染DOM
左選單的DOM
結構
<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
結構
<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>
複製程式碼
到這裡我們就基本上完成了這些需求了