【组件】小程序首字母检索

# 主要思路

  • 首字母检索项的值对应数据列的元素的id
  • 通过createSelectorQuery获取检索项每项的坐标信息(因为目前通过touchmove无法直接获取到手指所在元素)
  • 通过点击/滑动获取到首字母检索项的值,点击直接通过点击事件获取检索项的值,滚动通过touchmove获取手指所在的坐标信息,判断是否在某一个检索项范围中
  • 动态设置scroll-viewscroll-into-view (opens new window)的值,达到滚动到对应id的检索数据列

# wxml

<!-- 数据列表两层循环,外层为分组 ,内层为列表 -->
<view>
  <scroll-view scroll-into-view="{{scrollIntoVw}}"
        scroll-y 
        scroll-with-animation
        enable-back-to-top>
    <view wx:for="{{list}}" wx:key="index">
      <!-- 设置id,即对应检索滚动的位置 -->
      <view id="{{item.group}}"     
            class="group-type">{{item.group}}</view>
      <view wx:for="{{item.li}}"
            wx:for-item="itm"
            wx:for-index="idx"
            wx:key="idx"
            data-i="{{index + '_' + idx}}"
            bindtap="onBrand">
        <view class="group-item">
          <view>
            <image></image>
          </view>
          <text>{{itm.name}}</text>
        </view>
      </view>
    </view>
  </scroll-view>
</view>

<!--首字母检索列表-->
<view class="wrap-index" catch:touchmove="touchmove">
  <view class="index {{flag == item ? 'active' : ''}}"
        wx:for="{{abc}}" wx:key="index"
        data-id="{{item}}" bindtap="onIndex">{{item}}</view>
</view>

# wxss

scroll-view {
  height: 100vh; // 设置高度才能滚动
}

.group-type {
  font-size: 24rpx;
  color: #666;
  background-color: #F7F7F7;
  padding: 4rpx 40rpx;
}

.group-item {
  display: flex;
  align-items: center;
  font-size: 28rpx;
  padding: 8rpx 22rpx;
  background-color: #fff;
}

.group-item image {
  width: 80rpx;
  height: 80rpx;
  background-color: #F7F7F7;
  margin-right: 20rpx;
}

.wrap-index {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 16rpx;
  position: fixed;
  top: 0;
  bottom: 0;
  right: 0;
  z-index: 100;
}

.wrap-index>view {
  width: 34rpx;
  height: 34rpx;
  font-size: 24rpx;
  text-align: center;
  color: #666;
  border-radius: 50%;
  padding: 6rpx;
}

.wrap-index>.active {
  color: #fff;
  background-color: #CC2B22;
}

# js

data: {
    flag: 'A', // 设置检索项选中样式
    scrollIntoVw:'', // scroll-into-view
    indexPos: [], // 储存检索项位置信息
    // 列表数据结构
    list: [
      {
        group: 'A', li: [
          { name: '奥迪' },
          { name: '阿尔法·罗密欧' },
          ...
        ]
      }, {
        group: 'B', li: [
          { name: '比亚迪' },
          { name: '宝马' },
          ...
        ]
      }
    ],
    abc: [] // 首字母检索数组,直接定义变量或方法生成
},

touchmove(e) {
    // 节流
    let now = new Date().getTime()
    if (now - timestamp < 50) return
    timestamp = now
    
    // 双指/多指/误触
    if (e.touches.length > 1) return
    
    // 判断手指滑动位置坐标是否在元素top和bottom之间
    let y = e.touches[0].clientY
    let indexPos = this.data.indexPos
    for (const item of indexPos) {
      if (y >= item.top && y <= item.bottom) {
        this.scroll2Vw(item.dataset.id)
        break
      }
    }
},
scroll2Vw(id) {
    this.setData({
      scrollIntoVw: id,
      flag: id
    })
},
// 点击获取id
onIndex(e) {
    let id = e.currentTarget.dataset.id
    this.scroll2Vw(id)
},

onReady() {
    // 存储检索项位置信息
    let self = this
    wx.createSelectorQuery()
      .selectAll('.wrap-index .index')
      .boundingClientRect()
      .exec(function (res) {
        let indexPos = res[0]
        self.setData({ indexPos })
      })
}

# e.touches数据结构

[{
    clientX: 356
    clientY: 155
    force: 1
    identifier: 0
    pageX: 356
    pageY: 155 
}, ...]

# indexPos数据结构

[{
    bottom: 93.5,
    dataset: { id: "A" },
    height: 19,
    id: "",
    left: 381,
    right: 414,
    top: 74.5,
    width: 33
}, ...]