视图层-Search-pdd项目实现搜索页面左右联动滚动

左右联动滚动

项目源码地址

https://github.com/zhou-X-boy/Vue_study/tree/master/VueProject/pdd


定义数据

在Shop.vue组件中定义数据

1:右侧列表滑动的Y轴坐标

2:右侧所有分类的头部位置

<template>
//见源码
</template>
<script>
//1:引入vuex中从服务器端发送过来的数据,也就是state.js中的数据
import { mapState} from 'vuex';
//引入第三方插件库实现滚动效果
import BScroll from 'better-scroll'
    
 export default {
     name: 'Shop',
     data() {
         return {
             scrollY: 0,  //右侧列表滑动的Y轴坐标
             rightLiTops: []  //右侧所有分类的头部位置
         }
     }
 }
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
//css样式见源码
</style>

求出右侧所有版块的头部位置

1:创建一个方法计算右侧所有版块的头部位置,

2:并在watch中进行监听这个方法,当数据发生变化时才开始执行这个方法,

<template>
	<div class="shop">
        <!--左边-->
        <div class="menu-wrapper">
    	</div>
        <!--右边-->
    	<div class="shop-wrapper">
            <ul ref="shopsParent" >
                <li class="shops-li" v-for="(goods, index1) in searchgoods" :key="index1" >
                  <div class="shops-title">
                    <h4>{{goods.name}}</h4>
                    <a href="">查看更多 ></a>
                  </div>
                  <ul class="phone-type" v-if="goods.tag === 'phone'">
                    <li v-for="(phone,index3) in goods.category" :key="index3" >
                      <img :src="phone.icon" alt="">
                    </li>
                  </ul>
                  <ul class="shops-items">
                    <li v-for="(item, index2) in goods.items" :key="index2">
                      <img :src="item.icon" alt="">
                      <span>{{item.title}}</span>
                    </li>
                  </ul>
                </li>
    	    </ul>
   		</div>
    </div>
</template>
<script>
//1:引入vuex中从服务器端发送过来的数据,也就是state.js中的数据
import { mapState} from 'vuex';
//引入第三方插件库实现滚动效果
import BScroll from 'better-scroll'
    
export default {
 name: 'Shop',
 data() {
   return {
       scrollY: 0,  //右侧列表滑动的Y轴坐标(实时更新)
       rightLiTops: []  //右侧所有分类的头部位置
   }
 },
 methods: {
     //1.2:求出右侧所有版块的头部位置
     _initRightLiTops() {
         //1.2.1:创建临时数组
          const tempArr = [];
          //1.2.2:定义一个新的变量记录高度
          let top = 0;
          //1.2.3:将定义的变量加入到临时数组中
          tempArr.push(top);
          //1.2.4:遍历右侧商品列表class为shops-li的li标签,成为一个类数组
          let allLis = this.$refs.shopsParent.getElementsByClassName('shops-li');
          /*1.2.5:转化为一个真数组,Array.prototype(数组原型) slice.call()方法将类数组转化为真正的数组
           通过forEach()方法遍历这个数组
           */
          Array.prototype.slice.call(allLis).forEach(li => {
            //1.2.6:通过li.clientHeight()方法取得li的高度,并叠加计算每个li的高度
            top += li.clientHeight;
            //1.2.7:将取得的每个li得高度加入到tempArr数组中
            tempArr.push(top);
          });
          //1.2.8:更新数据,将tempArr数组赋值给创建的空数组rightLiTops
          this.rightLiTops = tempArr;
     }
	},
 //监听方法,当数据发生变化时,才被执行
 watch: {
     //监听searchgoods数据变化
     searchgoods() {
       //$nextTick()方法 将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它
       this.$nextTick(()=>{
         //监听右侧所有版块的的头部位置
         this._initRightLiTops();
       })
     }
 }
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
@import "../../../common/stylus/mixins.styl"
//css样式见源码
</style>

求出右侧列表滑动的Y轴坐标(实时更新)

利用better-scroll第三方库中带有的on方法监听滚动事件

<script>
//1:引入vuex中从服务器端发送过来的数据,也就是state.js中的数据
import { mapState} from 'vuex';
//引入第三方插件库实现滚动效果
import BScroll from 'better-scroll'
    
export default {
    name: 'Shop',
    data() {
       return {
           scrollY: 0,  //右侧列表滑动的Y轴坐标(实时更新)
           rightLiTops: []  //右侧所有分类的头部位置
       }
 	},
    methods: {
        //1.1:初始化方法
        _initBScroll() {
          //1.1:左边
          let leftScroll = new BScroll('.menu-wrapper', {
            scrollY: true,  //当设置为 true 的时候,可以开启纵向滚动。
            click: true
          });
          //1.2:左边
            //let rightScroll 改为 this.rightScroll 就讲局部的对象变为全局的对象
          let rightScroll = new BScroll('.shop-wrapper', {
            scrollY: true,  //当设置为 true 的时候,可以开启纵向滚动。
            click: true
          });
          //1.3:右侧滑动事件,通过better-scroll库中带有的on方法
          this.rightScroll.on('scroll',()=>{
              
          })
        }
    },
    width: {
        //监听searchgoods数据变化
        searchgoods() {
          //$nextTick()方法 将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它
          this.$nextTick(()=>{
            //1.1:监听初始化方法
            this._initBScroll();
            //1.2:监听右侧所有版块的的头部位置
            this._initRightLiTops();
          })
        }
    }
}
</script>

显示右侧滚动CSS样式变化

current类:当滚动联动时,左侧被选中的选项的样式变化

<style scoped lang="stylus" rel="stylesheet/stylus">
@import "../../../common/stylus/mixins.styl"
 //其他css样式见源码
.current
  	color #e02e24
  	.current::before   //:before 在元素之前插入内容
      content '' //插入的内容
      background-color #e02e24
      width 4px
      height 30px
      position absolute  //绝对定位
      left 0
</style>

动态绑定类

使class为shops-li的li标签动态绑定current类,当当前li标签的index===currentIndex计算属性获得的当前滚动到的index时,显示current类的样式

<template>
	<div class="shop">
        <!--左边-->
        <div class="menu-wrapper">
            <ul>
                <li class="menu-item" 
                    v-for="(goods, index) in searchgoods" 
                    :key="index" 
                    :class="{current: index === currentIndex}"
                >
                  
                </li>
    	    </ul>
    	</div>
        <!--右边-->
    	<div class="shop-wrapper">
            
   		</div>
    </div>
</template>
<script>
//1:引入vuex中从服务器端发送过来的数据,也就是state.js中的数据
import { mapState} from 'vuex';
//引入第三方插件库实现滚动效果
import BScroll from 'better-scroll'
    
export default {
 name: 'Shop',
 data() {
   return {
       scrollY: 0,  //右侧列表滑动的Y轴坐标(实时更新)
       rightLiTops: []  //右侧所有分类的头部位置
   }
 },
 methods: {
    //1.1:初始化方法
    _initBScroll() {
      //1.2:左边
      //let rightScroll 改为 this.rightScroll 就讲局部的对象变为全局的对象
      this.rightScroll = new BScroll('.shop-wrapper', {
        scrollY: true,  //当设置为 true 的时候,可以开启纵向滚动。
        click: true,
        /*
        * 有时候我们需要知道滚动的位置。
        * 当 probeType 为 1 的时候,会非实时(屏幕滑动超过一定时间后)派发scroll 事件;
        * 当 probeType 为 2 的时候,会在屏幕滑动的过程中实时的派发 scroll 事件;
        * 当 probeType 为 3 的时候,不仅在屏幕滑动的过程中,而且在 momentum 滚动动画运行过程中实时派发 scroll 事件。
        * 如果没有设置该值,其默认值为 0,即不派发 scroll 事件。
        */
        probeType: 3
      });
 },
 //监听方法,当数据发生变化时,才被执行
 watch: {
     
 },
 //计算属性
 computed: {
     //用于动态决定左侧哪个选项被选中
    currentIndex() {
      //1:获取数据
      const {scrollY,rightLiTops} = this;
      //2:遍历rightLiTops数组,取出索引
      /*
      *findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。
      *当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,
      * 如果没有符合条件的元素返回 -1
      */
      return rightLiTops.findIndex((LiTop,index)=> {
        return scrollY >= LiTop && scrollY <= rightLiTops[index + 1]
      })
    }
 }
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
@import "../../../common/stylus/mixins.styl"
//css样式见源码
.current
  	color #e02e24
  	.current::before   //:before 在元素之前插入内容
      content '' //插入的内容
      background-color #e02e24
      width 4px
      height 30px
      position absolute  //绝对定位
      left 0
</style>

实现点击左侧商品列表,右侧商品列表滚动

使用第三方库better-scroll带有的**scrollTo(x, y, time, easing, extraTransform)**方法来实现效果

  • {number} x 横轴坐标(单位 px)
  • {number} y 纵轴坐标(单位 px)
  • {number} time 滚动动画执行的时长(单位 ms)
  • {Object} easing 缓动函数,一般不建议修改,如果想修改,参考源码中的 packages/shared-utils/src/ease.ts 里的写法

监听左侧商品列表的点击事件

<template>
	<div class="shop">
        <!--左边-->
        <div class="menu-wrapper">
            <ul>
                <li class="menu-item" 
                    v-for="(goods, index) in searchgoods" 
                    :key="index" 
                    :class="{current: index === currentIndex}"
                     @click = "clickLeftItem(index)"
                >
                  
                </li>
    	    </ul>
    	</div>
        <!--右边-->
    	<div class="shop-wrapper">
   		</div>
    </div>
</template>
<script>
//1:引入vuex中从服务器端发送过来的数据,也就是state.js中的数据
import { mapState} from 'vuex';
//引入第三方插件库实现滚动效果
import BScroll from 'better-scroll'
    
export default {
 name: 'Shop',
 data() {
       return {
           scrollY: 0,  //右侧列表滑动的Y轴坐标(实时更新)
           rightLiTops: []  //右侧所有分类的头部位置
       }
 },
 methods: {
    //1.1:初始化方法
    _initBScroll() {
      //1.1:左边
      let leftScroll = new BScroll('.menu-wrapper', {
        scrollY: true,  //当设置为 true 的时候,可以开启纵向滚动。
        click: true
      });
      //1.2:右边
      //let rightScroll 改为 this.rightScroll 就讲局部的对象变为全局的对象
      this.rightScroll = new BScroll('.shop-wrapper', {
        scrollY: true,  //当设置为 true 的时候,可以开启纵向滚动。
        click: true,
        /*
        * 有时候我们需要知道滚动的位置。
        * 当 probeType 为 1 的时候,会非实时(屏幕滑动超过一定时间后)派发scroll 事件;
        * 当 probeType 为 2 的时候,会在屏幕滑动的过程中实时的派发 scroll 事件;
        * 当 probeType 为 3 的时候,不仅在屏幕滑动的过程中,而且在 momentum 滚动动画运行过程中实时派发 scroll 事件。
        * 如果没有设置该值,其默认值为 0,即不派发 scroll 事件。
        */
        probeType: 3
      });
      //1.3:右侧滑动事件,通过better-scroll库中带有的on(type, fn, context)方法
      this.rightScroll.on('scroll', (pos)=>{
        //通过Math.abs(int a)方法取得Y轴 ,返回int 值的绝对值。如果参数为非负数,则返回该参数。如果参数为负数,则返回该参数的相反数。
        this.scrollY = Math.abs(pos.y);
        // console.log(this.scrollY)
      });
      this.rightScroll.on('scrollEnd', (pos)=>{
        this.scrollY = Math.abs(pos.y);
      })
    },
 },
 //监听方法,当数据发生变化时,才被执行
 watch: {
     
 },
 //计算属性
 computed: {
    //用于动态决定左侧哪个选项被选中
    currentIndex() {
      //1:获取数据
      const {scrollY,rightLiTops} = this;
      //2:遍历rightLiTops数组,取出索引
      /*
      *findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。
      *当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,
      * 如果没有符合条件的元素返回 -1
      */
      return rightLiTops.findIndex((liTop,index)=> {
        return scrollY >= liTop && scrollY < rightLiTops[index + 1]
      })
    }
 }
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
@import "../../../common/stylus/mixins.styl"
//css样式见源码
</style>

实现滚动右侧商品列表,左侧商品列表滚动

使用第三方库better-scroll带有的**scrollToElement(el, time, offsetX, offsetY, easing)**方法来实现效果

  • {DOM | string} el 滚动到的目标元素, 如果是字符串,则内部会尝试调用 querySelector 转换成 DOM 对象。

  • {number} time 滚动动画执行的时长(单位 ms)

  • {number | boolean} offsetX 相对于目标元素的横轴偏移量,如果设置为 true,则滚到目标元素的中心位置

  • {number | boolean} offsetY 相对于目标元素的纵轴偏移量,如果设置为 true,则滚到目标元素的中心位置

  • {Object} easing 缓动函数,一般不建议修改,如果想修改,参考源码中的 packages/shared-utils/src/ease.ts 里的写法

    <template>
      <div class="shop">
        <!--左边-->
        <div class="menu-wrapper">
          <ul>
            <li class = "menu-item"
                v-for = "(goods, index) in searchgoods"
                :key = "index"
                :class = "{current: index === currentIndex}"
                @click = "clickLeftItem(index)"
                ref = "menulist"
            >
              <span>{{goods.name}}</span>
            </li>
          </ul>
        </div>
        <!--右边-->
        <div class="shop-wrapper">
          <ul ref="shopsParent" >
            
          </ul>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: "Shop",
      data() {
        return {
          scrollY: 0,  //右侧列表滑动的Y轴坐标(实时更新)
          rightLiTops: []  //所有分类的头部位置
        }
      },
      //生命周期函数,组件创建后调用
      mounted() {},
      methods: {
        //1.4:滚动右侧商品列表切换左侧商品列表
        _leftScroll(index){
          //取得左侧li标签
          let menuLists = this.$refs.menulist;
          //取得滚动右侧联动左侧当前索引所在的li标签
          let el = menuLists[index];
          //console.log(el);
          this.leftScroll.scrollToElement(el,300,0,-100);
        }
      },
      //计算属性
      computed: {
        //3:获取搜索列表数据
        ...mapState(['searchgoods']),
        //用于动态决定左侧哪个选项被选中
        currentIndex() {
          //1:获取数据
          const {scrollY,rightLiTops} = this;
          //2:遍历rightLiTops数组,取出索引
          /*
          *findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。
          *当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,
          * 如果没有符合条件的元素返回 -1
          */
          return rightLiTops.findIndex((liTop,index)=> {
            //绑定左侧索引位置滚动
            this._leftScroll(index);
            return scrollY >= liTop && scrollY < rightLiTops[index + 1]
          })
        }
      },
      //监听方法,数据发生变化,才开始执行
      watch: {},
    }
    </script>
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值