本插件用純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>