美文网首页小程序学习
微信小程序实现左右联动的菜单列表

微信小程序实现左右联动的菜单列表

作者: beatzcs | 来源:发表于2019-08-07 17:36 被阅读0次

    实现效果如下:


    效果展示.gif

    实现左右联动的菜单列表,主要依靠scroll-view的是三个属性:
    scroll-top:设置竖向滚动条位置(可视区域最顶部到scroll-view顶部的距离);
    scroll-into-view:值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素;
    bindscroll:滚动时触发,event.detail = {scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY}

    结构图示:


    布局.png

    wxml:

    <!-- tabs -->
    <view class='tabs font28 color9'>
      <view class='tab-item {{currentTab==0?"tab-active":""}}' bindtap='changeTab' data-pos='0'>选择项目</view>
      <view class='tab-item {{currentTab==1?"tab-active":""}}' bindtap='changeTab' data-pos='1'>选择技师</view>
      <view class='tab-item {{currentTab==2?"tab-active":""}}' bindtap='changeTab' data-pos='2'>优惠券</view>
    </view>
    
    <!-- 选择项目 -->
    <view wx:if="{{currentTab==0}}" class='cont-pro'>
      <!-- 左侧列表 -->
      <view class='pro-left font28 color9'>
        <view wx:for="{{serviceTypes}}" class='pro-title {{index==currentLeft?"font30 color3 bgWhite":""}}' bindtap='proItemTap' data-pos='{{index}}'>{{item.type}}</view>
      </view>
      <!-- 右侧列表 -->
      <scroll-view class='pro-right' scroll-y scroll-with-animation="true" scroll-into-view="{{selectId}}" bindscroll="scrollEvent" scroll-top="{{scrollTop}}">
        <!-- id要用来实现点击左侧右侧滚动至相应位置的效果;class(pro-box)要用来计算右侧对应左侧某一分类的高度 -->
        <!-- id: item0, item1, item2... (注意:不能直接使用数字或汉字做id)-->
        <view id='{{"item"+index}}' class='pro-box' wx:for="{{serviceTypes}}" wx:for-index="index" wx:for-item="item">
          <!-- 右侧列表里的标题,高度为50px -->
          <view class="item-title font30">{{item.type}}</view>
          <view class='pro-item' wx:for="{{item.services}}" wx:for-index="idx" wx:for-item="itemName">
            <image class='pro-img' src='{{itemName.img}}'></image>
            <view class='pro-text'>
              <view class='item-name color3 font32'>{{itemName.name}}</view>
              <view class='pro-tag'>
                <text wx:for="{{itemName.label}}" wx:for-item="tag">{{tag}}</text>
              </view>
              <view class='pro-bottom'>
                <text style='color:#C93131;' class='font32'>¥{{itemName.price}}</text>
                <view class='count font30 color6'>
                  <text catchtap='subCount' data-pos='{{idx}}' data-index='{{index}}' data-sid='{{itemName.id}}'>-</text>
                  <text class='color3'>{{itemName.count?itemName.count:0}}</text>
                  <text catchtap='addCount' data-pos='{{idx}}' data-index='{{index}}' data-sid='{{itemName.id}}'>+</text>
                </view>
              </view>
            </view>
          </view>
        </view>
      </scroll-view>
    </view>
    
    <!-- 选择技师 -->
    <view wx:if="{{currentTab==1}}" class='staff'>
      ...
    </view>
    
    <!-- 优惠券-->
    <view wx:if="{{currentTab==2}}" class='coupon'>
      ...
    </view>
    

    js:

    var app = getApp();
    Page({
      //右侧分类的高度累加数组
      //比如:[洗车数组的高度,洗车+汽车美容的高度,洗车+汽车美容+精品的高度,...]
      heightArr: [],
      //记录scroll-view滚动过程中距离顶部的高度
      distance: 0,
    
      /**
       * 页面的初始数据
       */
      data: {
        currentTab: 0,  //选择项目、选择技师、优惠券
        currentLeft: 0, //左侧选中的下标
        selectId: "item0",  //当前显示的元素id
        scrollTop: 0, //到顶部的距离
        serviceTypes: [], //项目列表数据
        staffList: [],
        coupons: []
      },
    
      /**
       * 生命周期函数--监听页面加载
       */
      onLoad: function(options) {
        this.request();
      },
    
      //请求列表数据
      request() {
        app.HttpClient.request({url: "services"}).then((res) => {
          console.log(res);
          this.setData({
            serviceTypes: res.data.serviceTypes,
            staffList: res.data.staffList,
            coupons: res.data.coupons
          });
          this.selectHeight();
        })
      },
    
      //选择项目左侧点击事件 currentLeft:控制左侧选中样式  selectId:设置右侧应显示在顶部的id
      proItemTap(e) {
        this.setData({
          currentLeft: e.currentTarget.dataset.pos,
          selectId: "item" + e.currentTarget.dataset.pos
        })
      },
    
      //计算右侧每一个分类的高度,在数据请求成功后请求即可
      selectHeight() {
        let that = this;
        this.heightArr = [];
        let h = 0;
        const query = wx.createSelectorQuery();
        query.selectAll('.pro-box').boundingClientRect()
        query.exec(function(res) {
          res[0].forEach((item) => {
            h += item.height;
            that.heightArr.push(h);
          })
          console.log(that.heightArr);
          // [160, 320, 1140, 1300, 1570, 1840, 2000]
          // 160:洗车标题高度50px,item的高度110,洗车只有一个item,所以50+110*1=160px;
          // 320: 汽车美容标题高度50px,只有一个item,再加上洗车的高度,所以50+110*1+160=320px;
          // ...
        })
      },
    
      //监听scroll-view的滚动事件
      scrollEvent(event) {
        if (this.heightArr.length == 0) {
          return;
        }
        let scrollTop = event.detail.scrollTop;
        let current = this.data.currentLeft;
        if (scrollTop >= this.distance) { //页面向上滑动
          //如果右侧当前可视区域最底部到顶部的距离 超过 当前列表选中项距顶部的高度(且没有下标越界),则更新左侧选中项
          if (current + 1 < this.heightArr.length && scrollTop >= this.heightArr[current]) {
            this.setData({
              currentLeft: current + 1
            })
          }
        } else { //页面向下滑动
          //如果右侧当前可视区域最顶部到顶部的距离 小于 当前列表选中的项距顶部的高度,则更新左侧选中项
          if (current - 1 >= 0 && scrollTop < this.heightArr[current - 1]) {
            this.setData({
              currentLeft: current - 1
            })
          }
        }
        //更新到顶部的距离
        this.distance = scrollTop;
      }
    })
    

    数据结构:


    数据结构.png

    如果你还想实现从其他页面,点击按钮跳转到当前页面,并且列表滚动到指定项,此项在可视区域的第一个展示:

      //优惠券点击事件
      couponTap(e) {
        let item = e.currentTarget.dataset.item;
        if (item.limitServiceName) {
          this.setData({
            currentTab: 0
          })
          this.scrollTo(item.limitServiceName);
        }
      },
    
      //滚动到指定名称的某一项(通过列表的商品name来判断,也可以用id或者其他的,只要是列表项的唯一标志)
      scrollTo(name) {
        let that = this;
        const query = wx.createSelectorQuery()
        query.select(".pro-item").boundingClientRect()
        //计算每一个item的高度(右侧分类的小标题高度是在css里写死的50px)
        query.exec(function(res) {
          that.moveHeight(res[0].height, name);
        })
      },
    
      moveHeight(height, name) {
        let list = this.data.serviceTypes;
        let top = 50; //右侧每一分类的标题名称的高度为50px,top记录每一个标题到顶部的距离
        for (let i = 0; i < list.length; i++) {
          for (let j = 0; j < list[i].services.length; j++) {
            //如果当前的item是要滚动到顶部的,
            if (list[i].services[j].name == name) {
              this.setData({
                scrollTop: height * j + top
              })
              break;
            }
          }
          //右侧每划过一个分类,就把此分类的高度和标题的高度累加到top上
          top = top + list[i].services.length * height + 50;
        }
      }
    
    moveTo.gif

    wxss:

    .cont-pro {
      height: 100%;
      display: flex;
      background-color: #fff;
    }
    
    .pro-left {
      width: 160rpx;
      flex-basis: 160rpx;
      background-color: #f6f6f6;
      overflow-y: scroll;
    }
    
    .pro-title {
      width: 100%;
      height: 100rpx;
      line-height: 100rpx;
      text-align: center;
    }
    
    .pro-right {
      flex: 1;
      background-color: #fff;
      overflow-y: scroll;
    }
    
    .item-title {
      width: 100%;
      height: 50px;
      line-height: 100rpx;
      padding: 0 30rpx;
      box-sizing: border-box;
    }
    
    .item-name {
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 1;
      overflow: hidden;
      word-break: break-all;
    }
    
    .pro-item {
      width: 100%;
      display: flex;
      padding: 30rpx;
      box-sizing: border-box;
    }
    
    .pro-img {
      width: 160rpx;
      height: 160rpx;
      flex-basis: 160rpx;
      flex-shrink: 0;
      border-radius: 4rpx;
      margin-right: 30rpx;
      background-color: #f5f5f5;
    }
    
    .pro-text {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
    }
    
    .pro-tag {
      color: #f08d31;
      font-size: 22rpx;
    }
    
    .pro-tag text {
      padding: 4rpx 10rpx;
      background-color: rgba(240, 141, 49, 0.15);
      margin-right: 10rpx;
      border-radius: 2rpx;
    }
    
    .pro-bottom {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    
    .count {
      width: 170rpx;
      flex-basis: 170rpx;
      background-color: #f6f6f6;
      border-radius: 28rpx;
      display: flex;
    }
    
    .count text {
      flex: 1;
      text-align: center;
      line-height: 50rpx;
    }
    

    相关文章

      网友评论

        本文标题:微信小程序实现左右联动的菜单列表

        本文链接:https://www.haomeiwen.com/subject/mbwodctx.html