iScroll太臃腫,自己寫了個,發出來供有需要的人,壓縮後3.8KB,支持滾動回彈。

本插件用純js實現,無需任何依賴,關鍵地方加了註釋。

本插件自動開啓硬件加速,只支持Y軸滾動且只支持touch事件,在桌面端請用chrome的手機調試器調試。

同時,有可能引用此插件之後你會發現click事件都觸發不了了,這時你要加上click事件觸發代碼,如:

var event = document.createEvent('HTMLEvents');
event.initEvent("click", true, true);
e.target.dispatchEvent(event);

要改動讓其支持桌面端也很方便,只需要多綁定兩個事件同時改下取座標的地方即可,轉載請標明出處。

代碼如下。

(function () {
    var allCache = {},
        cacheIndex = 1,
        startTime,
        container,
        startPointY,
        cache,
        utils = {
            addEvent: function (dom, type, handler) {
                utils.each(type.split(","), function (type) {
                    dom.addEventListener(type, handler);
                });
            },
            each: function (obj, callback) {
                for (var key in obj) {
                    callback(obj[key]);
                }
            },
            setScrollY: function (dom, y) {
                dom.style.webkitTransform = dom.style.transform = "translate3d(0, " + y + "px, 0)";
            },
            springbackYAnim: function (dom, y, endY, meCache) {//回彈動畫的處理程序
                var speed = (y - endY) / 8;

                _anim();

                function _anim() {
                    speed -= speed / 9;

                    y -= speed;

                    if (Math.abs(Math.abs(y) - Math.abs(endY)) < 0.8) {
                        y = endY;
                        meCache.animFrameID = undefined;
                    }

                    utils.setScrollY(dom, y);
                    meCache.scrollY = y;

                    if (y != endY)
                        meCache.animFrameID = utils.animFrame(_anim);
                    else if (endY > 0 || endY < meCache.maxScroll)
                        utils.springbackYAnim(dom, endY, endY > 0 ? 0 : meCache.maxScroll, meCache);
                }
            },
            scrollYAnim: function (dom, y, speed, meCache) {//正常滾動動畫的處理程序
                _anim();

                function _anim() {
                    speed -= speed / 30;

                    y -= speed;

                    var isSpringback = false;

                    if (y > 0 || y < meCache.maxScroll) {//判斷是否到頂部或底部
                        if (Math.abs(speed) > 1) {//如果速度還大於1則繼續滾動並調用回彈程序
                            var endY = y - speed * 5;

                            if (speed < 0 && endY > 40)
                                endY = 40;
                            else if (speed > 0 && endY < meCache.maxScroll - 40)
                                endY = meCache.maxScroll - 40;

                            utils.springbackYAnim(dom, y, endY, meCache);
                        }

                        isSpringback = true;
                    }

                    utils.setScrollY(dom, y);
                    meCache.scrollY = y;

                    if (!isSpringback && Math.abs(speed) > 0.1)//如果沒有回彈且速度還大於0.1則繼續滾動。
                        meCache.animFrameID = utils.animFrame(_anim);
                }
            },
            init: function (dom, meCacheID) {//初始化cache的程序
                var meCache = allCache[meCacheID];

                if (!meCache) {//如果當前實例的cache不存在,則綁定事件並初始化cache
                    utils.addEvent(dom.parentElement, "touchstart", utils.touchHandler);
                    meCache = allCache[meCacheID] = {};
                }

                meCache.containerHeight = dom.parentNode.offsetHeight;
                meCache.height = dom.offsetHeight;
                meCache.maxScroll = meCache.containerHeight - meCache.height;
                meCache.scrollY = meCache.scrollY || 0;

                meCache.maxScroll = meCache.maxScroll > 0 ? 0 : meCache.maxScroll;

                if (meCache.scrollY < meCache.maxScroll)//當用戶手動調用實例的refresh方法時重新判斷滾動值是否還在範圍內。
                    utils.setScrollY(dom, meCache.maxScroll);
            },
            touchHandler: function (e) {//用戶手指操作處理程序。
                switch (e.type) {
                    case "touchstart":
                        startPointY = e.changedTouches[0].pageY;
                        startTime = (new Date()).getTime();
                        container = this.children[0];
                        cache = allCache[container.getAttribute("data-nt-scroll") || container.id];

                        if (cache.animFrameID) {
                            utils.cancelAnimFrame(cache.animFrameID);
                            cache.animFrameID = undefined;
                        }
                        break;
                    case "touchmove":
                        if (startPointY !== undefined) {
                            if (e.changedTouches[0].pageY <= 2)//如果移動到距離頂端屏幕2像素的地方自動釋放本次滾動(有些移動端移出頂部就不觸發事件了)
                                utils.touchEndHandler(e);
                            else
                                utils.scrollYHandle(e);
                        }
                        break;
                    default:
                        if (startPointY !== undefined)
                            utils.touchEndHandler(e);
                        break;
                }

                e.preventDefault();
            },
            touchEndHandler: function (e) {
                var y = endY = utils.scrollYHandle(e);

                if (y > 0 || cache.height <= cache.containerHeight) {
                    endY = 0;
                } else if (y < cache.maxScroll) {
                    endY = cache.maxScroll;
                } else {
                    var currentTime = (new Date()).getTime(),
                        speed = (cache.scrollY - y) / (currentTime - startTime) * 10;

                    if (Math.abs(speed) > 5)//如果速度大於5則開始動畫滾動
                        utils.scrollYAnim(container, y, speed, cache);
                }

                cache.scrollY = y;

                if (endY != y)//如果當前的滾動值和最終想要到達的滾動值不一樣,則開始回彈動畫
                    utils.springbackYAnim(container, y, endY, cache);

                startPointY = startTime = container = cache = undefined;//清除一些臨時變量的控空間
            },
            scrollYHandle: function (e) {//正常滾動的處理程序
                var offsetY = e.changedTouches[0].pageY - startPointY;

                if (cache.height <= cache.containerHeight) {//如果容器高度大於內容高度
                    offsetY = offsetY / 3;//則滾動值等於偏離值的三分之一,就是頂部往下滑會有點粘滯感的樣子
                } else if (offsetY + cache.scrollY > 0) {//偏移值加已經滾動了的值大於0則代表滾動已經超出頂部了
                    if (offsetY > 0) {
                        if (cache.scrollY >= 0)
                            offsetY = cache.scrollY + offsetY / 3;
                        else
                            offsetY = (cache.scrollY + offsetY) / 3;
                    } else {
                        offsetY = cache.scrollY + offsetY;
                    }
                } else if (offsetY + cache.scrollY < cache.maxScroll) {//和上面的判斷一樣,只不過這裏是判斷是否到底部
                    if (offsetY < 0) {
                        if (cache.scrollY <= cache.maxScroll)
                            offsetY = cache.scrollY + offsetY / 3;
                        else
                            offsetY = cache.maxScroll + (cache.scrollY + offsetY - cache.maxScroll) / 3;
                    } else {
                        offsetY = cache.scrollY + offsetY;
                    }
                } else {
                    offsetY += cache.scrollY;
                }

                utils.setScrollY(container, offsetY);//設置好滾動高度

                return offsetY;//返回當前滾動高度
            },
            attr: function (dom, name, val) {
                if (val === undefined)
                    return dom.getAttribute(name);
                
                dom.setAttribute(name, val);
            },
            animFrame: function (callback) {
                var animFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
                return animFrame(callback);
            },
            cancelAnimFrame: function (animFrameID) {
                var cancelAnimFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame;
                cancelAnimFrame(animFrameID);
            },
            initCacheID: function (dom) {//初始化插件的cacheID程序
                var meCacheID = utils.attr(dom, "data-nt-scroll");
                if (!meCacheID) {
                    meCacheID = "ntScroll" + cacheIndex++;
                    utils.attr(dom, "data-nt-scroll", meCacheID);
                }

                return meCacheID;
            }
        };

    window.ntScroll = function (dom) {//插件類
        var me = this,
            meCacheID;

        if (typeof dom === "string")//如果傳進來的是字符串則認爲該字符串是dom的id,否則直接認爲就是dom對象。
            dom = document.getElementById(dom);

        meCacheID = utils.initCacheID(dom);//初始化實例的cacheID值。

        me.refresh = function () {//設置對外的刷新函數。
            utils.init(dom, meCacheID);
        };

        if (!allCache[meCacheID])//如果是第一次初始化該實例,則手動初始化一次。
            me.refresh();
    };

    utils.addEvent(window, "touchmove,touchend,touchcancel", utils.touchHandler);//自動綁定window的幾個事件。
})();

實例代碼

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,initial-scale=1.0,width=device-width" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
	<meta charset="utf-8" />
    <style type="text/css">
        body, html{
            width:100%;
            height:100%;
            padding:0;
            margin:0;
        }
    </style>
</head>
<body>
    <div style="position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden;">
        <div style="position:absolute;left:0;top:0;width:100%;" id="test">
            <script>
                for (var i = 0; i < 150; i++) {
                    document.write('<h1>' + (i + 1) + '</h1>'); 
                }
            </script>
        </div>
    </div>
    <script src="js/ntScroll.js"></script>
    <script>
        new ntScroll("test");
    </script>
</body>
</html>



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章