小程序实现鼠标上下滚动列表和tab标签联动的效果

微信   2024-12-28 09:18   94   0  

4779_pajc_3629.png

顶部的头部区域不跟随列表滚动; 头部区域以下属于滚动区域。


原理介绍

这个地方的实现主要借助了微信小程序原生的scroll-view组件。

使用它的 scroll-into-view 属性,可以实现点击顶部的tab栏,将页面滚动到指定的列表位置;

使用 bindscroll 事件,可以知道当前页面滚动的距离,根据滚动的距离做tab栏的切换操作;


页面布局代码

先说下界面的整体布局,主要分为两部分,头部固定区域 + 可滚动列表区域。

可滚动的列表区域的标题栏当滚动一定的距离后,它也要固定在顶部。

index.wxml

<view class="list">
  <!--顶部固定区域-->
  <view style="height:88rpx;width:100%;background-color: burlywood;text-align:center;">头部区域</view>
  <!--可滚动区域-->
  <scroll-view scroll-y="true" style="width:100%;height:{{scrollAreaHeight}}px;" bindscroll="scroll" scroll-into-view="{{scrollToItem}}" scroll-with-animation="true" scroll-top="{{scrollTop}}">
    <!--水平滚动的tab栏-->
    <scroll-view scroll-x="true" style="height: 88rpx;width: 100%;">
      <view class="head-area {{float?'head-float':''}}">
        <view class="head-area-item {{curSelectTab === index?'head-area-item-select':''}}" wx:for="{{appGroupList}}" bindtap="tabClick" data-index="{{index}}">
          {{item.name}}
        </view>
      </view>
    </scroll-view>
    <!--数据列表-->
    <view class="list-group" style="height: {{listGroupHeight}}px;">
      <view class="list-group-item" id="v_{{index}}" wx:for="{{appGroupList}}" data-index="{{index}}">
        <view class="group-name">
          {{item.name}}
        </view>
        <view class="group-children">
          <view wx:for="{{item.children}}" class="group-children-item" style="width: {{itemWidth}}px;">
            <image src="{{item.url}}"></image>
            <view>{{item.name}}</view>
          </view>
        </view>
      </view>
    </view>
  </scroll-view>
</view>


index.js

Page({
  heightArr: [],
  //记录scroll-view滚动过程中距离顶部的高度
  distance: 0,
  data: {
    appGroupList: [
      {
        name: "分组01",
        children: [{
            "name": "测试0",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试1",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试2",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试3",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试4",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试5",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试6",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试7",
            "url": "/images/bluetooth.png"
          }
        ]
      },
      {
        name: "分组02",
        children: [{
            "name": "测试0",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试1",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试2",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试3",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试4",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试5",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试6",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试7",
            "url": "/images/bluetooth.png"
          }
        ]
      },
      {
        name: "分组03",
        children: [{
            "name": "测试0",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试1",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试2",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试3",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试4",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试5",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试6",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试7",
            "url": "/images/bluetooth.png"
          }
        ]
      },
      {
        name: "分组04",
        children: [{
            "name": "测试0",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试1",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试2",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试3",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试4",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试5",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试6",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试7",
            "url": "/images/bluetooth.png"
          }
        ]
      },
      {
        name: "分组05",
        children: [{
            "name": "测试0",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试1",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试2",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试3",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试4",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试5",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试6",
            "url": "/images/bluetooth.png"
          },
          {
            "name": "测试7",
            "url": "/images/bluetooth.png"
          }
        ]
      },
    ],
    itemWidth: wx.getSystemInfoSync().windowWidth / 4,
    scrollAreaHeight: wx.getSystemInfoSync().windowHeight - 44,
    float: false,
    curSelectTab: 0,
    scrollToItem: null,
    scrollTop: 0, //到顶部的距离
    listGroupHeight: 0,
  },
  onReady: function () {
    this.cacluItemHeight();
  },
  scroll: function (e) {
    console.log("scroll:", e);
    if (e.detail.scrollTop >= 44) {
      this.setData({
        float: true
      })
    } else if (e.detail.scrollTop < 44) {
      this.setData({
        float: false
      })
    }
    let scrollTop = e.detail.scrollTop;
    let current = this.data.curSelectTab;
    if (scrollTop >= this.distance) {
      //页面向上滑动
      //列表当前可视区域最底部到顶部的距离 超过 当前列表选中项距顶部的高度(且没有下标越界),则更新tab栏
      if (current + 1 < this.heightArr.length && scrollTop >= this.heightArr[current]) {
        this.setData({
          curSelectTab: current + 1
        })
      }
    } else {
      //页面向下滑动
      //如果列表当前可视区域最顶部到顶部的距离 小于 当前列表选中的项距顶部的高度,则切换tab栏的选中项
      if (current - 1 >= 0 && scrollTop < this.heightArr[current - 1]) {
        this.setData({
          curSelectTab: current - 1
        })
      }
    }
    //更新到顶部的距离
    this.distance = scrollTop;
  },
  tabClick(e) {
    this.setData({
      curSelectTab: e.currentTarget.dataset.index,
      scrollToItem: "v_" + e.currentTarget.dataset.index
    })
  },
  //计算每一个item高度
  cacluItemHeight() {
    let that = this;
    this.heightArr = [];
    let h = 0;
    const query = wx.createSelectorQuery();
    query.selectAll('.list-group-item').boundingClientRect()
    query.exec(function (res) {
      res[0].forEach((item) => {
        h += item.height;
        that.heightArr.push(h);
      })
      console.log(that.heightArr);
      that.setData({
        listGroupHeight: that.heightArr[that.heightArr.length - 1]
      })
    })
  },
})


index.wxss

.list {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}
.head-area {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  height: 88rpx;
  width: 100%;
  padding: 0 10;
}
.head-area-item {
  display: flex;
  height: 88rpx;
  text-align: center;
  width: 150rpx;
  align-items: center;
  justify-content: center;
}
.head-area-item-select {
  color: #09bb07;
}
image {
  width: 88rpx;
  height: 88rpx;
}
.list-group {
  display: flex;
  width: 100%;
  height: 1000%;
  flex-direction: column;
}
.list-group-item {
  display: flex;
  width: 100%;
  background-color: #aaa;
  flex-direction: column;
}
.group-name {
  height: 88rpx;
  display: flex;
  text-align: center;
  align-items: center;
  margin-left: 20rpx;
}
.group-children {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  width: 100%;
}
.group-children-item {
  height: 160rpx;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.head-float {
  position: fixed;
  top: 88rpx;
  background-color: #ffffff;
}

在逻辑代码中最主要的有两个地方:

    1、cacluItemHeight  计算列表中item的高度数组,并将最终计算的结果保存在 heightArr数组中。

heightArr数组中的每一项的值是在前一项的基础之上进行累加。

2、scroll 中判断当前的滚动方向,根据滚动判断当前的方向,然后根据滚动的距离设置当前选择的tab。

    好了,就这么多,基于以上的内容基本可以实现想要的滚动联动、切换tab联动效果。


来源:https://www.finclip.com/news/f/85230.html

博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。