移动端开发---菜单栏与商品左右联动

效果图

在这里插入图片描述

用到的插件 — better-scroll

在项目中引入两个包

  1. better-scroll 滚动的核心库
  2. @better-scroll/mouse-wheel 该插件的作用是可以使用鼠标滚动

代码实现

使用vue2结合better-scroll实现菜单与商品左右联动

这里截取better-scroll官网的说明,有兴趣可以去阅读官网:https://www.wenjiangs.com/doc/2fh58adz
在这里插入图片描述

  1. html部分
<!-- 注意事项
	1.BetterScroll 默认处理容器的第一个子元素的滚动,其它的元素都会被忽略。按照我下面的HTML嵌套结构
	2.必须给容器设置高度,且overflow为hidden
	3.如果容器外层还有父元素,父元素也必须设置高度
	4.满足以上条件better-scroll的滚动效果才能生效
-->


<!-- 左侧导航栏 -->
<div ref="siderMenu" class="pkg-content-detail__left">
  <div class="sider-menu">
    <div
      :key="index"
      :class="index === activeKey ? 'active' : ''"
      v-for="(menu, index) in menuList"
      ref="menuItem"
      class="menu-item"
      @click="handleClickMenu(index)"
    >
   	  <span>{{ menu.name}}</span>
    </div>
    <!-- 占位 -->
    <div :style="{ height: calc(100% - customHeight) }" v-if="menuList.length < 10" style="background: #f7f7f7;"></div>
  </div>
</div>
<!-- 右侧商品区域 -->
<div ref="goodsContainer" class="pkg-content-detail__right">
  <div ref="pkgItemContainer" class="content">
    <div
      :key="index"
      v-for="(good, index) in goodsList"
      class="pkg-content-detail__right-item"
    >
      <!-- 该组件为商品的详情组件,可根据自己的需求自行封装 -->
      <add-goods-item :goodsGroup="good"/>
    </div>
    <!-- 这里是滑动到丢展示的文字,样式可以自行调整 -->
    <div style="padding: 30px 0 310px; color: #666; font-size: 14px; text-align: center;">已经到底啦</div>
  </div>
</div>
  1. js部分
// 引入插件
import BScroll from 'better-scroll'
import MouseWheel from '@better-scroll/mouse-wheel'
BScroll.use(MouseWheel)
data() {
	return {
		menuList: [
			{
	          name: '推荐'
	        },
	        {
	          name: '中医科检查室'
	        },
	        {
	          name: '人体成分'
	        },
	        {
	          name: '核医学科'
	        },
	        {
	          name: '骨密度室'
	        },
	        {
	          name: '病理科'
	        },
	        {
	          name: '动脉硬化'
	        },
	        {
	          name: '心电室'
	        },
	        {
	          name: '口腔科'
	        },
	        {
	          name: '化验室'
	        }
		], // 菜单列表
		goodsList: [], // 商品列表 --- 可根据自己的需求添加商品数据
		goodsTops: [], // 左侧菜单对应的商品高度
        menuTops: [], // 左侧菜单的高度
        goodsScrollY: 0, // 商品滚动条高度
        menuScrollY: 0, // 左侧菜单滚动条高度
        goodsScroll: null, // 商品滚动条容器
        menuScroll: null, // 菜单滚动条容器
	} 
},
mounted() {
	this.$nextTick(() => {
        this._initScroll()
        this._initTops()
    })
},
watch: {
    activeKey: {
      immediate: true,
      handler (newVal, oldVal) {
        // 这里是让商品滚动的过程中也让菜单跟着滚动
        if (newVal > 5 && newVal !== oldVal) {
          this.menuScroll.scrollTo(0, -this.menuTops[this.activeKey - 6], 300)
        } else if (newVal <= 5 && this.menuScrollY > 0) {
          this.menuScroll.scrollTo(0, 56, 300)
        }

        // 为当前点击菜单的上一个兄弟与下一个兄弟添加圆角
        this.$nextTick(() => {
          let arr = Array.from(this.$refs.menuItem)
          arr.forEach(item => {
            item.style.borderRadius = '0'
            if (!item.className.includes('active')) {
              item.style.background = '#f7f7f7'
            } else {
              item.style.background = '#ffffff'
            }
          })
          if (arr[newVal].nextSibling && arr[newVal].nextSibling.className?.includes('menu-item')) {
            arr[newVal].nextSibling.style.borderRadius = '0 12px 0 0'
          }
          if (arr[newVal].previousSibling) {
            arr[newVal].previousSibling.style.borderRadius = '0 0 12px 0'
          }
        })
      }
    }
},
computed: {
	// 计算当前分类的下标
    activeKey () {
      // 得到条件数据
      let { goodsScrollY, goodsTops } = this
      // 根据条件计算产生一个结果
      // findIndex方法常用来查找数组中满足条件的第一项元素的下标
      // 返回的是满足条件的第一项元素的下标,这要注意的是findIndex会给数组中的每一项执行一个函数来判断是否满足表达式,如果满足条件后,剩下的元素则不再执行
      let index = goodsTops.findIndex((top, index) => {
        // scrollY要大于等于当前top值,还要小于下一个top
        return goodsScrollY >= top && goodsScrollY < goodsTops[index + 1]
      })
      // 返回结果
      if (index === -1) {
        index = 0
      }
      return index
    },
},

methods: {
	// 初始化滚动
	_initScroll () {
	  // 商品滚动配置
	  this.goodsScroll = new BScroll(this.$refs.goodsContainer, {
	    // 惯性滑动被触发
	    probeType: 3,
	    click: true,
	    bounce: false,
	    // 开启鼠标滚动
	    mouseWheel: {
	      speed: 20,
	      invert: false,
	      easeTime: 300
	    }
	  })
	  this.goodsScroll.on('scroll', ({ y }) => {
	    // math.abs绝对值
	    this.goodsScrollY = Math.abs(y)
	  })
	  this.goodsScroll.on('scrollEnd', ({ y }) => {
	    this.goodsScrollY = Math.abs(y)
	  })
	
	  // 菜单栏滚动配置
	  this.menuScroll = new BScroll(this.$refs.siderMenu, {
	    // 惯性滑动被触发
	    probeType: 3,
	    click: true,
	    bounce: false,
	    // 开启鼠标滚动
	    mouseWheel: {
	      speed: 20,
	      invert: false,
	      easeTime: 300
	    }
	  })
	  this.menuScroll.on('scroll', ({ y }) => {
	    // math.abs绝对值
	    this.menuScrollY = Math.abs(y)
	  })
	  this.menuScroll.on('scrollEnd', ({ y }) => {
	    this.menuScrollY = Math.abs(y)
	  })
	},
	// 初始化tops
	_initTops () {
	  // 商品的tops --- 也就是每个菜单对应的商品所占的高度:[0, 141, 337, 533, 729, 925, 1218, 1414, 1635, 1831, 11377, 11573, 13371, 13689, 13885, 14106, 16145, 17361, 17557, 18069, ......]
	  const goodsTops = []
	  let top1 = 0
	  goodsTops.push(top1)
	  // 找到所有分类的item
	  const goodsItems = this.$refs.pkgItemContainer.getElementsByClassName('pkg-content-detail__right-item')
	  Array.prototype.slice.call(goodsItems).forEach(item => {
	    top1 = top1 + item.clientHeight
	    goodsTops.push(top1)
	  })
	  this.goodsTops = goodsTops
	
	  // 菜单的tops --- 菜单的高度数组:[56, 112, 168, 224, 280, 336, 392, 448, 504, 560, 616, 672, 728, 784, 840, 896, 952, 1008, ......]
	  const menuTops = []
	  let top2 = 0
	  goodsTops.push(top2)
	  // 找到所有分类的item
	  const items = this.$refs.siderMenu.getElementsByClassName('menu-item')
	  Array.prototype.slice.call(items).forEach(item => {
	    top2 = top2 + item.clientHeight
	    menuTops.push(top2)
	  })
	  this.menuTops = menuTops
	}
},
// 点击左侧列表右侧滚动到相应位置
handleClickMenu (index) {
  // 得到目标scrollY
  const y = this.goodsTops[index]
  //   立即更新scrollY
  this.goodsScrollY = y
  //   平滑滑动右侧列表
  if (index === 0) {
    this.goodsScroll.scrollTo(0, -y, 300)
  } else {
    this.goodsScroll.scrollTo(0, -(y + 10), 300)
  }
},
  1. scss部分
pkg-content-detail__left {
  flex: 1;
  height: 100%;
  overflow: hidden;
  .sider-menu {
    .menu-item {
      position: relative;
      display: flex;
      align-items: center;
      width: 100%;
      height: 112px;
      padding: 0 25px;
      color: #666666;
      font-weight: 400;
      font-size: 32px;
      background: #F7F7F7;
    }
    .active {
      color: #318FF5;
      font-weight: 600;
      background: #fff;
    }
    .active + .menu-item {
      border-radius: 0 12px 0 0;
    }
  }
}
pkg-content-detail__right {
  flex: 3;
  height: 100%;
  padding: 0 14px;
  overflow: hidden;
  background: #fff;
  .content {
    padding-top: 10px;
  }
  &-item:not(:first-child) {
    padding-top: 30px;
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值