UNPKG

react-visibility-tracking-hooks

Version:
241 lines (212 loc) 8.22 kB
'use strict'; 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