VUE饿了么学习笔记(6)goods界面滚动和点击联动的实现

使用插件Bscroll,在依赖文件package.json中添加版本,在install,run之后使用
1)import BScroll from

2)BScroll实例化的时候要接收一个DOM,此处将实例化并获取DOM的过程封装成方法,获取DOM

<div class="menu-wrapper" ref="menuWrapper">
<div class="foods-wrapper" ref="foodsWrapper">
methods: {
   _initScroll() {
     //v-el(this.$els.foodWrapper)获取DOM对象,json对象
     this.menuScroll = new BScroll(this.$refs.menuWrapper, {
       click: true //阻止默认,允许点击
     });
     this.foodsScroll = new BScroll(this.$refs.foodsWrapper, {
       click: true,
       probeType: 3 //BScroll滚动时,能实时告诉我们滚动的位置,类似探针的效果
     });
     //foodsScroll监听事件,在scroll滚动时能见位置实时暴露出来
    this.foodsScroll.on('scroll', (pos) => {
      this.scrollY = Math.abs(Math.round(pos.y)); //取正值
    });
   }

在created中添加初始化,在this.$nextTick(() => {初始化})

 created() {
   this.classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee'];

    //请求的ajex地址,成功的回调,拿到一个response
    this.$http.get('/api/goods').then((response) => {
      //response是一个属性,将其转化为json对象
      response = response.body;
      //console.log(response);
      if (response.errno === ERR_OK) {
        // 数据在data字段中,将goods对象传给header组件,通过:goods
        this.goods = response.data;
//        console.log(this.goods);
        //初始化betterScroll的时候,DOM的更新时异步的,所以我们在这虽然改变了数据,但是DOM并没有变化,计算不到正确的高度
        this.$nextTick(() => {
           //调用scroll函数,实现滚动
          this._initScroll();
          //拿到数据以后计算高度
          this._calculateHeight();
        }); 
      }
    });
 }

左右两侧都可以产生的滚动效果

3)滚动右侧时左侧相应滚动,计算右侧不同区间高度的一个数组,然后对比当前滑动的坐标落在哪个区间,并取到这个区间的索引值,就可以知道左侧导航栏要高亮的索引值了,利用vue的class绑定令其高亮

在data中定义数组用来存储不同区间的高度

data() {
  return {
    goods: [],
    listHeight: [] //存储区块的高度
  };
}

阿达的为了获取高度,给food-list定义一个class--food-list-hook,不用来编写css,专门用来获取DOM元素,没有实际的效果,只是用来被js选择的

<div class="foods-wrapper" ref="foodsWrapper">
     <ul>
       <li v-for="item in goods" :key="item.id" class="food-list food-list-hook">

foodList拿到了每个li,每个li是包括包括标题在内的每一类food的高度,不是单独的一种good,将_calculateHeight放在nextTick中初始化_initScroll的后面,保证其能正确计算到高度

   _calculateHeight() {
     //food-list-hook类的添加知识为了能拿到food列表,例如,拿到的是多个类似整个粥品的区块
    let foodList = this.$refs.foodsWrapper.getElementsByClassName('food-list-hook');
    let height = 0;
    this.listHeight.push(height); //listHeight是一个递增的区间数组,是每个专区高度的累加
    for (let i = 0; i < foodList.length; i++) {
      let item = foodList[i];
      height += item.clientHeight;
      this.listHeight.push(height);
    }
   }

在data中定义一个scrollY对象,用来跟踪滚动的高度 scrollY:0;在初始化betterScroll时,为右侧添加probeType,可以检测到右侧实时滚动的位置,监听scroll,将其实时滚动的位置暴露出来

this.foodsScroll = new BScroll(this.$refs.foodsWrapper, {
       click: true,
       probeType: 3 //BScroll滚动时,能实时告诉我们滚动的位置,类似探针的效果
     });
     //foodsScroll监听事件,在scroll滚动时能见位置实时暴露出来
    this.foodsScroll.on('scroll', (pos) => {
      this.scrollY = Math.abs(Math.round(pos.y)); //本身是个负值,取正值
    })

4)拿到滚动的高度和内容区的固定高度之后, 查看scrollY落在哪个区间,并返回那个区间的索引(!height2是测试最后一个区间的)其中,>= 向下的是一个闭区间,这样第一个就会高亮了

computed: {
  currentIndex() { //currentIndex对应菜单栏的下标
    for (let i = 0; i < this.listHeight.length; i++) { //不要忘了加this引用
      let height1 = this.listHeight[i];
      let height2 = this.listHeight[i + 1];
      //获得了一个区间的上下范围,判断scrollY落到这个区间,!height2是判断最后一个区间
      //避免i溢出,>= 向下的是一个闭区间,这样第一个就会高亮了
      if (!height2 || (this.scrollY >= height1 && this.scrollY < height2)) {
        return i; //映射到第5行menu的变化
      }
    }
    return 0;
  

拿到index之后,回到左侧的menu区,当我们遍历menu的时候,如果$index等于我们计算得到的currentIndex时,就为当前的li添加一个current样式

<!-- 如果index等于currentIndex,就为这个li添加一个current类,改变左侧导航栏的背景颜色-->
       <li v-for="(item,index) in goods" :key="item.id" class="menu-item" :class="{'current': currentIndex === index}" 
       @click = "selectMenu($index, $event)"> <!--  click所传递的事件-->
        &.current
          position relative
          margin-top -1px
          background #fff
          font-weight 700
          z-index 10
          .text
            border-none()

5)点击左侧右侧滚动
在左侧菜单栏添加点击事件selectMenu, @click = "selectMenu($index, $event)",将index传进去,就可以知道点选的是哪个区域,然后利用原生DOM操作将高度滚动到相应的位置

点击左侧菜单栏的时候没有反应,因为BScroll默认阻止点击事件,所以在 _initScroll()中获取DOM对象时添加click: true,并解决PC端双点击问题,event是点击时的event

    selectMenu (index, event) {
		 if (!event._constructed) { //浏览器直接return掉,去掉自带click事件的点击
			return;
		 }
		let foodList = this.$refs.foodsWrapper.getElementsByClassName('food-list-hook');
		let ref = foodList[index]; //取到index对应的DOM
		this.foodsScroll.scrollToElement(ref, 300);//滚动到DOM所在位置
		//console.log(index);
	  }
	  

猜你喜欢

转载自blog.csdn.net/qq_22317389/article/details/80391080