写在前面

最近在做移动端方面运用到了饿了么的vue前端组件库,因为不想单纯用组件而使用它,故想深入了解一下实现原理。后续将会继续研究一下其他的组件实现原理,有兴趣的可以关注下。

swiper

移动端效果之Picker

移动端效果之CellSwiper

移动端效果之IndexList

移动端效果之scrollList

代码在这里:戳我 or github

1. 说明

父容器overflow:hidden;,子页面transform:translateX(-100%);width:100%;

2. 核心解析

2.1 页面初始化

由于所有页面都在手机屏幕左侧一个屏幕宽度的位置,因此最开始的情况是页面中看不到任何一个子页面,所以第一步应该设置应该显示的子页面,默认情况下defaultIndex:0

function reInitPages() {
    // 得出页面是否能够被滑动
    // 1. 子页面只有一个
    // 2. 用户手动设置不能滑动 noDragWhenSingle = true
    noDrag = children.length === 1 && noDragWhenSingle;

    var aPages = [];
    var intDefaultIndex = Math.floor(defaultIndex);
    var defaultIndex = (intDefaultIndex >= 0 && intDefaultIndex < children.length) 
        ? intDefaultIndex : 0;
    
    // 得到当前被激活的子页面索引
    index = defaultIndex;

    children.forEach(function(child, index) {
        aPages.push(child);
        // 所有页面移除激活class
        child.classList.remove('is-active');

        if (index === defaultIndex) {
            // 给激活的子页面加上激活class
            child.classList.add('is-active');
        }
    });

    pages = aPages;
}

2.2 容器滑动开始(onTouchStart)

在低版本的android手机上,设置event.preventDefault()会起到一定的性能提升作用,使得滑动起来不是那么卡。

前置工作:

滑动开始:

使用一个全局对象记录信息,这些信息包括:

dragState = {
    startTime           // 开始时间
    startLeft           // 开始的X坐标
    startTop            // 开始的Y坐标(相对于整个页面viewport pageY)
    startTopAbsolute    // 绝对Y坐标(相对于文档顶部 clientY)
    pageWidth           // 一个页面宽度
    pageHeight          // 一个页面的高度
    prevPage            // 上一个页面
    dragPage            // 当前页面
    nextPage            // 下一个页面
};

2.3 容器滑动(onTouchMove)

套用全局dragState,记录新的信息

dragState = {
    currentLeft         // 开始的X坐标
    currentTop          // 开始的Y坐标(相对于整个页面viewport pageY)
    currentTopAbsolute  // 绝对Y坐标(相对于文档顶部 clientY)
};

那么我们就可以通过开始和滑动中的信息来计算出一些东西:

2.4 滑动结束(onTouchEnd)

前置工作:

在滑动中,我们是可以实时地来判断到底是不是用户的自然滚动userScrolling,如果是用户自然滚动,那么swiper的滑动信息就不算数,因此要做一些清除操作:

dragging = false;
dragState = {};

当然如果userScrolling:false,那么就是滑动子页面,执行doOnTouchEnd方法

后置工作:

清除一次滑动周期中保存的状态信息

dragging = false;
dragState = {};

总结

整体来说实现原理还是比较简单的,滑动开始记录初始位置,计算上一页与下一页的应该展示的页面;滑动中计算位移,计算上一页下一页的位移;滑动结束根据位移结果执行相应的动画。

有一个细节就是,在滑动中transition的效果置为空,是为了防止在滑动中上一页与下一页因为过渡存在而位移得不自然,在滑动结束后再给他们加上动画效果。