实现的效果如下图所示:项目来源《Vue.js高仿饿了么实战》

Vue.js实战 better-scroll 应用

这里是对better-scroll 组件的简单应用,可以参考文章:https://www.imooc.com/article/18232#

本文对这个页面功能的better-scroll应用做点小的改进,记录学习过程,分享大家!

教学项目里做的效果是左侧列表滚动,右边菜单导航条的高亮切换显示,但有个问题,如果高亮的菜单藏在视口范围之下,就看不到当前哪个菜单被高亮了,于是乎需要改进,即当上图左侧列表滚动时,如果当前需要高亮显示的菜单不在可视区域的情况下,自动把右侧菜单导航条滚动,让当前高亮的菜单显示出来。

做法如下:

let menuScrollQueue; 

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);
        }

      });

在实际测试过程中,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>&yen;</i>{{f.price}}</span><span class="old" v-show="f.oldPrice"><i>&yen;</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>



相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-06-14
  • 2021-05-10
  • 2021-10-12
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-06-08
  • 2021-08-02
  • 2022-12-23
  • 2021-12-09
  • 2021-06-06
  • 2022-12-23
相关资源
相似解决方案