小程序实现通讯录带字母导航的滚动列表

微信   2025-01-01 12:01   82   0  

14529_3gjg_6633.gif

下面先把列表滚动的组件代码贴出来。

wxml代码如下:

<view class="c-alphabet-nav" style="height: {{ height }}">
  <view wx:if="{{ list.length }}">
    <scroll-view
      bindscroll="listScroll"
      scroll-with-animation
      scroll-top="{{ scrollTo }}"
      scroll-y
      class="wrap-scroll">
      <view class="list" wx:for="{{ list }}" wx:key="item.alphabet" id="{{ item.alphabet }}">
        <view class="list-title">
          <text>{{ item.alphabet }}</text>
        </view>
        <view
          class="list-item"
          wx:for="{{ item.data }}"
          wx:for-item="it"
          wx:key="it"
          bindtouchstart="tapItem"
          data-itemInfo="{{ it }}">
          <text>{{ it }}</text>
        </view>
      </view>
    </scroll-view>
    <view class="alphaNavWrap" wx:if="{{ alphaNavFlag }}">
      <view class="alphaNav" capture-bind:touchmove="touchmoveHandle" bindtouchend="touchendHandle">
        <view
          wx:for="{{ list }}"
          wx:key="item.alphabet"
          class="'alphabet' {{ currentAlpha === item.alphabet ? 'active': '' }}"
          bindtouchstart="touchstartHandle"
          data-alpha="{{item.alphabet}}">
          {{ item.alphabet }}
          <view wx:if="{{ moveFlag && currentAlpha === item.alphabet }}" class="sign">{{ item.alphabet }}</view>
        </view>
      </view>
    </view>
  </view>
  <view wx:if="{{ !loading && !list.length }}" class="nodata">
    <text>暂无数据</text>
  </view>
  <view wx:if="{{ loading }}" class="loading">
    <text>loading...</text>
  </view>
</view>
wxss代码如下:
.c-alphabet-nav {
    position: relative;
}
/*滚动区样式*/
.c-alphabet-nav>view {
    height: 100%;
}
.c-alphabet-nav .wrap-scroll {
    height: 100%;
    border-top: 1px solid #ddd;
}
.c-alphabet-nav .list .list-title{
    height: 40px;
    line-height: 40px;
    padding: 0 20rpx;
}
.c-alphabet-nav .list .list-item {
    position: relative;
    height: 40px;
    line-height: 40px;
    background-color: #fff;
}
.c-alphabet-nav .list .list-item text {
    display: block;
    font-size: 14px;
    padding: 0 20rpx;
}
.c-alphabet-nav .list .list-item:after {
    content: '';
    display: block;
    width: 100%;
    height: 1px;
    bottom: 0;
    left: 0;
    position: absolute;
    background-color: #d9d9d9;
}
.c-alphabet-nav .loading, .c-alphabet-nav .nodata {
    text-align: center;
    padding-top: 100rpx;
    font-size: 28rpx;
}
/*字母导航区样式*/
.alphaNavWrap {
    width: 20px;
    height: 100%;
    position: absolute;
    top: 0;
    right: 14rpx;
    display: flex;
    align-items: center;
}
.c-alphabet-nav .alphaNav {
    font-size: 26rpx;
}
.c-alphabet-nav .alphaNav .alphabet {
    width: 40rpx;
    height: 40rpx;
    line-height: 40rpx;
    text-align: center;
    padding-right: 6rpx;
    box-sizing: border-box;
}
.c-alphabet-nav .alphaNav .alphabet.active {
    background-color: rgba(6, 6, 6, 0.3);
    border-radius: 50%;
}
.c-alphabet-nav .alphaNav .sign {
    position: absolute;
    font-size: 40rpx;
    left: -200rpx;
    top: -30rpx;
    width: 100rpx;
    height: 100rpx;
    text-align: center;
    line-height: 100rpx;
    border-radius: 50%;
    background-color: #ddd;
    display: none;
}
.c-alphabet-nav .alphaNav .sign:after {
    content: '';
    display: block;
    width: 0;
    height: 0;
    border-left: 84rpx solid #ddd;
    border-top: 50rpx solid transparent;
    border-bottom: 50rpx solid transparent;
    position: absolute;
    right: -46rpx;
    top: 1rpx;
    z-index: -1;
}
.c-alphabet-nav .alphaNav .alphabet.active .sign {
    display: block;
}
js文件如下
Component({
  properties: {
    loading: {
      type: Boolean,
      value: false
    },
    height: {
      type: String,
      value: '80vh'
    },
    list: {
      type: Array,
      observer (list) {
        if (list.length) {
          try {
            const res = wx.getSystemInfoSync();
            this.setData({windowHeight: res.windowHeight})
          } catch (e) {
            console.error(e)
          }
          const query = this.createSelectorQuery();
          query.select('.wrap-scroll').boundingClientRect((rect) => {
            this.data.scrollviewHeight = rect.height
          });
          if (this.data.alphaNavFlag) {
            query.select('.alphaNav').boundingClientRect((rect) => {
              this.data.alphaNavPageY = Math.floor(rect.top);
              this.data.alphaNavItemHeight = rect.height / this.data.list.length
            });
          }
          query.select('.list-item').boundingClientRect((rect) => {
            let tempArray = [];
            this.data.list.forEach((item, i) => {
              let tempObj = {};
              tempObj.alphabet = item.alphabet;
              tempObj.height = (item.data.length + 1) * rect.height;
              if (i) tempObj.height += tempArray[i - 1].height; // 高度累加
              tempArray.push(tempObj);
            });
            this.setData({'itemHeight': tempArray});

            // 当滚动区滑块滑动到底部时,滚动条的位置
            let pageHeight = tempArray[tempArray.length - 1].height; // 滚动区内容总高度
            let maxScrollTop = pageHeight - this.data.scrollviewHeight;
            if (maxScrollTop > 0) {
              this.setData({'maxScrollTop': maxScrollTop});
              this.setData({ 'alphaNavFlag': true })
            } else {
              this.setData({ 'alphaNavFlag': false })
            }
          });
          setTimeout(() => {
            query.exec();
          })
        }
      }
    }
  },
  data: {
    alphaNavFlag: false,
    currentAlpha: 'A', // 列表当前滑动到的元素
    itemHeight: [], // 列表字母项底部距滚动区文档顶部的高度
    scrollTo: 0, // 当前元素项顶部距离滚动区文档顶部的距离
    windowHeight: 603, // 设备高度
    scrollviewHeight: 0, // 滚动区域高度
    alphaNavHeight: 0, // 右侧字母列表高度
    down: false,
    moveFlag: false,
    maxScrollTop: 0 // 页面滑到底时滚动条距顶部的高度
  },
  methods: {
    inputHandle (e) {
      this.setData({'searchText': e.detail.value})
    },
    touchmoveHandle (e) {
      this.setData({ 'moveFlag': true });
      const distance = e.touches[0].pageY - this.data.alphaNavPageY;
      const currentAlpha = this.data.list[Math.floor(distance / this.data.alphaNavItemHeight)].alphabet;
      this.setData({ 'currentAlpha': currentAlpha })
      this.goScroll();
    },
    touchendHandle () {
      this.setData({ 'moveFlag': false });
    },
    goScroll () {
      let parameter = {
        'scrollTo': 0,
        'currentAlpha': this.data.currentAlpha
      };
      let { currentAlpha, list, itemHeight, maxScrollTop } = this.data;
      if (currentAlpha !== list[0].alphabet) {
        itemHeight.forEach((item, i) => {
          if (item.alphabet === currentAlpha) {
            parameter.scrollTo = itemHeight[i - 1].height;
            this.setData({'down': itemHeight[i - 1].height > maxScrollTop});
          }
        })
      }
      this.setData(parameter);
    },
    tapItem (e) {
      this.triggerEvent('tapItem', e.currentTarget.dataset)
    },
    touchstartHandle (e) {
      this.setData({'currentAlpha': e.target.dataset.alpha});
      this.goScroll();
    },
    listScroll (e) {
      if (this.data.timeId) {
        clearTimeout(this.data.timeId);
      }
      this.data.timeId = setTimeout(() => {
        let currentScrollTop = e.detail.scrollTop; // 滚动条距顶部的实时距离
        const { itemHeight, maxScrollTop, down } = this.data;
        if (currentScrollTop >= maxScrollTop && down) {
          this.setData({'down': false});
          return
        }
        for (let i = 0; i < itemHeight.length; i++) {
          if (currentScrollTop < itemHeight[i].height) {
            this.setData({
              'currentAlpha': itemHeight[i].alphabet
            });
            break
          }
        }
      }, 100);
    },
  }
});

转载于:https://www.cnblogs.com/qddyh/p/11160950.html



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