react-visibility-tracking-hooks
Version:
React Hooks for Tracking Element Position in Window Viewport
241 lines (212 loc) • 8.22 kB
JavaScript
;
var react = require('react');
function getVisibilityRect(rect) {
var visibilityRect = {
top: 0,
left: 0,
bottom: 0,
right: 0
};
if (rect instanceof DOMRect) {
visibilityRect = {
top: Math.floor(rect.y),
left: Math.floor(rect.x),
bottom: Math.floor(rect.y + rect.height),
right: Math.floor(rect.x + rect.width)
};
} else {
visibilityRect = {
top: Math.floor(rect.top),
left: Math.floor(rect.left),
bottom: Math.floor(rect.bottom),
right: Math.floor(rect.right)
};
}
return visibilityRect;
}
function getContainmentRect() {
var containmentRect = {
top: 0,
left: 0,
bottom: window.innerHeight || document.documentElement.clientHeight,
right: window.innerWidth || document.documentElement.clientWidth
};
return containmentRect;
}
function checkIsVisible(nodeRect, containmentRect, minElementOffset, partiallyVisible) {
var nodeWidth = nodeRect.right - nodeRect.left;
var nodeHeight = nodeRect.bottom - nodeRect.top;
var hasSize = nodeWidth > 0 && nodeHeight > 0;
var partialNodeRect = {
top: nodeRect.top,
left: nodeRect.left,
bottom: nodeRect.bottom,
right: nodeRect.right
};
if (minElementOffset) {
partialNodeRect.top += minElementOffset.top || 0;
partialNodeRect.left += minElementOffset.left || 0;
partialNodeRect.bottom -= minElementOffset.bottom || 0;
partialNodeRect.right -= minElementOffset.right || 0;
}
var visibilityObject = {
top: partialNodeRect.top >= containmentRect.top,
left: partialNodeRect.left >= containmentRect.left,
bottom: partialNodeRect.bottom <= containmentRect.bottom,
right: partialNodeRect.right <= containmentRect.right
};
if (partiallyVisible) {
if (partiallyVisible === true) {
return Object.values(visibilityObject).some(function (isVisible) {
return isVisible;
});
} else {
return visibilityObject[partiallyVisible];
}
} else {
var isVisible = hasSize && visibilityObject.top && visibilityObject.left && visibilityObject.bottom && visibilityObject.right;
return isVisible;
}
}
function computePercentVisible(nodeRect, containmentRect) {
// No Intersection Case
if (nodeRect.left > containmentRect.right || nodeRect.right < containmentRect.left || nodeRect.top > containmentRect.bottom || nodeRect.bottom < containmentRect.top) {
return {
horizontalPercent: 0,
verticalPercent: 0,
overallPercent: 0
};
}
var nodeWidth = nodeRect.right - nodeRect.left;
var nodeHeight = nodeRect.bottom - nodeRect.top;
var horizontalIntersect = Math.min(containmentRect.right - containmentRect.left, containmentRect.right - nodeRect.left, nodeRect.right - containmentRect.left, nodeRect.right - nodeRect.left);
var verticalIntersect = Math.min(containmentRect.bottom - containmentRect.top, containmentRect.bottom - nodeRect.top, nodeRect.bottom - containmentRect.top, nodeRect.bottom - nodeRect.top);
var horizontalPercent = horizontalIntersect / nodeWidth;
var verticalPercent = verticalIntersect / nodeHeight;
var overallPercent = horizontalIntersect * verticalIntersect / (nodeWidth * nodeHeight);
return {
horizontalPercent: horizontalPercent,
verticalPercent: verticalPercent,
overallPercent: overallPercent
};
}
function useVisibilityTracking(_temp) {
var _ref = _temp === void 0 ? {} : _temp,
onVisibilityChange = _ref.onVisibilityChange,
_ref$partiallyVisible = _ref.partiallyVisible,
partiallyVisible = _ref$partiallyVisible === void 0 ? false : _ref$partiallyVisible,
_ref$scrollCheck = _ref.scrollCheck,
scrollCheck = _ref$scrollCheck === void 0 ? true : _ref$scrollCheck,
_ref$scrollThrottleLi = _ref.scrollThrottleLimit,
scrollThrottleLimit = _ref$scrollThrottleLi === void 0 ? 250 : _ref$scrollThrottleLi,
_ref$resizeCheck = _ref.resizeCheck,
resizeCheck = _ref$resizeCheck === void 0 ? false : _ref$resizeCheck,
_ref$resizeThrottleLi = _ref.resizeThrottleLimit,
resizeThrottleLimit = _ref$resizeThrottleLi === void 0 ? 250 : _ref$resizeThrottleLi,
_ref$minElementOffset = _ref.minElementOffset,
minElementOffset = _ref$minElementOffset === void 0 ? {
top: 0,
left: 0,
bottom: 0,
right: 0
} : _ref$minElementOffset;
var _useState = react.useState(false),
isVisible = _useState[0],
setIsVisible = _useState[1];
var _useState2 = react.useState({
horizontalPercent: 0,
verticalPercent: 0,
overallPercent: 0
}),
percentVisible = _useState2[0],
setPercentVisible = _useState2[1];
var nodeRef = react.useRef(null);
var eventListenersRef = react.useRef(null);
var checkVisibility = react.useCallback(function () {
var rect = nodeRef && nodeRef.current ? nodeRef.current.getBoundingClientRect() : null;
if (!rect) return;
var nodeRect = getVisibilityRect(rect);
var containmentRect = getContainmentRect();
var nextIsVisible = checkIsVisible(nodeRect, containmentRect, minElementOffset, partiallyVisible);
var percentVisible = computePercentVisible(nodeRect, containmentRect);
setIsVisible(nextIsVisible);
setPercentVisible(percentVisible);
if (onVisibilityChange) onVisibilityChange(nextIsVisible, percentVisible);
}, [minElementOffset, onVisibilityChange, partiallyVisible]);
var addEventListener = react.useCallback(function (event, throttleLimit) {
if (!eventListenersRef.current) {
eventListenersRef.current = {};
}
var eventListeners = eventListenersRef.current;
var timeout;
var checkVisibilityCallback = function checkVisibilityCallback() {
timeout = null;
checkVisibility();
};
var eventListenerFn = function eventListenerFn() {
if (timeout !== null) {
timeout = setTimeout(checkVisibilityCallback, throttleLimit < 0 ? 0 : throttleLimit);
}
};
var eventListenerInfo = {
eventListenerFn: eventListenerFn,
getTimeout: function getTimeout() {
return timeout;
}
};
window.addEventListener(event, eventListenerInfo.eventListenerFn);
eventListeners[event] = eventListenerInfo;
return function () {
if (timeout !== null) {
clearTimeout(timeout);
}
};
}, [checkVisibility]); // use "callback ref" instead of normal useRef
// https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node
var elementCallbackRef = react.useCallback(function (node) {
if (node !== null) {
nodeRef.current = node;
if (scrollCheck) {
addEventListener('scroll', scrollThrottleLimit);
}
if (resizeCheck) {
addEventListener('resize', resizeThrottleLimit);
}
} else {
var eventListeners = eventListenersRef.current;
for (var event in eventListeners) {
var eventListenerInfo = eventListeners[event];
if (eventListenerInfo !== undefined) {
if (eventListenerInfo.getTimeout() !== null) {
clearTimeout(eventListenerInfo.getTimeout());
}
window.removeEventListener(event, eventListenerInfo.eventListenerFn);
}
}
}
}, [scrollCheck, resizeCheck, addEventListener, scrollThrottleLimit, resizeThrottleLimit]);
react.useEffect(function () {
return function () {
var eventListeners = eventListenersRef.current;
for (var event in eventListeners) {
var eventListenerInfo = eventListeners[event];
if (eventListenerInfo !== undefined) {
if (eventListenerInfo.getTimeout() !== null) {
clearTimeout(eventListenerInfo.getTimeout());
}
window.removeEventListener(event, eventListenerInfo.eventListenerFn);
}
}
};
}, []);
var rect = nodeRef && nodeRef.current && nodeRef.current.getBoundingClientRect();
return [elementCallbackRef, {
rect: rect,
isVisible: isVisible,
percentVisible: percentVisible
}];
}
exports.checkIsVisible = checkIsVisible;
exports.computePercentVisible = computePercentVisible;
exports.default = useVisibilityTracking;
//# sourceMappingURL=react-visibility-tracking-hooks.cjs.development.js.map