知乎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>
到这里咱们就基本上完成了这些需求了