1、前言

今天要开发类通讯录的可滑动字母索引效果,搜遍了百度也没找到相关的代码,都是只能点击,不能滑动。
其实这个功能不难实现,各位大神应该是不屑提起。这篇文章只是作为记录,如果能给初学者带来一点帮助就更好了。

2、先上效果图:

test.gif

3、思路

小程序不能操作DOM,只能通过setData来改变dom状态。但是,小程序提供了获取元素位置的能力:createSelectorQuery,并且touchmove方法也提供了手指所在的坐标信息。所以可以通过判断手指是否进入了索引元素,来决定数据列表跳转的位置。通过setData来改变scrollIntoView,使scroll-view跳转到指定的id所在的位置。具体参考微信小程序开发文档scroll-view组件。

4、实现

首先需要一个scrollView用来显示列表:

<scroll-view scroll-y scroll-into-view='{{scrollIntoView}}'>
    <!-- 用来定位 -->
    <view id='A' class='index'>A</view>
    <view class='item'>这里是列表</view>
</scroll-view>

字母索引部分的布局如下(仅仅是示例):

<view class='index-box' catch:touchmove='touchmove'>
    <view class='index' data-id='A'>A</view>
    <view class='index' data-id='B'>B</view>
    <view class='index' data-id='C'>C</view>
    ......
</view>

可以声明一个数组来存储所有索引元素的位置信息:

wx.createSelectorQuery()
    .selectAll('.index-box .index')
    .boundingClientRect()
    .exec(function(res) {
        arrTouchBarPosition = res[0];  // 需要预先定义arrTouchBarPosition
    });

获取到的信息结构如下:

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

其中的topbottom是用来和touchmove事件中的坐标信息比对的。
touchmove事件如下:

let clientY = e.touches[0].clientY;
for (let item of arrTouchBarPosition) {
    if (clientY >= item.top && clientY <= item.bottom) {
        this.setData({
            scrollIntoView: item.dataset.id
        });
        break;
    }
}

到这里,想要的效果已经实现了。但是为了提高性能,我们可以加上节流和其他减少触发touchmove事件的条件。最终完整的touchmove事件函数如下:

touchmove(e) {
    // 节流,需要预先定义timestamp
    let now = new Date().getTime();
    if (now - timestamp < 50) {
        return;
    }

    timestamp = now;

    // 无意触发
    if (e.touches.length > 1) {
        return;
    }

    let clientY = e.touches[0].clientY;
    for (let item of arrTouchBarPosition) {
        // 如果已经在目标位置,则不执行
        if (clientY >= item.top && clientY <= item.bottom) {
            if (item.dataset.id == this.data.scrollIntoView) {
                break;
            }

            this.setData({
                scrollIntoView: item.dataset.id
            });
            break;
        }
    }
}

如果显示的列表比较长,最好关闭scroll-view的动画效果。如果不关闭,可能会在操作比较快时,手指已经离开屏幕很长时间,动画还在继续……
即使是关闭了动画,操作较快时也会有同样的问题,但加上节流后问题完美解决。
考虑到需求和性能问题,没有添加在手指滑动到某个字母时,屏幕中间显示该字母的效果。后期将添加这个效果,并封装成一个插件发布到github。

欢迎评论和互动。

标签: 微信小程序