小程序实现索引栏遇到的坑

最近在做小程序项目的时候遇到了索引栏功能的东西,该项目是选择车型的需求。先看效果图:
小程序实现索引栏遇到的坑_第1张图片
小程序实现索引栏遇到的坑_第2张图片
小程序实现索引栏遇到的坑_第3张图片
实现上面的功能大概有自定义弹框、点击索引字母跳转到对应的位置、搜索关键字高亮等。下面一个个来实现。

特别说明:刚开始看到这种索引栏组件,就想到了使用vant的indexBar组件,但是后面使用popup组件indexBar共用的时候,点击索引字母是失效的,原因是因为popup的弹出层需要定位导致跳转对应的字母位置失效,很坑!!

css代码

<!-- 蒙层 -->
        <view class="remark" wx:if="{{showBrand}}" bindtap="closeBrand" catchtouchmove="preventTouchMove"></view>
        <!-- 内容 -->
        <view class="re-con" wx:if="{{showBrand}}">
          <view class="cityBox" wx:if="carBrandList">
            <view class="search-city">
              <view class="search-con">
                <icon class="iconfont icon-fangdajing" bindtap="searchKey"></icon>
                <input placeholder="请输入车型搜索" type="text" value="{{searchValue}}" bindinput="getCity"/>
              </view>
            </view>
            <view class="content">
              <view class="all-city">
                <scroll-view class="city-scroll {{isFirstId?'':'city-scoll-height'}}" scroll-y="true" scroll-with-animation="true" scroll-into-view="{{toView}}">
                  <view class="city-list" wx:if="{{!searchList.length}}">
                    <!-- 循环城市列表 start -->
                    <view wx:for="{{carBrandList}}" wx:key="index" id="{{'city'+index}}" catchtap='selectcity' data-initial="{{item.initial}}" class="listGroup">
                      <!-- 字母导航 -->
                      <view class="nav-text">
                        <text class="{{index==currentInitial?'currentClass':''}}">{{item.initial}}</text>
                      </view>
                      <!-- 汽车一级品牌名字 -->
                      <view class="show-city">
                        <radio-group class="radio-group" bindchange="radioChange" wx:if="{{!isFirstId}}">
                          <view class="city-item" wx:for="{{item.carBrandInfoList}}" wx:key="index" data-name="{{item.name}}" data-vendorid="{{item.parentId}}" data-obj="{{item}}" bindtap="clickFirstId">
                            <label for="{{item.carModelId}}">
                              <image src="{{item.logo}}"></image>
                              <text class="text">{{item.name}}</text>
                            </label>
                            <radio class="radio" id="{{item.carModelId}}" hidden="{{car.carModelId!=item.recordId}}" value="{{item.name}}" checked="{{car.carModelId==item.recordId}}" color="#28b8ff"></radio>
                          </view>
                        </radio-group>
                        <block wx:else>
                          <view class="city-item" wx:for="{{item.carBrandInfoList}}" wx:key="index" data-name="{{item.name}}" data-obj="{{item}}" bindtap="clickFirstId">
                            <image src="{{item.logo}}"></image>
                            <text>{{item.name}}</text>
                          </view>
                        </block>
                      </view>
                    </view>
                    <!-- 循环城市列表 end -->
                  </view>
                  <!-- 关键词搜索的列表 -->
                  <view class="city-list" wx:if="{{searchList.length}}">
                    <view wx:for="{{searchList}}" wx:key="index" id="{{'city'+index}}" catchtap='selectcity' data-initial="{{item.initial}}">
                      <view class="show-city">
                        <radio-group class="radio-group" bindchange="radioChange">
                          <view class="city-item" data-name="{{item.fullName}}" data-vendorid="{{item.vendorId}}" data-obj="{{item}}" bindtap="clickFirstId">
                            <label for="{{item.recordId}}">
                              <!-- <text >{{item.fullName}}</text> -->
                              <text wx:for="{{item.fullName}}" wx:key="index" class="{{item ==  searchValue? 'currentClass' : '' }}">{{item}}</text>
                            </label>
                            <radio class="radio" id="{{item.recordId}}" hidden="{{car.carModelId!=item.recordId}}" value="{{item.fullName}}" checked="{{car.carModelId==item.recordId}}" color="#28b8ff"></radio>
                          </view>
                        </radio-group>
                      </view>
                    </view>
                  </view>
                </scroll-view>
              </view>
              <!-- 字母索引 start -->
              <view class="search-nav" wx:if="{{isFirstId&&!searchList.length}}">
                <text bindtap="cityScroll" data-initial="{{item.initial}}" data-index="{{index}}" wx:for="{{carBrandList}}" wx:key="index" class="navlist {{index==currentInitial?'currentClass':''}}" catchtouchmove="preventTouchMove">{{item.nav}}</text>
              </view>
              <!-- 字母索引 end -->
            </view>
          </view>
          <!-- 取消确定 -->
          <view class="foot-btn" wx:if="{{!isFirstId}}">
            <view>
              <view class="cancel" bindtap="clickReset">重置</view>
              <view class="comfir" bindtap="clickComfir">确认</view>
            </view>
          </view>
        </view>

js代码

1、点击右边索引字母实现跳转到对应的位置

// 点击英文字母进行跳转到相应位置
  cityScroll(e) {
    let data = this.data.carBrandList
    let Index = e.currentTarget.dataset.index
    let initial = data[Index].initial
    console.log(initial)
    if (data.length) {
      data.map((res, index) => {
        if (initial == res.initial) {
          this.setData({
            currentInitial: index,
            toView: `city${index}`//用于定位
          })
        }
      })
    }
    wx.showToast({
      title: initial,
      icon: 'none'
    })
  },

上面的toView是利用小程序的scroll-view组件的scroll-into-view属性(值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素)将每一块的车型(首字母为A的为一块)的顶层元素设置一个id,对应上面绑定的id="{{‘city’+index}}" 点击的时候可以获取到当前的index,然后实现跳转。

2、搜索关键词高亮

//搜索关键词
  searchKey() {
    this.setData({
      isFirstId: false
    })
    if (this.data.searchValue) {
      netRequest.webRequest(getApi.getKeyWords, {
        keyWords: this.data.searchValue
      }, "get").then(res => {
        let data = res.data
        if (data.length) {
          data.map(item => {
            item.fullName = this.getSpit(item.fullName, item.brandName)
          })
          this.setData({
            searchList: data
          })
          this.searchTap()
        }
      })
    } else {
      this.setData({
        searchList: '',
        carBrandList: this.data.copyBrand,
        isFirstId: true
      })
    }
  },
  //处理关键词高亮
  searchTap: function() {
    var that = this;
    that.setData({
      listDataCopy: that.data.searchList//搜索关键词后台返回的搜索列表
    })
    var data = that.data.searchList;
    var newData = that.data.listDataCopy;
    for (var i = 0; i < data.length; i++) {
      var dic = data[i]
      var newDic = newData[i]
      var fullName = dic["fullName"]
      newDic["fullName"] = this.getInf(fullName, that.data.searchValue);
    }
    that.setData({
      searchList: newData,
    })
    console.log(this.data.searchList)
  },
   //整理关键词
  getInf(str, key) {
    return str.toString().replace(new RegExp(`${key}`, 'g'), `%%${key}%%`).split('%%')
  },

实现思路:首先根据关键词去获取后台返回的搜索值列表,然后遍历这个数组,拿到需要展示的属性,例子是fullName,将搜索关键词与遍历的fullName传入getInf函数,该函数实现在fullName中找到(fullName中一定有关键词字符串,因为是根据关键词搜索出来的值)找到之后将fullName分隔开,如fluuName=“宝马X5M”,关键词为“宝马”,那么getInf返回的是["",“宝马”,“X5M”]的数组,然后在界面遍历这个数组,并且遍历的item如果跟搜索值"宝马"相同那么添加高亮的calss,上例为数组的第二个(下标为1)会添加高亮的calss,从而实现关键词“宝马”高亮的效果。

扩展功能

实现手指滑动右侧的索引字母列表跳转到对应的位置
本项目因为scroll-view组件的原因,存在动画延迟跳转所以为实现这个功能。可以参考以下思路:
想要手指滑动索引跳转到对应位置需要计算每一块的高度,并获取计算出,触摸的pageY和触摸时的一直变化的pageY的差值/每个索引元素的高度,代码如下:

 //获取每一项组合的高度(根据首字母分组)
  calculateHeight() {
    let that = this
    const query = wx.createSelectorQuery()
    const list = query.selectAll('.listGroup')
    if (this.data.isFirstId) {
      const height = query.select('.navlist')
      //获取第一个字母索引元素的高度
      height.boundingClientRect(function(rect) {
        that.setData({
          height: rect.height
        })
      }).exec()
    }
    let arr = []
    list.boundingClientRect(function(rect) {
      if (rect.length) {
        rect.map(res => {
          arr.push(res.height)
        })
        that.setData({
          listHeight: arr
        })
        console.log(arr)
      }
    }).exec()
  },

上图的wx.createSelectorQuery是小程序自带获取demo元素的api,传送门=>boundingClientRect
该api可以直接获取元素的高度、宽度、left、right、top、bottom等等信息。

结语

划水结束。趁空闲时间写的,有点赶,如有错误请联系我!本人微信:huang009516

你可能感兴趣的:(项目总结)