前端入门之(我与iscroll的不期而遇)

博客已经停滞了一段时间了,目前一年是在跟js打交道,从最初的android到React-Native然后再到H5,也就短短的1年的时间,几乎明天都在学习,我tm都佩服我自己是怎么坚持下来的……^^,因为有android基础,所以RN上手相对容易一点,但是对于h5真的是从几乎0开始,一开始做东西的时候是灰常恐惧的,都不知道自己写出来的东西运行起来到底咋样,慢慢的遇到的坑多了,也就没什么感觉了(虽然现在依然很菜 ^^),哈哈,就当是心里安慰一下自己了,毕竟接下来的路还很长很长……

说了那么多p话,进入今天的主题哈,虽然iscroll已经出来了很久了,也已经在很多前端大牛身边耳濡目染了,所以对于我(一个刚准备入坑h5的人)来说,iscroll还是带着一层神秘面纱的,一直想着哪天能够好好地去读读她,刚好最近在写列表需求,发现自带的overflow:scroll在原生(ios/android)上运行的时候遇到很多坑,比如ios跟android的滚动效果不一样、内存消耗太大、适配太烂、扩展性太差等等.

先附上iscroll的文档跟github地址:
官方文档:http://iscrolljs.com/#scrollbars
github地址:https://github.com/cubiq/iscroll/

其实用法啥的官方文档已经写的很清楚了,我这边就不啰嗦了,小伙伴自己去看哈(虽然tm都是英文^_^,也还是要去看看的).

之前搞android的养成了一个毛病,就是喜欢边看源码然后结合文档去读别人的第三方库,你还别说,还是有点效果的!所以也建议小伙伴可以这样去读第三方库哈,没必须弄懂每一行代码,大概思路就可以了~

我们直接去github上面把源码clone下来:

这里写图片描述

可以看到,东西还是挺多的,包括iscroll的源码,然后各种demo,不得不说,作者平时还是比较闲的,哈哈哈~~~~

我们先看看最简单的用法:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">

<title>iScroll demo: simple</title>

<script type="text/javascript" src="../../build/iscroll.js"></script>
<script type="text/javascript" src="../demoUtils.js"></script>
<script type="text/javascript">

var myScroll;

function loaded () {
    myScroll = new IScroll('#wrapper', { mouseWheel: true,scrollbars: true,shrinkScrollbars: 'scale',snap: true});
}

document.addEventListener('touchmove', function (e) { e.preventDefault(); }, isPassive() ? {
    capture: false,
    passive: false
} : false);

</script>

<style type="text/css">
* {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

html {
    -ms-touch-action: none;
}

body,ul,li {
    padding: 0;
    margin: 0;
    border: 0;
}

body {
    font-size: 12px;
    font-family: ubuntu, helvetica, arial;
    overflow: hidden; /* this is important to prevent the whole page to bounce */
}

#header {
    position: absolute;
    z-index: 2;
    top: 0;
    left: 0;
    width: 100%;
    height: 45px;
    line-height: 45px;
    background: #CD235C;
    padding: 0;
    color: #eee;
    font-size: 20px;
    text-align: center;
    font-weight: bold;
}

#footer {
    position: absolute;
    z-index: 2;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 48px;
    background: #444;
    padding: 0;
    border-top: 1px solid #444;
}

#wrapper {
    position: absolute;
    z-index: 1;
    top: 45px;
    bottom: 48px;
    left: 0;
    width: 100%;
    background: #ccc;
    overflow: hidden;
}

#scroller {
    position: absolute;
    z-index: 1;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
    width: 100%;
    -webkit-transform: translateZ(0);
    -moz-transform: translateZ(0);
    -ms-transform: translateZ(0);
    -o-transform: translateZ(0);
    transform: translateZ(0);
    -webkit-touch-callout: none;
    -webkit-user-select: text;
    -moz-user-select: text;
    -ms-user-select: text;
    user-select: text;
    -webkit-text-size-adjust: none;
    -moz-text-size-adjust: none;
    -ms-text-size-adjust: none;
    -o-text-size-adjust: none;
    text-size-adjust: none;
}

#scroller ul {
    list-style: none;
    padding: 0;
    margin: 0;
    width: 100%;
    text-align: left;
}

#scroller li {
    padding: 0 10px;
    height: 40px;
    line-height: 40px;
    border-bottom: 1px solid #ccc;
    border-top: 1px solid #fff;
    background-color: #fafafa;
    font-size: 10px;
    -webkit-text-size-adjust: none;
    -moz-text-size-adjust: none;
    -ms-text-size-adjust: none;
    -o-text-size-adjust: none;
    text-size-adjust: none;
}

</style>
</head>
<body onload="loaded()">
<div id="header">iScroll</div>

<div id="wrapper">
    <div id="scroller">
        <ul>
            <li>Pretty row 1</li>
            <li>Pretty row 2</li>
            <li>Pretty row 3</li>
            <li>Pretty row 4</li>
            <li>Pretty row 5</li>
            <li>Pretty row 6</li>
            <li>Pretty row 7</li>
            <li>Pretty row 8</li>
            <li>Pretty row 9</li>
            <li>Pretty row 10</li>
            <li>Pretty row 11</li>
            <li>Pretty row 12</li>
            <li>Pretty row 13</li>
            <li>Pretty row 14</li>
            <li>Pretty row 15</li>
            <li>Pretty row 16</li>
            <li>Pretty row 17</li>
            <li>Pretty row 18</li>
            <li>Pretty row 19</li>
            <li>Pretty row 20</li>
            <li>Pretty row 21</li>
            <li>Pretty row 22</li>
            <li>Pretty row 23</li>
            <li>Pretty row 24</li>
            <li>Pretty row 25</li>
            <li>Pretty row 26</li>
            <li>Pretty row 27</li>
            <li>Pretty row 28</li>
            <li>Pretty row 29</li>
            <li>Pretty row 30</li>
            <li>Pretty row 31</li>
            <li>Pretty row 32</li>
            <li>Pretty row 33</li>
            <li>Pretty row 34</li>
            <li>Pretty row 35</li>
            <li>Pretty row 36</li>
            <li>Pretty row 37</li>
            <li>Pretty row 38</li>
            <li>Pretty row 39</li>
            <li>Pretty row 40</li>
            <li>Pretty row 41</li>
            <li>Pretty row 42</li>
            <li>Pretty row 43</li>
            <li>Pretty row 44</li>
            <li>Pretty row 45</li>
            <li>Pretty row 46</li>
            <li>Pretty row 47</li>
            <li>Pretty row 48</li>
            <li>Pretty row 49</li>
            <li>Pretty row 50</li>
        </ul>
    </div>
</div>

<div id="footer"></div>

</body>
</html>

用法很简单:

第一步:指定滑动的包裹元素,设置其高度、并且设置其overflow: hidden去除元素默认的滚动

#wrapper {
    position: absolute;
    z-index: 1;
    top: 45px;
    bottom: 48px;
    left: 0;
    width: 100%;
    background: #ccc;
    overflow: hidden;
}
<div id="wrapper">....</div>

第二步:设置内部滑动元素,也就是滑动的主体部分及其内容


<div id="wrapper">
    <div id="scroller">
        <ul>
            <li>Pretty row 1</li>
            <li>Pretty row 2</li>
            ....
                <li>Pretty row 50</li>
        </ul>
    </div>
</div>

第三步:等dom加载完毕的时候创建iscroll对象,并绑定滑动包裹元素

var myScroll;

function loaded () {
    myScroll = new IScroll('#wrapper');
}

好啦~ 简单的几步操作之后,我们就可以开心的玩起来了.

当我们创建一个iscroll对象的时候,我们去源码里看看它干了什么.

首先看iscroll的构造方法:

function IScroll (el, options) {
    this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
    this.scroller = this.wrapper.children[0];
    this.scrollerStyle = this.scroller.style;       // cache style for better performance

    this.options = {

        resizeScrollbars: true,

        mouseWheelSpeed: 20,

        snapThreshold: 0.334,

// INSERT POINT: OPTIONS
        disablePointer : !utils.hasPointer,
        disableTouch : utils.hasPointer || !utils.hasTouch,
        disableMouse : utils.hasPointer || utils.hasTouch,
        startX: 0,
        startY: 0,
        scrollY: true,
        directionLockThreshold: 5,
        momentum: true,

        bounce: true,
        bounceTime: 600,
        bounceEasing: '',

        preventDefault: true,
        preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/ },

        HWCompositing: true,
        useTransition: true,
        useTransform: true,
        bindToWrapper: typeof window.onmousedown === "undefined"
    };

    for ( var i in options ) {
        this.options[i] = options[i];
    }

    // Normalize options
    this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';

    this.options.useTransition = utils.hasTransition && this.options.useTransition;
    this.options.useTransform = utils.hasTransform && this.options.useTransform;

    this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
    this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;

    // If you want eventPassthrough I have to lock one of the axes
    this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;
    this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;

    // With eventPassthrough we also need lockDirection mechanism
    this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
    this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;

    this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;

    this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;

    if ( this.options.tap === true ) {
        this.options.tap = 'tap';
    }

    // https://github.com/cubiq/iscroll/issues/1029
    if (!this.options.useTransition && !this.options.useTransform) {
        if(!(/relative|absolute/i).test(this.scrollerStyle.position)) {
            this.scrollerStyle.position = "relative";
        }
    }

    if ( this.options.shrinkScrollbars == 'scale' ) {
        this.options.useTransition = false;
    }

    this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1;

// INSERT POINT: NORMALIZATION

    // Some defaults
    this.x = 0;
    this.y = 0;
    this.directionX = 0;
    this.directionY = 0;
    this._events = {};

// INSERT POINT: DEFAULTS

    this._init();
    this.refresh();

    this.scrollTo(this.options.startX, this.options.startY);
    this.enable();
}

我们慢慢来看哈,首先是获取wrapper跟scroller元素:

this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
    this.scroller = this.wrapper.children[0];

然后就是初始化一些参数:

this.options = {

        resizeScrollbars: true, //是否重置scrollbar的大小

        mouseWheelSpeed: 20, //鼠标滑动的速度

        snapThreshold: 0.334, //翻页的距离值

// INSERT POINT: OPTIONS
        disablePointer : !utils.hasPointer,
        disableTouch : utils.hasPointer || !utils.hasTouch,
        disableMouse : utils.hasPointer || utils.hasTouch,
        startX: 0, //横向滑动的的初始值
        startY: 0,//竖直滑动的初始值
        scrollY: true, //是否运行竖直滑动
        ...
    };

当然还有一些包括指示器的一些参数(自定义、样式等等),文档上貌似都没列出来,小伙伴只能去看源码了.

然后就是比较重要的几个初始化了:

    this._init();//初始化()
    this.refresh(); //获取一些基本参数(如果scroller的高度变化了需要调用此方法)

    this.scrollTo(this.options.startX, this.options.startY);//调整初始位置
    this.enable();//默认允许滑动
_init: function () {
        this._initEvents();//绑定事件(鼠标滑动、touch等等)

        if ( this.options.scrollbars || this.options.indicators ) {
            this._initIndicators();//如果需要显示指示器的话,就去初始化指示器
        }

        if ( this.options.mouseWheel ) {
            this._initWheel(); //监听鼠标滑动事件(默认打开)
        }

        if ( this.options.snap ) {
            this._initSnap();//翻页效果实现(默认关闭)
        }

        if ( this.options.keyBindings ) {
            this._initKeys();//绑定上下左右按键出发滚动(默认关闭)
        }

// INSERT POINT: _init

    },

那么既然iscroll禁用了默认的滑动,那么它是怎么滑动的呢?小伙伴猜也应该猜到了,就是监听元素(默认wrapper)的鼠标滑动、touch等等事件然后做出相应计算:

_initEvents: function (remove) {
        var eventType = remove ? utils.removeEvent : utils.addEvent,
            target = this.options.bindToWrapper ? this.wrapper : window;

        eventType(window, 'orientationchange', this);//当转换屏幕的时候refresh重置参数
        eventType(window, 'resize', this);//当window的size变换的时候时候refresh重置参数

        if ( this.options.click ) {//wrapper的点击事件
            eventType(this.wrapper, 'click', this, true);
        }

        if ( !this.options.disableMouse ) {//监听鼠标事件
            eventType(this.wrapper, 'mousedown', this);
            eventType(target, 'mousemove', this);
            eventType(target, 'mousecancel', this);
            eventType(target, 'mouseup', this);
        }

        if ( utils.hasPointer && !this.options.disablePointer ) {//处理指针事件,主要处理tap事件
            eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
            eventType(target, utils.prefixPointerEvent('pointermove'), this);
            eventType(target, utils.prefixPointerEvent('pointercancel'), this);
            eventType(target, utils.prefixPointerEvent('pointerup'), this);
        }

        if ( utils.hasTouch && !this.options.disableTouch ) {//监听touch事件
            eventType(this.wrapper, 'touchstart', this);
            eventType(target, 'touchmove', this);
            eventType(target, 'touchcancel', this);
            eventType(target, 'touchend', this);
        }

        eventType(this.scroller, 'transitionend', this);//监听transition过渡动画过程
        eventType(this.scroller, 'webkitTransitionEnd', this);
        eventType(this.scroller, 'oTransitionEnd', this);
        eventType(this.scroller, 'MSTransitionEnd', this);
    },

我们重点看一下,当我们的手指滑动的时候,scroller元素是怎么滑动的:

handleEvent: function (e) {
        switch ( e.type ) {
            ...
            case 'touchmove':
            case 'pointermove':
            case 'MSPointerMove':
            case 'mousemove':
                this._move(e);
                break;
            ...
        }
    }

处理滑动事件:

_move: function (e) {
        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
            return;
        }

        if ( this.options.preventDefault ) {    // increases performance on Android? TODO: check!
            e.preventDefault();
        }

        var point       = e.touches ? e.touches[0] : e,
            deltaX      = point.pageX - this.pointX,
            deltaY      = point.pageY - this.pointY,
            timestamp   = utils.getTime(),
            newX, newY,
            absDistX, absDistY;

        this.pointX     = point.pageX;
        this.pointY     = point.pageY;

        this.distX      += deltaX;
        this.distY      += deltaY;
        absDistX        = Math.abs(this.distX);
        absDistY        = Math.abs(this.distY);

        // We need to move at least 10 pixels for the scrolling to initiate
        if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
            return;
        }

        // If you are scrolling in one direction lock the other
        if ( !this.directionLocked && !this.options.freeScroll ) {
            if ( absDistX > absDistY + this.options.directionLockThreshold ) {
                this.directionLocked = 'h';     // lock horizontally
            } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
                this.directionLocked = 'v';     // lock vertically
            } else {
                this.directionLocked = 'n';     // no lock
            }
        }

        if ( this.directionLocked == 'h' ) {
            if ( this.options.eventPassthrough == 'vertical' ) {
                e.preventDefault();
            } else if ( this.options.eventPassthrough == 'horizontal' ) {
                this.initiated = false;
                return;
            }

            deltaY = 0;
        } else if ( this.directionLocked == 'v' ) {
            if ( this.options.eventPassthrough == 'horizontal' ) {
                e.preventDefault();
            } else if ( this.options.eventPassthrough == 'vertical' ) {
                this.initiated = false;
                return;
            }

            deltaX = 0;
        }

        deltaX = this.hasHorizontalScroll ? deltaX : 0;
        deltaY = this.hasVerticalScroll ? deltaY : 0;

        newX = this.x + deltaX;
        newY = this.y + deltaY;

        // Slow down if outside of the boundaries
        if ( newX > 0 || newX < this.maxScrollX ) {
            newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
        }
        if ( newY > 0 || newY < this.maxScrollY ) {
            newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
        }

        this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
        this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;

        if ( !this.moved ) {
            this._execEvent('scrollStart');
        }

        this.moved = true;

        this._translate(newX, newY);

/* REPLACE START: _move */

        if ( timestamp - this.startTime > 300 ) {
            this.startTime = timestamp;
            this.startX = this.x;
            this.startY = this.y;
        }

/* REPLACE END: _move */

    },

代码有点多,小伙伴别被吓住了哈,主要就是计算上一次与这一次滑动的距离,然后设置元素的偏移量,我们可以看到最后有一行代码:

this._translate(newX, newY);
_translate: function (x, y) {
        if ( this.options.useTransform ) {

/* REPLACE START: _translate */

            this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;

/* REPLACE END: _translate */

        } else {
            x = Math.round(x);
            y = Math.round(y);
            this.scrollerStyle.left = x + 'px';
            this.scrollerStyle.top = y + 'px';
        }

        this.x = x;
        this.y = y;


    if ( this.indicators ) {
        for ( var i = this.indicators.length; i--; ) {
            this.indicators[i].updatePosition();
        }
    }


// INSERT POINT: _translate

    },

可以看到,最后也就是设置x轴或者y轴的偏移量:

x = Math.round(x);
            y = Math.round(y);
            this.scrollerStyle.left = x + 'px';
            this.scrollerStyle.top = y + 'px';

原理其实很简单,但是iscroll可不单单如此简单哈~~ 她可不简单,慢慢研究吧!! 睡啦睡啦~~生命重要!!!小伙伴下期见哈,大牛勿喷!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值