@livelybone/scroll-get
Version:
Some useful tool of browser scroll, such as tool for calculating position relative to page/client, tool for getting the native scrollbar width...
424 lines (348 loc) • 12.3 kB
JavaScript
/**
* Bundle of @livelybone/scroll-get
* Generated: 2021-06-28
* Version: 6.2.1
* License: MIT
* Author: 2631541504@qq.com
*/
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}
function _iterableToArray(iter) {
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function getRect(el) {
return el.getBoundingClientRect();
}
function posRelativeToPage(el) {
var o = {
pageLeft: 0,
pageTop: 0
};
var $el = el;
while ($el) {
o.pageLeft += $el.offsetLeft;
o.pageTop += $el.offsetTop;
$el = $el.offsetParent;
}
return o;
}
function posRelativeToClient(el) {
var rect = getRect(el);
return {
clientLeft: rect.left,
clientTop: rect.top
};
}
/**
* @desc if el === window || el === undefined, return the global scrollbar width info
* if el is an Element, return the scrollbar width info of the Element
*
* x: width of horizontal scrollbar
* y: width of vertical scrollbar
* */
function getNativeScrollbarWidth(el) {
var $el = el || window;
var isWindow = $el === window;
try {
var info = isWindow ? window.nativeScrollbarWidth : null;
if (!(info && typeof info.y === 'number' && typeof info.x === 'number')) {
// If nativeScrollbarWidth is illegal, reset it
var doc = el && 'ownerDocument' in el ? el.ownerDocument : document;
var wrapper = isWindow ? doc.createElement('div') : $el;
if (isWindow) {
wrapper.setAttribute('style', 'position:fixed;top:0;left:0;opacity:0;pointer-events:none;width:200px;height:200px;overflow:scroll');
doc.body.appendChild(wrapper);
}
info = {
y: wrapper.offsetWidth - wrapper.clientWidth,
x: wrapper.offsetHeight - wrapper.clientHeight
};
if (isWindow) {
window.nativeScrollbarWidth = info;
doc.body.removeChild(wrapper);
}
}
return info;
} catch (e) {
// For server render
return {
y: 17,
x: 17
};
}
}
/**
* This affects the performance of the animation by modifying the rate
*
* rate >= 0 && rate <= 1
* */
function defaultRateFactor(rate) {
return rate + (1 - rate) * rate;
}
function animation(time, cb, rateFactor) {
var $rateFactor = rateFactor || defaultRateFactor;
var run = function run($cb) {
window.requestAnimationFrame(function () {
if ($cb()) run($cb);
});
};
return new Promise(function (res) {
var start = Date.now();
run(function () {
var rate = $rateFactor(Math.min(1, (Date.now() - start) / time));
cb(rate);
if (rate >= 1) {
res();
return false;
}
return true;
});
});
}
/**
* 获取元素的可能达到的最大的 scrollTop 和 scrollLeft 值
*
* Gets the maximum possible scrollTop ans scrollLeft value for the element
* */
function getMaxScrollOffset(el) {
var style = window.getComputedStyle(el);
var offset = {
top: 0,
left: 0
};
if (el.nodeName === 'HTML' || ['scroll', 'auto', 'overlay'].includes(style.overflowX)) {
offset.left = Math.max(0, el.scrollWidth - el.clientWidth);
}
if (el.nodeName === 'HTML' || ['scroll', 'auto', 'overlay'].includes(style.overflowY)) {
offset.top = Math.max(0, el.scrollHeight - el.clientHeight);
}
return offset;
}
function nonScrollOffset(offset) {
return !offset.top && !offset.left;
}
/**
* 向上遍历元素的祖先,获取第一个能滚动的祖先元素
*
* Traverse up the ancestor of the element to get the first scrollable ancestor element
* */
function getScrollParent($el) {
if (!($el !== null && $el !== void 0 && $el.style)) return undefined;
var style = window.getComputedStyle($el);
if (style.position === 'fixed') return undefined;
var doc = $el.ownerDocument;
if (style.position === 'absolute') {
if ($el.offsetParent) {
return getScrollParent($el.offsetParent);
}
return !nonScrollOffset(getMaxScrollOffset(doc.body)) ? doc.body : getScrollParent(doc.body);
}
var scrollParent = $el.parentElement;
if (scrollParent) {
return !nonScrollOffset(getMaxScrollOffset(scrollParent)) || scrollParent === doc.documentElement ? scrollParent : getScrollParent(scrollParent);
}
return undefined;
}
/**
* @param el The target element you want scroll to
* @param [options] ScrollToElementOptions
* */
function scrollToElement(el, options) {
var _ref = options || {},
_ref$offset = _ref.offset,
$offset = _ref$offset === void 0 ? 0 : _ref$offset,
_ref$time = _ref.time,
time = _ref$time === void 0 ? 300 : _ref$time,
resOptions = _objectWithoutProperties(_ref, ["offset", "time"]);
var offset = typeof $offset === 'number' ? {
left: $offset,
top: $offset
} : {
left: $offset.left || 0,
top: $offset.top || 0
};
var scrollParent = getScrollParent(el);
if (scrollParent && (!resOptions.topDisabled || !resOptions.leftDisabled)) {
var parentScroll = function parentScroll() {
return scrollToElement(scrollParent, _objectSpread2({
time: time
}, resOptions));
};
var maxScrollOffset = getMaxScrollOffset(scrollParent);
var originScrollOffset = {
scrollLeft: scrollParent.scrollLeft,
scrollTop: scrollParent.scrollTop
};
var rect = getRect(el);
var _scrollParentRect = getRect(scrollParent);
if (scrollParent.nodeName === 'HTML') {
_scrollParentRect.y = 0;
}
var delta = {
left: Math.min(rect.left - _scrollParentRect.left + offset.left, maxScrollOffset.left - scrollParent.scrollLeft),
top: Math.min(rect.top - _scrollParentRect.top + offset.top, maxScrollOffset.top - scrollParent.scrollTop)
};
if (delta.left && !resOptions.leftDisabled || delta.top && !resOptions.topDisabled) {
return animation(time, function (rate) {
if (!resOptions.topDisabled) {
scrollParent.scrollTop = originScrollOffset.scrollTop + delta.top * rate;
}
if (!resOptions.leftDisabled) {
scrollParent.scrollLeft = originScrollOffset.scrollLeft + delta.left * rate;
}
}, resOptions.rateFactor).then(resOptions.affectParent ? parentScroll : null);
}
if (resOptions.affectParent) {
return parentScroll();
}
}
return Promise.resolve();
}
function getViewElementsWhenScroll(scrollElement, targetElements, cb) {
if (targetElements.length > 0) {
var oldEl = [];
var scroll = function scroll(ev) {
var scrollRect = getRect(scrollElement);
var elementsRect = targetElements.map(getRect); // 重新计算元素当前的区域高度及可见区域高度
var rects = elementsRect.map(function (rect, i) {
var $rect = rect;
$rect.viewHeight = Math.max(0, Math.min($rect.top + $rect.height, scrollRect.top + scrollRect.height) - Math.max(scrollRect.top, $rect.top));
$rect.viewWidth = Math.max(0, Math.min($rect.left + $rect.width, scrollRect.left + scrollRect.width) - Math.max(scrollRect.left, $rect.left));
if ($rect.height && $rect.width) {
$rect.viewPercent = $rect.viewHeight * $rect.viewWidth / $rect.height * $rect.width;
} else if (!$rect.height && !$rect.width) {
$rect.viewPercent = 0;
} else if (!$rect.height) {
$rect.viewPercent = $rect.viewWidth / $rect.width;
} else {
$rect.viewPercent = $rect.viewHeight / $rect.height;
}
return {
rect: $rect,
element: targetElements[i]
};
}); // 通过比较各自当前的可见区域的大小获得当前的最近元素
var viewElements = rects.filter(function (el) {
return el.rect.viewPercent > 0;
}).sort(function (a, b) {
var l1 = b.rect.viewPercent - a.rect.viewPercent;
if (l1) return l1;
return b.rect.viewHeight * b.rect.viewWidth - a.rect.viewHeight * a.rect.viewWidth;
});
if (viewElements.length !== oldEl.length || viewElements.some(function (el, i) {
return el.element !== oldEl[i].element;
})) {
cb(oldEl = viewElements, scrollRect, ev);
}
};
scroll();
scrollElement.addEventListener('scroll', scroll);
return function () {
return scrollElement.removeEventListener('scroll', scroll);
};
}
return function () {};
}
/**
* Judge whether the element is in current page view
* */
function isElementInView(el) {
if (!el) return false;
var rect = el.getBoundingClientRect();
var x = rect.x + rect.width / 2;
var y = rect.y + rect.height / 2;
var points = [[x, rect.y], [x, rect.y + rect.height - 1], [rect.x, y], [rect.x + rect.width - 1, y], [x, y]];
return points.some(function (point) {
var _el$ownerDocument;
return el.contains((_el$ownerDocument = el.ownerDocument).elementFromPoint.apply(_el$ownerDocument, _toConsumableArray(point)));
});
}
export { animation, getMaxScrollOffset, getNativeScrollbarWidth, getRect, getScrollParent, getViewElementsWhenScroll, isElementInView, posRelativeToClient, posRelativeToPage, scrollToElement };