• 小程序-实现带字母导航的滚动列表


    在前端岗位已经有足足三年了,一直没有写过微信小程序,我知道它很火,有近似原生app的体验却无需安装app,即开即用,非常方便。工作一直挺忙,都是些再熟悉不过的搬砖工作,这大概就叫做瞎忙吧。近来稍闲,就学习了一下微信小程序,做了两个小demo,一个是仿《汽车之家》app里面的带字母导航的列表滚动效果,另外一个是日历控件,这两个都比较常用,放在那里以备后面工作需要,工作中有碰到类似需求的小伙伴也可以拿去用,相互借鉴嘛,碰到bug的话,劳烦给我留个言,我会及时关注解决的。

    效果如下:

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

    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;
    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 {
    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 {
    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;
    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;
    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);
    },
    }
    });
    
    
  • 相关阅读:
    我从来没有想要去
    微信公众号开发系列-发展模式,创建自己的自定义菜单
    iOS6之后 NSAttributedString 福利
    iOS 面试题:OC标题的基本概念&lt;延续&gt;
    逻辑、认识论和本体论“三统一”
    C++中的头文件和源文件
    extern与头文件(*.h)的区别和联系
    Predicate Format String Syntax 与字面量
    编码、格式与网络通信
    充分条件和必要条件的联系和区别是什么
  • 原文地址:https://www.cnblogs.com/qddyh/p/11160950.html
Copyright © 2020-2023  润新知