overscroll
Version:
502 lines (443 loc) • 15.9 kB
JavaScript
;
exports.__esModule = true;
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"); } }; }();
var _hammerjs = require('hammerjs');
var _hammerjs2 = _interopRequireDefault(_hammerjs);
var _css = require('./utils/css');
var _addDomEventListener = require('./utils/dom/addDomEventListener');
var _addDomEventListener2 = _interopRequireDefault(_addDomEventListener);
var _requestAnimationFrame = require('./utils/requestAnimationFrame');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
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;
exports['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((0, _requestAnimationFrame.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 _hammerjs2['default'].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((0, _addDomEventListener2['default'])(target, 'mousedown', scrollStop).remove);
handleDestroy((0, _addDomEventListener2['default'])(target, 'mouseup', scrollRestore).remove);
}
if (touchable) {
handleDestroy((0, _addDomEventListener2['default'])(target, 'touchstart', scrollStop).remove);
handleDestroy((0, _addDomEventListener2['default'])(target, 'touchend', scrollRestore).remove);
handleDestroy((0, _addDomEventListener2['default'])(target, 'touchcancel', scrollRestore).remove);
}
mc.add(new _hammerjs2['default'].Pan({ direction: _hammerjs2['default'].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) {
(0, _css.set)(element, 'height', '100%');
(0, _css.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);
}
}
};
}
};
};