UNPKG

overscroll

Version:
489 lines (438 loc) 15.4 kB
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); import Hammer from 'hammerjs'; import { set } from './utils/css'; import addEventListener from './utils/dom/addDomEventListener'; import { requestAnimFrame } from './utils/requestAnimationFrame'; function sign(number) { return number > 0 ? 1 : -1; } var boundOut = function boundOut(t) { return Math.sin(Math.PI * t); }; var esseOut = function esseOut(t) { return t < 0.5 ? 1.5 : Math.sin(Math.PI * t) * 2.5 * 29 / 30 + 1 / 30; }; var divisor = function divisor(d, S) { return esseOut((S - d) / S); }; var bound = function bound(d, S) { return boundOut((S - d) / S); }; var abs = Math.abs; var max = Math.max; export default (function (scope) { var scrollX = scope.scrollX, scrollY = scope.scrollY, handleDestroy = scope.handleDestroy, overscroll = scope.overscroll, hasScrollY = scope.hasScrollY, hasScrollX = scope.hasScrollX, setScroll = scope.setScroll, target = scope.target, switchScale = scope.switchScale, anchors = scope.anchors, getPosition = scope.getPosition, resetCache = scope.resetCache, getScroll = scope.getScroll, scrollable = scope.scrollable, onScroll = scope.onScroll, onAfterScroll = scope.onAfterScroll, onBeforeScroll = scope.onBeforeScroll, handleAfterScroll = scope.handleAfterScroll, handleBeforeScroll = scope.handleBeforeScroll, handleScroll = scope.handleScroll, handleInit = scope.handleInit, dragable = scope.dragable, touchable = scope.touchable; // 横向排版 getPosition 有小数,所以加上范围限制保证在寻找缓存中的位置信息时能够精确找到 function getYPoss() { var scrollTopRange = overscroll.scrollTopRange; var _getScroll = getScroll(), scrollTop = _getScroll.top; return anchors.map(getPosition).map(function (_ref) { var top = _ref.top; return scrollTopRange(top + scrollTop); }); } function getXPoss() { var scrollLeftRange = overscroll.scrollLeftRange; var _getScroll2 = getScroll(), scrollLeft = _getScroll2.left; return anchors.map(getPosition).map(function (_ref2) { var left = _ref2.left; return scrollLeftRange(left + scrollLeft); }); } var possX = void 0; var possY = void 0; var scrollCache = null; var getNearest = function getNearest(pos) { var positions = scrollX ? possX : possY; return positions.reduce(function (nearest, position) { return abs(position - pos) < abs(nearest - pos) ? position : nearest; }, null); }; var frame = function frame(v, d, s, rebound) { if (v > 0 && d < 0 || v < 0 && d > 0) { rebound = true; v += sign(d) * max(0.5, 10 * bound(abs(d), s)); d -= v; } else if (rebound) { v = sign(d) * max(0.5, 30 * bound(abs(d), s)); if (abs(d) < abs(v)) { v = d; } d = sign(d) * max(abs(d) - abs(v), 0); } else { v = sign(d) * max(0.5, 30 * divisor(abs(d), s)); if (abs(d) < abs(v)) { v = d; } d = sign(d) * max(abs(d) - abs(v), 0); } return { v: v, d: d, rebound: rebound }; }; var runAnimFrame = function runAnimFrame() { var vx = 0; var vy = 0; var dx = 0; var dy = 0; var odx = 0; var ody = 0; var ovx = 0; var ovy = 0; var reboundX = false; var reboundY = false; handleDestroy(requestAnimFrame(function () { var scrollLeft = overscroll.scrollLeft, scrollTop = overscroll.scrollTop, clientHeight = overscroll.clientHeight, clientWidth = overscroll.clientWidth; if (hasScrollY() && dy !== 0) { var computed = frame(vy, dy, clientHeight, reboundY); vy = computed.v; dy = computed.d; reboundY = computed.rebound; } else if (hasScrollX() && dx !== 0) { var _computed = frame(vx, dx, clientWidth, reboundX); vx = _computed.v; dx = _computed.d; reboundX = _computed.rebound; } if (!overscroll.scrolling && ovx === 0 && ovy === 0 && (dx !== 0 || dy !== 0)) { onBeforeScroll(); } if (vx !== 0 || vy !== 0) { setScroll(dx === 0 ? getNearest(scrollLeft + vx) : scrollLeft + vx, dy === 0 ? getNearest(scrollTop + vy) : scrollTop + vy); if (dx !== 0 || dy !== 0) { onScroll(); } } if (dy === 0) { reboundY = false; vy = 0; ody = 0; } if (dx === 0) { reboundX = false; vx = 0; odx = 0; } if (overscroll.scrolling && vx === 0 && vy === 0 && dx === 0 && dy === 0 && (ovx !== 0 || ovy !== 0)) { scrollCache = null; setScroll(); onAfterScroll(); } ovx = vx; ovy = vy; }).cancel); return { scrollMove: function scrollMove(velocity, distance) { if (scrollX) { reboundX = false; vx = velocity; dx = distance; odx = distance; } else { reboundY = false; vy = velocity; dy = distance; ody = distance; } }, scrollStop: function scrollStop() { if (vx !== 0 || vy !== 0 || dx !== 0 || dy !== 0) { scrollCache = { vx: vx, vy: vy, dx: dx, dy: dy, odx: odx, ody: ody, reboundX: reboundX, reboundY: reboundY }; } vx = 0; vy = 0; dx = 0; ovx = 0; ovy = 0; dy = 0; odx = 0; ody = 0; reboundX = false; reboundY = false; setScroll(); }, scrollRestore: function scrollRestore() { if (scrollCache) { vx = scrollCache.vx; vy = scrollCache.vy; dx = scrollCache.dx; dy = scrollCache.dy; odx = scrollCache.odx; ody = scrollCache.ody; reboundX = scrollCache.reboundX; reboundY = scrollCache.reboundY; } }, scrollClear: function scrollClear() { scrollCache = null; } }; }; function runHammer(_ref3) { var scrollMove = _ref3.scrollMove, scrollStop = _ref3.scrollStop, scrollRestore = _ref3.scrollRestore, scrollClear = _ref3.scrollClear; var lastDeltaX = void 0; var lastDeltaY = void 0; var handleTarget = void 0; var directionX = [2, 4]; var directionY = [8, 16]; var _switchScale = _slicedToArray(switchScale, 2), upScale = _switchScale[0], downScale = _switchScale[1]; var mc = new Hammer.Manager(target); var sectionMoving = function sectionMoving(v, nearestpos, curpos, d, delta) { // 初速度足够触发上下滑动 if (v > 0.5) { // 下滑 scrollMove(-v * 20, nearestpos - (curpos + d)); } else if (v < -0.5) { // 上滑 scrollMove(-v * 20, nearestpos - curpos); } // 靠近上方 else if (nearestpos - curpos < downScale * d) { scrollMove(-v * 20, nearestpos - curpos); } // 靠近下方 else if (d + curpos - nearestpos < upScale * d) { scrollMove(-v * 20, nearestpos - (curpos + d)); } // 在中间位置 else if (delta > 0) { scrollMove(-v * 20, nearestpos - (curpos + d)); } else if (delta < 0) { scrollMove(-v * 20, nearestpos - curpos); } else if (nearestpos - curpos > d / 2) { scrollMove(0, nearestpos - (curpos + d)); } else { scrollMove(0, nearestpos - curpos); } }; if (dragable) { handleDestroy(addEventListener(target, 'mousedown', scrollStop).remove); handleDestroy(addEventListener(target, 'mouseup', scrollRestore).remove); } if (touchable) { handleDestroy(addEventListener(target, 'touchstart', scrollStop).remove); handleDestroy(addEventListener(target, 'touchend', scrollRestore).remove); handleDestroy(addEventListener(target, 'touchcancel', scrollRestore).remove); } mc.add(new Hammer.Pan({ direction: Hammer.DIRECTION_ALL, threshold: 0 })); mc.on('panstart panmove panend', function (event) { event.preventDefault(); if (!dragable && event.pointerType === 'mouse') { return; } if (!touchable && event.pointerType === 'touch') { return; } var type = event.type, velocityY = event.velocityY, velocityX = event.velocityX, deltaX = event.deltaX, deltaY = event.deltaY, offsetDirection = event.offsetDirection; var rightDirection = (scrollY ? directionY : directionX).includes(offsetDirection); var targetScrollable = scrollable(handleTarget || event.target); var targetUnscrollableX = !targetScrollable.left && velocityX >= 0 || !targetScrollable.right && velocityX <= 0; var targetUnscrollableY = !targetScrollable.top && velocityY >= 0 || !targetScrollable.bottom && velocityY <= 0; var scrollTop = overscroll.scrollTop; var scrollLeft = overscroll.scrollLeft; var x = scrollLeft; var y = scrollTop; if (type === 'panstart') { resetCache(scope); lastDeltaX = 0; lastDeltaY = 0; handleTarget = event.target; if (!targetUnscrollableY || !targetUnscrollableX) { scrollClear(); } } else if (type === 'panend' && scrollY) { handleTarget = null; possY = getYPoss(); if (!targetUnscrollableY) { scrollClear(); } if (!possY.includes(scrollTop)) { var clientHeight = overscroll.clientHeight; var _deltaY = event.deltaY, _velocityY = event.velocityY; var nearestpos = possY.reduce(function (pos, top) { return top > scrollTop && top < clientHeight + scrollTop ? top : pos; }, 0); if (nearestpos !== 0) { sectionMoving(_velocityY, nearestpos, scrollTop, clientHeight, _deltaY); } } } else if (type === 'panend' && scrollX) { handleTarget = null; possX = getXPoss(); if (!targetUnscrollableX) { scrollClear(); } if (!possX.includes(scrollLeft)) { var clientWidth = overscroll.clientWidth; var _deltaX = event.deltaX, _velocityX = event.velocityX; var _nearestpos = possX.reduce(function (pos, left) { return left > scrollLeft && left < clientWidth + scrollLeft ? left : pos; }, 0); if (_nearestpos !== 0) { sectionMoving(_velocityX, _nearestpos, scrollLeft, clientWidth, _deltaX); } } } else if (rightDirection) { if (!targetUnscrollableX) { x = scrollLeft - (deltaX - lastDeltaX); } if (!targetUnscrollableY) { y = scrollTop - (deltaY - lastDeltaY); } if (x !== scrollLeft || y !== scrollTop) { if (!overscroll.scrolling) { onBeforeScroll(); } setScroll(x, y); onScroll(); } } lastDeltaX = deltaX; lastDeltaY = deltaY; }); handleDestroy(function () { return mc.destroy(); }); } function _position(runtime) { var scrollLeft = overscroll.scrollLeft, scrollTop = overscroll.scrollTop; var curpos = scrollX ? scrollLeft : scrollTop; var poss = scrollX ? runtime ? getXPoss() : possX : runtime ? getYPoss() : possY; return poss.filter(function (pos) { return pos <= curpos; }).length; } function setPositionCache() { overscroll.positions = scrollX ? possX : possY; } handleInit(setPositionCache); handleScroll(setPositionCache); handleBeforeScroll(setPositionCache); handleAfterScroll(setPositionCache); function setSectionCache() { overscroll.section = _position(); } // handleScroll( setSectionCache ); // handleBeforeScroll( setSectionCache ); handleAfterScroll(setSectionCache); function initSections() { // 设置 anchor 样式撑满容器 anchors.forEach(function (element) { set(element, 'height', '100%'); set(element, 'width', '100%'); }); // 重置容器信息 resetCache(); if (scrollX) { possX = getXPoss(); } else { possY = getYPoss(); } overscroll.section = _position(true); } return { run: function run() { initSections(); var anim = runAnimFrame(); if (dragable || touchable) { runHammer(anim); } return { position: function position() { return _position(true); }, scrollTo: function scrollTo(targetPos, noAnimation) { resetCache(); var poss = scrollX ? getXPoss() : getYPoss(); var scrollLeft = overscroll.scrollLeft, scrollTop = overscroll.scrollTop, clientWidth = overscroll.clientWidth, clientHeight = overscroll.clientHeight; var curpos = scrollX ? scrollLeft : scrollTop; var d = scrollX ? clientWidth : clientHeight; var curIndex = poss.indexOf(curpos) + 1; var index = poss.filter(function (pos) { return pos <= targetPos; }).length; if (scrollX) { possX = poss; } else { possY = poss; } if (noAnimation === true) { setScroll(scrollX ? poss[index - 1] : 0, scrollY ? poss[index - 1] : 0); } else if (curIndex !== index) { anim.scrollStop(); anim.scrollClear(); anim.scrollMove(0, d * (index - 1) - curpos); } }, scrollToSection: function scrollToSection(index, noAnimation) { resetCache(); var scrollLeft = overscroll.scrollLeft, scrollTop = overscroll.scrollTop, clientWidth = overscroll.clientWidth, clientHeight = overscroll.clientHeight; var poss = scrollX ? getXPoss() : getYPoss(); var curpos = scrollX ? scrollLeft : scrollTop; var d = scrollX ? clientWidth : clientHeight; var curIndex = poss.indexOf(curpos) + 1; if (scrollX) { possX = poss; } else { possY = poss; } if (noAnimation === true) { setScroll(scrollX ? poss[index - 1] : 0, scrollY ? poss[index - 1] : 0); } else if (curIndex !== index) { anim.scrollStop(); anim.scrollClear(); anim.scrollMove(0, d * (index - 1) - curpos); } } }; } }; });