在做移動端商城或者其他頁面的時候,經常會遇到左右聯動的效果,今天小編vue2.0和better-scroll這個外掛一起實現左右聯動效果。
實現上面的效果,思路一定很重要,還有需求
1. 左邊一級分類和右邊二級分類形成聯動
2. 當滑動右側分類列表時, 更新左側分類選中
3. 點選左側一級分類項時, 右側列表滑動到對應位置
在vue腳手架的時候,引入第三方外掛better-scroll,如果想了解的話,可以去看看它的中午文件說明,
npm install better-scroll --save直接安裝到自己專案當中,並引入
1.頁面結構搭建
<div class="search"> <!-- 搜尋導航 --> <SearchNav></SearchNav> <div class="shop"> <!-- 左邊 --> <div class="menu-wrapper"> <ul> <!-- current --> <li class="menu-item" v-for="(goods,index) in searchgoods" :key="index" :class="{current: index === currentIndex}" @click="clickList(index)" ref="menuList" > <span>{{goods.name}}</span> </li> </ul> </div> <!-- 右邊 --> <div class="shop-wrapper"> <ul ref="itemList"> <li class="shops-li" v-for="(goods, index1) in searchgoods" :key="index1"> <div class="shops-title"> <h4>{{goods.name}}</h4> <a href="">檢視更多 > </a> </div> <ul class="phone-type" v-if="goods.tag === 'phone'"> <li v-for="(phone,index) in goods.category" :key="index"> <img :src="phone.icon" alt=""> </li> </ul> <ul class="shops-items"> <li v-for="(item, index2) in goods.items" :key="index2"> <img :src="item.icon" alt=""> <span>{{item.title}}</span> </li> </ul> </li> </ul> </div> </div> </div>
css樣式
<style lang="stylus" rel="stylesheet/stylus" scoped> @import "../../common/stylus/mixins.styl" .search width 100% height 100% background-color #f5f5f5 overflow hidden .shop display flex position absolute top 60px bottom 50px width 100% overflow hidden .menu-wrapper background-color #e0e0e0 width 80px flex 0 0 80px .menu-item width 100% height 60px background #fafafa display flex justify-content center align-items center font-family lighter color #666 position relative .current color #e02e24 background #ffffff .current::before content '' background-color #e02e24 width 4px height 30px position absolute left 0 .shop-wrapper flex 1 background #fff .shops-title display flex flex-direction row padding 0 10px height 40px align-items center justify-content space-between color #9999 a text-decoration none color #9c9c9c font-size 14px .shops-items display flex flex-wrap wrap li display flex flex-direction column width 33.3% height 90px justify-content center align-items center img width 60% height 60% margin-bottom 5px span color #151516 font-size 13px .phone-type width 100% display flex flex-direction row flex-wrap wrap border-bottom-1px (#cccccc) li width 33.3% display flex justify-content center align-items center margin 5px 0 img width 70% </style>
頁面分為左右兩個部分,
先實現左右兩邊滾動效果,我們需要在methods定義一個方法,但是better-scroll的初始化一定要在資料渲染完成後進行
methods:{
//左邊滾動 this.leftBscroll = new BScroll('.menu-wrapper',{}); //右邊滾動 this.rightBscroll = new BScroll('.shop-wrapper',{
});
}
我們通過watch監聽searchgoods資料是否有,並通過this.$nextTick去呼叫_initBScroll方法。
searchgoods是資料儲存的地方
watch:{
searchgoods(){
//監聽資料
this.$nextTick(() =>{
//左右兩邊滾動
this. _initBScroll();
})
}
},
2.計算出每一個li標籤的高度,並把它存放在一個陣列當中
1.需要在data中定義兩個變數
data () {
return {
scrollY:0, //右側列表滑動的y軸座標
rightLiTops:[] //所有分類頭部位置
}
},
2.在methods中定義一個方法,_initRightHeight,這個方法是用於計算每個li標籤的高度
//求出右邊列表的高度
_initRightHeight(){
let itemArray=[]; //定義一個偽陣列
let top = 0;
itemArray.push(top)
//獲取右邊所有li的禮
let allList = this.$refs.itemList.getElementsByClassName('shops-li');
//allList偽陣列轉化成真陣列
Array.prototype.slice.call(allList).forEach(li => {
top += li.clientHeight; //獲取所有li的每一個高度
itemArray.push(top)
});
this.rightLiTops = itemArray;
// console.log(this.rightLiTops)
},
通過上面的方法,已經把所有li標籤的高度計算出來
3.監聽右邊滾動事件
通過better-scroll提供的 on 事件,當右邊內容滾動的時候計算出滾動的距離,一定要在滾動的時候觸發這個事件_initBScroll這個方法當中去寫
//監聽右邊滾動事件
this.rightBscroll.on('scroll',(pos) => {
this.scrollY = Math.abs(pos.y);
console.log(this.scrollY)
})
4.動態繫結class樣式
1需要給左右的li標籤繫結一個:class="{current: index === currentIndex}",通過計算屬性,實現這個效果
computed: { //動態繫結class類名 currentIndex(index) { const {scrollY,rightLiTops} = this; return rightLiTops.findIndex((tops,index )=>{ this._initLeftScroll(index); //呼叫左右聯調滾動效果 return scrollY >= tops && scrollY < rightLiTops[index + 1] }) } },
5.點選左邊實現滾動和左右滾動聯調
5.1實現點選左邊實現滾動效果,需要給左邊的li標籤繫結一個點選事件@click="clickList(index)",通過index來來計算出點選的位置
this.rightLiTops[index]通過index索引得到點選的時候,會得到每一塊li標籤的高度
通過better-scroll提供的scrollTo來實現具體滾動的位置
clickList(index){
this.scrollY = this.rightLiTops[index];
this.rightBscroll.scrollTo(0,-this.scrollY,200,)
},
5.2當右邊內容滾動的時候,滾動一定的程度的時候,希望左邊也會隨著滾動,
5.2.1通過ref所有所有li標籤
//左右聯調
_initLeftScroll(index){
let menu = this.$refs.menuList;
let el = menu[index];
this.leftBscroll.scrollToElement(el,300,0,-300)
}
以上都是基本上完成了這些需求了
最終程式碼
<template> <div class="search"> <!-- 搜尋導航 --> <SearchNav></SearchNav> <div class="shop"> <!-- 左邊 --> <div class="menu-wrapper"> <ul> <!-- current --> <li class="menu-item" v-for="(goods,index) in searchgoods" :key="index" :class="{current: index === currentIndex}" @click="clickList(index)" ref="menuList" > <span>{{goods.name}}</span> </li> </ul> </div> <!-- 右邊 --> <div class="shop-wrapper"> <ul ref="itemList"> <li class="shops-li" v-for="(goods, index1) in searchgoods" :key="index1"> <div class="shops-title"> <h4>{{goods.name}}</h4> <a href="">檢視更多 > </a> </div> <ul class="phone-type" v-if="goods.tag === 'phone'"> <li v-for="(phone,index) in goods.category" :key="index"> <img :src="phone.icon" alt=""> </li> </ul> <ul class="shops-items"> <li v-for="(item, index2) in goods.items" :key="index2"> <img :src="item.icon" alt=""> <span>{{item.title}}</span> </li> </ul> </li> </ul> </div> </div> </div> </template> <script> import SearchNav from './Children/SearchNav' import {mapState} from 'vuex' import BScroll from 'better-scroll' export default { name: 'chat', data () { return { scrollY: 0, //右側列表滑動的y軸座標 rightLiTops:[] //所有分類頭部位置 } }, computed: { ...mapState(['searchgoods']), //列表資料 //動態繫結class類名 currentIndex(index) { const {scrollY,rightLiTops} = this; return rightLiTops.findIndex((tops,index )=>{ this._initLeftScroll(index); return scrollY >= tops && scrollY < rightLiTops[index + 1] }) } }, mounted() { this.$store.dispatch('reqSearchGoods') }, components: { SearchNav }, watch:{ searchgoods(){ //監聽資料 this.$nextTick(() =>{ //左右兩邊滾動 this. _initBScroll(); //右邊列表高度 this._initRightHeight() }) } }, methods:{ _initBScroll() { //左邊滾動 this.leftBscroll = new BScroll('.menu-wrapper',{}); //右邊滾動 this.rightBscroll = new BScroll('.shop-wrapper',{ probeType:3 }); //監聽右邊滾動事件 this.rightBscroll.on('scroll',(pos) => { this.scrollY = Math.abs(pos.y); // console.log(this.scrollY) }) }, //求出右邊列表的高度 _initRightHeight(){ let itemArray=[]; //定義一個偽陣列 let top = 0; itemArray.push(top) //獲取右邊所有li的禮 let allList = this.$refs.itemList.getElementsByClassName('shops-li'); //allList偽陣列轉化成真陣列 Array.prototype.slice.call(allList).forEach(li => { top += li.clientHeight; //獲取所有li的每一個高度 itemArray.push(top) }); this.rightLiTops = itemArray; // console.log(this.rightLiTops) }, //點選左邊實現滾動 clickList(index){ this.scrollY = this.rightLiTops[index]; console.log(this.scrollY) this.rightBscroll.scrollTo(0,-this.scrollY,200,) }, //左右聯調 _initLeftScroll(index){ let menu = this.$refs.menuList; let el = menu[index]; this.leftBscroll.scrollToElement(el,300,0,-300) } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style lang="stylus" rel="stylesheet/stylus" scoped> @import "../../common/stylus/mixins.styl" .search width 100% height 100% background-color #f5f5f5 overflow hidden .shop display flex position absolute top 60px bottom 50px width 100% overflow hidden .menu-wrapper background-color #e0e0e0 width 80px flex 0 0 80px .menu-item width 100% height 60px background #fafafa display flex justify-content center align-items center font-family lighter color #666 position relative .current color #e02e24 background #ffffff .current::before content '' background-color #e02e24 width 4px height 30px position absolute left 0 .shop-wrapper flex 1 background #fff .shops-title display flex flex-direction row padding 0 10px height 40px align-items center justify-content space-between color #9999 a text-decoration none color #9c9c9c font-size 14px .shops-items display flex flex-wrap wrap li display flex flex-direction column width 33.3% height 90px justify-content center align-items center img width 60% height 60% margin-bottom 5px span color #151516 font-size 13px .phone-type width 100% display flex flex-direction row flex-wrap wrap border-bottom-1px (#cccccc) li width 33.3% display flex justify-content center align-items center margin 5px 0 img width 70% </style>