一、前言
二、主要內容
(1)文字說明:滑動右側左側對應的類別顯示高亮,點擊左側的類別名稱右側滑動到對應的位置
(2)圖示

(1)分析:滑動右側的時候左側對應的類高亮,高亮顯示是通過current類來控制的,當右邊滑動到不同個類的時候,同時更新左側的current的顯示。
(2)要做的事情: ①current來設置當前高亮, ②要實時監聽scrollY, ③將右側每一個類的頂部位置記錄下來
(3)根據滑動的位置scrollY,與記錄的每個類的頂部,來計算當前下標
第零步:在data中定義兩個屬性,分別來接收滑動的scrollY的值,和右側每個分類的位置
data(){ return{ scrollY:0, tops:[] //存放每一個類的初始位置 } }
第一步:用到better-scroll庫(http://ustbhuangyi.github.io/better-scroll/doc/api.html)
/*下載better-scroll庫*/ npm install better-scroll -S
第二步:在組件中引入這個庫,並且分別創建左右兩個滑動列表,注意這個滑動列表時后台數據異步請求完成之后才創建的,所以在this.$nextTick()里面創建這兩個滑動列表
mounted(){ //異步請求可以傳過去兩個參數, this.$store.dispatch('getShopGoods',()=>{ //數據請求完之后再執行這里了 //初始化左邊滾動 this.$nextTick(()=>{ new BScroll('.menu-wrapper',{ click:true }) //創建右邊的滾動 this.foodswrapper = new BScroll('.foods-wrapper',{ click:true, probeType:3 }) }) }) }
第三步:將獲取當前scrollY的函數,和獲取右側top的函數提取出來,定義在method中
methods:{ //獲取scrollY _initScrollY() //獲取右側tops _initTops() }
第四步:實現_initScrollY()
//初始化BScroll _initScrollY(){ new BScroll('.menu-wrapper',{ click:true //滑動列表默認是沒有點擊的,必須加上這個才能出發點擊事件 }) //創建右邊的 this.foodswrapper = new BScroll('.foods-wrapper',{ click:true, probeType:2 //這里可以取4個值:默認為0, 1, 2 , 3具體解釋參考官方文檔 }) //給右側綁定的BScroll綁定監聽事件,獲取滑動過程中的位置 this.foodswrapper.on('scroll',({x,y})=>{ console.log(x,y)//默認沒有分發滾動事件 this.scrollY=Math.abs(y); }) //獲取停下來的位置 //給右側綁定的BScrollEnd綁定監聽事件,獲取滑動結束時的位置 this.foodswrapper.on('scrollEnd',({x,y})=>{ //console.log(x,y)//默認沒有分發滾動事件 this.scrollY=Math.abs(y); }) }
第五步:實現_initTops()
先分析:

代碼:
//初始化數組,獲取到每個li 的坐標 ,_initTops(){ var tops=[] //定義一個空數組 let top=0; tops[0]=0 //第一個li的坐標為0 var lis = this.$refs.foodsUl.children; //獲取到了每個li Array.prototype.slice.call(lis).forEach((li,index)=>{ top = top + li.clientHeight//當前的位置,等於上一個的位置,加上這一個的高度 tops.push(top) }) this.tops=tops console.log(tops) },
第六步:計算currentIndex
computed:{ currentIndex(){ //如果滑動的位置在當前這個和下一個之間,返回的是這個的下標 /*比如: tops=[0, 5, 12,18,23] 如果scrollY=4 ----返回0 scrollY=8-----返回1 */ return this.tops.findIndex((top,index)=>{ return this.scrollY>=top && this.scrollY<this.tops[index+1] }) } }
有了前面幾步,就可以很容易的實現我們想要的功能
(1)滑動右側,左側高亮,只需在左側加current類的時候,判斷計算的currentIndex是否等於當前的index
<ul>
<!--current-->
<li class="menu-item " v-for="(good,index) in goods" :key="index" :class="{current:index===currentIndex}" @click="clickMenuItem(index)">
<span class="text bottom-border-1px">
<img class="icon" :src="good.icon" v-if="good.icon" >
{{good.name}}
</span>
</li>
</ul>
(2)點擊左側,右側滑動到相應的位置
注冊點擊事件:
<!--current--> <li class="menu-item " v-for="(good,index) in goods" :key="index" :class="{current:index===currentIndex}" @click="clickMenuItem(index)"> <span class="text bottom-border-1px"> <img class="icon" :src="good.icon" v-if="good.icon" > {{good.name}} </span> </li>
methods中定義這個點擊事件‘’
//將當前的index傳進來 clickMenuItem(index){ //先得到目標位置scrollY const top = this.tops[index]; // 立即更新scrollY,更新當前分類,點擊的分類項成為當前 this.scrollY=top //平滑滾動右側列表 this.foodswrapper.scrollTo(0, -top, 3); }
<template>
<div>
<div class="goods">
<div class="menu-wrapper" >
<ul>
<!--current-->
<li class="menu-item " v-for="(good,index) in goods" :key="index" :class="{current:index===currentIndex}" @click="clickMenuItem(index)">
<span class="text bottom-border-1px">
<img class="icon" :src="good.icon" v-if="good.icon" >
{{good.name}}
</span>
</li>
</ul>
</div>
<div class="foods-wrapper">
<ul ref="foodsUl">
<li class="food-list-hook" v-for="(good, index) in goods" :key="index" >
<h1 class="title">{{good.name}}</h1>
<ul>
<li class="food-item bottom-border-1px" v-for="(food, index) in good.foods" :key="index">
<div class="icon">
<img width="57" height="57" :src="food.icon">
</div>
<div class="content">
<h2 class="name">{{food.name}}</h2>
<p class="desc">{{food.description}}</p>
<div class="extra">
<span class="count">月售{{food.sellCount}}份</span>
<span>好評率{{food.rating}}%</span>
</div>
<div class="price">
<span class="now">¥{{food.price}}</span>
<span class="old" v-if="food.oldPrice">¥{{food.oldPrice}}</span>
</div>
<div class="cartcontrol-wrapper">
CartControl
</div>
</div>
</li>
</ul>
</li>
</ul>
</div>
</div>
food
</div>
</template>
<script>
import {mapState} from 'vuex'
import BScroll from 'better-scroll'
export default {
data(){
return{
scrollY:0,
tops:[] //存放每一個類的初始位置
}
},
//這里的數據是異步顯示的,所以我們要等數據異步請求之后再創建這個滑動列表
mounted(){
//異步請求可以傳過去兩個參數,
this.$store.dispatch('getShopGoods',()=>{
//數據請求完之后再執行這里了
//初始化滾動
this.$nextTick(()=>{
//初始化,並且實時獲取滾動坐標
this._initScrollY()
//初始化右邊的數組
this._initTops();
})
})
},
methods:{
//初始化BScroll
_initScrollY(){
new BScroll('.menu-wrapper',{
click:true
})
//創建右邊的
this.foodswrapper = new BScroll('.foods-wrapper',{
click:true,
probeType:3
})
//給右側綁定的BScroll綁定監聽事件,但是你會發現並沒有調用
this.foodswrapper.on('scroll',({x,y})=>{
console.log(x,y)//默認沒有分發滾動事件
this.scrollY=Math.abs(y);
})
//獲取停下來的位置
//給右側綁定的BScroll綁定監聽事件,但是你會發現並沒有調用
this.foodswrapper.on('scrollEnd',({x,y})=>{
//console.log(x,y)//默認沒有分發滾動事件
this.scrollY=Math.abs(y);
})
}
//初始化數組,獲取到每個li 的坐標
,_initTops(){
var tops=[] //定義一個空數組
let top=0;
tops[0]=0 //第一個li的坐標為0
var lis = this.$refs.foodsUl.children; //獲取到了每個li
Array.prototype.slice.call(lis).forEach((li,index)=>{
top = top + li.clientHeight//當前的位置,等於上一個的位置,加上這一個的高度
tops.push(top)
})
this.tops=tops
console.log(tops)
},
//將當前的index傳進來
clickMenuItem(index){
//先得到目標位置scrollY
const top = this.tops[index];
// 立即更新scrollY,更新當前分類,點擊的分類項成為當前
this.scrollY=top
//平滑滾動右側列表
this.foodswrapper.scrollTo(0, -top, 3);
}
},
computed:{
...mapState(['goods']),
currentIndex(){
return this.tops.findIndex((top,index)=>{
return this.scrollY>=top && this.scrollY<this.tops[index+1]
})
}
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import "../../../common/stylus/mixins.styl"
.goods
display: flex
position: absolute
top: 195px
bottom: 46px
width: 100%
background: #fff;
overflow: hidden
.menu-wrapper
flex: 0 0 80px
width: 80px
background: #f3f5f7
.menu-item
display: table
height: 54px
width: 56px
padding: 0 12px
line-height: 14px
&.current
position: relative
z-index: 10
margin-top: -1px
background: #fff
color: $green
font-weight: 700
.text
border-none()
.icon
display: inline-block
vertical-align: top
width: 12px
height: 12px
margin-right: 2px
background-size: 12px 12px
background-repeat: no-repeat
.text
display: table-cell
width: 56px
vertical-align: middle
bottom-border-1px(rgba(7, 17, 27, 0.1))
font-size: 12px
.foods-wrapper
flex: 1
.title
padding-left: 14px
height: 26px
line-height: 26px
border-left: 2px solid #d9dde1
font-size: 12px
color: rgb(147, 153, 159)
background: #f3f5f7
.food-item
display: flex
margin: 18px
padding-bottom: 18px
bottom-border-1px(rgba(7, 17, 27, 0.1))
&:last-child
border-none()
margin-bottom: 0
.icon
flex: 0 0 57px
margin-right: 10px
.content
flex: 1
.name
margin: 2px 0 8px 0
height: 14px
line-height: 14px
font-size: 14px
color: rgb(7, 17, 27)
.desc, .extra
line-height: 10px
font-size: 10px
color: rgb(147, 153, 159)
.desc
line-height: 12px
margin-bottom: 8px
.extra
.count
margin-right: 12px
.price
font-weight: 700
line-height: 24px
.now
margin-right: 8px
font-size: 14px
color: rgb(240, 20, 20)
.old
text-decoration: line-through
font-size: 10px
color: rgb(147, 153, 159)
.cartcontrol-wrapper
position: absolute
right: 0
bottom: 12px
</style>
三、總結
