微信小程序中实现类似 t-indexes 组件的效果,即多条数据分组显示,滚动时下一组的标题置顶,可以通过以下步骤实现

24 阅读1分钟

1. 数据结构

首先,准备分组数据。例如:

Page({
  data: {
    list: [{
        title: 'A',
        items: ['Apple', 'Ant', 'Animal', ...(new Array(100).fill('列表内容-相信光吗'))],
      },
      {
        title: 'B',
        items: ['Banana', 'Bear', 'Ball', ...(new Array(100).fill('列表内容-相信光吗'))],
      },
      {
        title: 'C',
        items: ['Cat', 'Car', 'Cup', ...(new Array(100).fill('列表内容-相信光吗'))],
      },
    ],
    currentTitle: '', // 当前置顶的标题
  },
});

2. 页面布局

在 WXML 中,使用 scroll-view 实现滚动,并通过 wx:for 渲染分组数据:

<view class="container">
  <!-- 置顶标题 -->
  <view class="sticky-title" wx:if="{{currentTitle}}">
    {{currentTitle}}
  </view>

  <!-- 滚动内容 -->
  <scroll-view
    scroll-y
    style="height: 100vh;"
    bindscroll="onScroll"
    scroll-top="{{scrollTop}}"
  >
    <block wx:for="{{list}}" wx:key="title">
      <!-- 分组标题 -->
      <view class="group-title" id="group-{{index}}">
        {{item.title}}
      </view>
      <!-- 分组内容 -->
      <view class="group-item" wx:for="{{item.items}}" wx:key="*this">
        {{item}}
      </view>
    </block>
  </scroll-view>
</view>

3. 样式

在 WXSS 中,设置样式:

/* 置顶标题 */
.sticky-title {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  background-color: #f8f8f8;
  padding: 10px;
  font-weight: bold;
  z-index: 999;
}

/* 分组标题 */
.group-title {
  background-color: #f0f0f0;
  padding: 10px;
  font-weight: bold;
}

/* 分组内容 */
.group-item {
  padding: 10px;
  border-bottom: 1px solid #e0e0e0;
}

4. 实现滚动逻辑

在 JS 中,监听 scroll-view 的滚动事件,计算当前显示的组,并更新置顶标题:

Page({
  data: {
      list: [{
        title: 'A',
        items: ['Apple', 'Ant', 'Animal', ...(new Array(100).fill('列表内容-相信光吗'))],
      },
      {
        title: 'B',
        items: ['Banana', 'Bear', 'Ball', ...(new Array(100).fill('列表内容-相信光吗'))],
      },
      {
        title: 'C',
        items: ['Cat', 'Car', 'Cup', ...(new Array(100).fill('列表内容-相信光吗'))],
      },
    ],
    currentTitle: '', // 当前置顶的标题
    scrollTop: 0, // 滚动位置
    groupTops: [], // 每个组的顶部位置
  },

  onLoad() {
    // 计算每个组的顶部位置
    this.calculateGroupTops();
  },

  calculateGroupTops() {
    const query = wx.createSelectorQuery().in(this);
    const groupTops = [];

    this.data.list.forEach((group, index) => {
      query.select(`#group-${index}`).boundingClientRect((rect) => {
        groupTops[index] = rect.top;
        if (index === this.data.list.length - 1) {
          this.setData({ groupTops });
        }
      }).exec();
    });
  },

  onScroll(e) {
    const scrollTop = e.detail.scrollTop;
    const groupTops = this.data.groupTops;

    // 找到当前显示的组
    for (let i = groupTops.length - 1; i >= 0; i--) {
      if (scrollTop >= groupTops[i]) {
        this.setData({ currentTitle: this.data.list[i].title });
        break;
      }
    }
  },
});

5. 优化

  • 性能优化:如果数据量较大,可以使用虚拟列表(如 recycle-view)优化渲染性能。
  • 动画效果:可以为置顶标题添加动画效果,提升用户体验。
  • 兼容性:确保在不同设备和屏幕尺寸下表现一致。

6. 完整代码

以下是完整的代码示例:

WXML

<view class="container">
  <!-- 置顶标题 -->
  <view class="sticky-title" wx:if="{{currentTitle}}">
    {{currentTitle}}
  </view>

  <!-- 滚动内容 -->
  <scroll-view
    scroll-y
    style="height: 100vh;"
    bindscroll="onScroll"
    scroll-top="{{scrollTop}}"
  >
    <block wx:for="{{list}}" wx:key="title">
      <!-- 分组标题 -->
      <view class="group-title" id="group-{{index}}">
        {{item.title}}
      </view>
      <!-- 分组内容 -->
      <view class="group-item" wx:for="{{item.items}}" wx:key="*this">
        {{item}}
      </view>
    </block>
  </scroll-view>
</view>

WXSS

.container {
  position: relative;
}

.sticky-title {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  background-color: #f8f8f8;
  padding: 10px;
  font-weight: bold;
  z-index: 999;
}

.group-title {
  background-color: #f0f0f0;
  padding: 10px;
  font-weight: bold;
}

.group-item {
  padding: 10px;
  border-bottom: 1px solid #e0e0e0;
}

JS

Page({
  data: {
     list: [{
        title: 'A',
        items: ['Apple', 'Ant', 'Animal', ...(new Array(100).fill('列表内容-相信光吗'))],
      },
      {
        title: 'B',
        items: ['Banana', 'Bear', 'Ball', ...(new Array(100).fill('列表内容-相信光吗'))],
      },
      {
        title: 'C',
        items: ['Cat', 'Car', 'Cup', ...(new Array(100).fill('列表内容-相信光吗'))],
      },
    ],
    currentTitle: '',
    scrollTop: 0,
    groupTops: [],
  },

  onLoad() {
    this.calculateGroupTops();
  },

  calculateGroupTops() {
    const query = wx.createSelectorQuery().in(this);
    const groupTops = [];

    this.data.list.forEach((group, index) => {
      query.select(`#group-${index}`).boundingClientRect((rect) => {
        groupTops[index] = rect.top;
        if (index === this.data.list.length - 1) {
          this.setData({ groupTops });
        }
      }).exec();
    });
  },

  onScroll(e) {
    const scrollTop = e.detail.scrollTop;
    const groupTops = this.data.groupTops;

    for (let i = groupTops.length - 1; i >= 0; i--) {
      if (scrollTop >= groupTops[i]) {
        this.setData({ currentTitle: this.data.list[i].title });
        break;
      }
    }
  },
});

通过以上步骤,你可以在微信小程序中实现类似 t-indexes 的效果。