title: Vue之网易云音乐横向菜单的实现 date: 2018-06-30 20:25:22 categories: Vue tags:
- Vue
- better-scroll
- 横向菜单 top: 100 copyright: true
之前在学习的时候有稍微捣鼓一下网易云音乐,主要是为了学习Vue,巩固基础知识,然后看到这个横向菜单,当然个人也喜欢看球,所以每次看腾讯NBA的时候总是会想这个是这样实现的,于是借助之前还没写完的demo,完成这个横向菜单的实现,废话不多说,先上效果图
从使用虎牙直播横向菜单的体验得到,我们的横向菜单的业务逻辑如下:
- 滑动菜单,并选择菜单项;
- 选择某个菜单项,该菜单项居中(去除边界菜单项)
我们的使用的better-scroll这个插件来实现,具体安装请参考
前端DOM结构
复制代码
{ {item.title}}
通过使用插件Vue来调试项目
其中tabs包括菜单项名和它的路由
data () { return { tabs: [ { to: '/mv/recommend-mv', title: '推荐' }, { to: '/mv/music-mv', title: '音乐' }, { to: 'show-mv', title: 'Show' }, { to: '/mv/acg-mv', title: '二次元' }, { to: '/mv/dance-mv', title: '舞蹈' }, { to: '/mv/game-mv', title: '游戏' }, { to: '/mv/mvs', title: 'mv' } ], mX: 0, // tab移动的距离 tabWidth: 80 // 每个tab的宽度 }复制代码
样式
.mv-tabs position relative top -5.5rem bottom 0 width 100% .tabs margin-top 3rem height 2.5rem width 100% line-height 2.5rem box-sizing border-box overflow hidden white-space nowrap .tab-item float left width 80px height 40px text-align center .tab-link padding-bottom 5px color #333333 &.router-link-active color #d33a31 border-bottom 2px solid #d33a31 box-sizing border-box复制代码
样式和DOM结构就不详细讲了,具体讲实现吧 首先需要计算出这个菜单中所有内容的width,也就是包裹这个菜单的容器;接着初始化better-scroll,并忽略该实例对象的垂直方向的滑动.
methods: { _initMenu () { let tabsWidth = this.tabWidth let width = this.tabs.length * tabsWidth this.$refs.tab.style.width = `${width}px` this.$nextTick(() => { if (!this.scroll) { this.scroll = new BScroll(this.$refs.tabsWrapper, { scrollX: true, eventPassthrough: 'vertical' // 忽略这个实例对象的垂直滑动事件 }) } else { this.scroll.refresh() } }) } }复制代码
这里是第二个业务逻辑的思路(应该会有更好的思路,求大佬指点)
我的思路是这样的:每一个菜单项都会有各自的点击移动操作,所以我是根据当前tabs的位置,通过点击事件将tabs移动到它相应的位置,例如,中间菜单项在点击时会移动到居中的位置。
methods: { selectItem (index) { let tabs = this.$refs.tab let moveX = +tabs.style.transform.replace(/[^0-9\-,]/g, '').split(',')[0] switch (index) { case 0: if (moveX <= 0 && moveX > -this.tabWidth) { this.mX = 0 } break case 1: if (moveX <= 0 && moveX > -this.tabWidth * 2) { this.mX = 0 } break case 2: if (moveX < 0 && moveX >= -this.tabWidth * 2) { this.mX = 0 } break case 3: if (moveX <= 0 && moveX >= -this.tabWidth * 2) { this.mX = -this.tabWidth } break case 4: if (moveX <= 0 && moveX >= -this.tabWidth * 2) { this.mX = -this.tabWidth * 2 } else if (moveX === 0) { this.mX = -this.tabWidth * 2 } break case 5: if (moveX < 0 && moveX > -this.tabWidth * 2) { this.mX = -this.tabWidth * 2 } break case 6: if (moveX > -this.tabWidth * 2 && moveX < -this.tabWidth * 3 / 2) { this.mX = -this.tabWidth * 2 + 10 } break default: break } tabs.style.transform = `translate(${this.mX}px, 0)` } }复制代码
很多时候我们在使用better-scroll的时候,发现这个实例对象已经初始化,但是不能滑动,是因为,Vue是异步更新数据的,所以我们需要异步计算它实际内容的宽度或者高度,Vue提供一个了this.$nextTick()这个hock来实现,这个API是在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
官方解释:
当生命钩子mounted触发时,初始化better-scroll
mounted () { this.$nextTick(() => { this._initMenu() })}复制代码
全部代码
复制代码
{ {item.title}}