教学项目里做的效果是左侧列表滚动,右边菜单导航条的高亮切换显示,但有个问题,如果高亮的菜单藏在视口范围之下,就看不到当前哪个菜单被高亮了,于是乎需要改进,即当上图左侧列表滚动时,如果当前需要高亮显示的菜单不在可视区域的情况下,自动把右侧菜单导航条滚动,让当前高亮的菜单显示出来。
在实际测试过程中,foodScroll的scroll 事件被频繁触发,为了防止menuScroll.scrollToElement 多次执行,浪费效率,特定义了menuScrollQueue 减少执行的次数,在不是加速上下快速滑动滚动情况下,实际只会执行1到2次,至于完全避免重复执行,尚需对better-scroll深入学习或改进来满足需求。
<
template
>
<
div
class=
"goods"
>
<
div
class=
"menu-wrapper"
ref=
"menuWrapper"
>
<
ul
>
<
li
:key="index"
v-for="(itm,index)
in goods"
class=
"menu-item menu-item-hook"
:class="{
'current':currentIndex===index}"
v-on:click="gotoIndex(index,$event)"
>
<
span
class=
"text border-1px"
>
<
span
class=
"icon"
:class="classMap[itm.type]"
v-show="itm.type>
0"
></
span
>{{itm.name}}
</
span
>
</
li
>
</
ul
>
</
div
>
<
div
class=
"foods-wrapper"
ref=
"foodsWrapper"
>
<
ul
>
<
li
:key="g.name"
v-for="g
in goods"
class=
"food-list-hook"
>
<
h1
class=
"title"
>{{g.name}}
</
h1
>
<
ul
>
<
li
:key="f.name"
v-for="f
in g.foods"
class=
"food-item"
>
<
div
class=
"icon"
>
<
img
width=
"57"
height=
"57"
:src="f.image"
/>
</
div
>
<
div
class=
"content"
>
<
h2
class=
"name"
>{{f.name}}
</
h2
>
<
p
class=
"desc"
>{{f.description}}
</
p
>
<
div
class=
"extra"
>
<
span
class=
"count"
>月售{{f.sellCount}}份
</
span
>
<
span
>好评率{{f.rating}}%
</
span
>
</
div
>
<
div
class=
"price"
>
<
span
class=
"now"
><
i
>¥
</
i
>{{f.price}}
</
span
><
span
class=
"old"
v-show="f.oldPrice"
><
i
>¥
</
i
>{{f.oldPrice}}
</
span
>
</
div
>
<
div
class=
"char-ctrl"
>
<
chartCtrl
:food="f"
></
chartCtrl
>
</
div
>
</
div
>
</
li
>
</
ul
>
</
li
>
</
ul
>
</
div
>
<
shopChart
:delivery-price="seller.deliveryPrice"
:min-price="seller.minPrice"
></
shopChart
>
</
div
>
</
template
>
<
script
>
import BScroll
from
'better-scroll';
import shopChart
from
'../shopchart/shopchart.vue';
import chartCtrl
from
'../chartcontrol/chartcontrol.vue';
let menuScrollQueue;
export
default {
props: [
'seller'],
data () {
return {
goods: [],
classMap: [],
scrollY:
0,
menuScrollY:
0,
foodsClientHeight: [],
currentMenuIndex:
0
};
},
components: {
shopChart,
chartCtrl
},
computed: {
currentIndex: {
get () {
for (
let i =
0; i <
this.foodsClientHeight.length; i++) {
let h1 =
this.foodsClientHeight[i];
let h2 =
this.foodsClientHeight[i +
1];
if (!h2 || (
this.scrollY >= h1 &&
this.scrollY < h2)) {
console.log(
`h1=
${h1
}
,h2=
${h2
}
`);
console.log(
'>>>>>>>>>>>>>>>>>' + i +
' scrollY=' +
this.scrollY);
return i;
}
}
return
0;
}
}
},
created () {
this.classMap = [
'decrease',
'discount',
'guarantee',
'invoice',
'special'];
this.$http.get(global.host.api +
'/getGoods').then((responose)
=> {
this.goods = responose.data;
this.$nextTick(()
=> {
this._initScroller();
this._calculatorFoodsHeight();
});
});
},
methods: {
_initScroller () {
this.menuScroll =
new BScroll(
this.$refs.menuWrapper, {
probeType:
3,
click:
true
});
this.foodScroll =
new BScroll(
this.$refs.foodsWrapper, {
probeType:
3,
click:
true
});
console.log(
'_initScroller..................');
this.menuScroll.on(
'scroll', (pos)
=> {
this.menuScrollY = Math.abs(Math.round(pos.y));
});
this.foodScroll.on(
'scroll', (pos)
=> {
this.scrollY = Math.abs(Math.round(pos.y));
// this.currentMenuIndex = 0;
// 把menu -wrapper中不在当前可视区域的元素自动滚动出来
var mlist =
this.$refs.menuWrapper.getElementsByClassName(
'menu-item-hook');
var mtarget = mlist[
this.currentIndex];
var _cx = mtarget.offsetTop + (mtarget.offsetHeight /
2);
var visibleBottom =
this.menuScrollY +
this.$refs.menuWrapper.clientHeight;
console.log(
'>>>>' +
this.scrollY);
if (_cx <
this.menuScrollY || _cx > visibleBottom) {
if (menuScrollQueue) {
clearTimeout(menuScrollQueue);
}
menuScrollQueue = setTimeout(()
=> {
console.log(mtarget);
this.menuScroll.scrollToElement(mtarget,
300);
},
20);
}
});
},
_calculatorFoodsHeight () {
this.foodsClientHeight = [];
var flist =
this.$refs.foodsWrapper.getElementsByClassName(
'food-list-hook');
this.foodsClientHeight.push(
0);
let _h =
0;
for (
var i =
0; i < flist.length; i++) {
_h += flist[i].clientHeight;
this.foodsClientHeight.push(_h -
1);
// console.log(_h);
}
},
gotoIndex (index, event) {
if (!event._constructed) {
return
false;
}
var flist =
this.$refs.foodsWrapper.getElementsByClassName(
'food-list-hook');
var target = flist[index];
this.foodScroll.scrollToElement(target,
300);
this.currentMenuIndex = index;
// console.log(this.currentIndex);
}
}
};
</
script
>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<
style
scoped
lang=
'stylus'
type=
'text/stylus'
>
@
import
'../../common/stylus/mixin.styl';
.goods
display flex
position absolute
top
168px
bottom
46px
width
100%
overflow hidden
.menu-wrapper
flex
0
0
80px
background-color #f3f5f7
.menu-item
display table
width
56px
height
54px
padding
0
12px
line-height
14px
font-size
0
&.current
position relative
z-index
10
margin-top
-1px
background #fff
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
&.decrease
bg-image(
'decrease_3')
&.discount
bg-image(
'discount_3')
&.guarantee
bg-image(
'guarantee_3')
&.invoice
bg-image(
'invoice_3')
&.special
bg-image(
'special_3')
.text
display table-cell
width
56px
vertical-align middle
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-color #f3f5f7
.food-item
position relative
display flex
margin
18px
padding-bottom
18px
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
.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
400px
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)
.char-ctrl
position absolute
right
0px
bottom
5px
</
style
>