Vue.js实战 better-scroll 应用

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


这里是对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 >



猜你喜欢

转载自blog.csdn.net/elie_yang/article/details/80417784
今日推荐