UNPKG

braid-design-system

Version:
103 lines (102 loc) 4.49 kB
"use strict"; const easeModifier = (t) => t > 0.5 ? 4 * Math.pow(t - 1, 3) + 1 : 4 * Math.pow(t, 3); const getExpectedTime = (distance, duration, speed, minDuration) => { const calculatedDuration = distance / speed; const normalizedDuration = minDuration > calculatedDuration ? minDuration : calculatedDuration; return duration !== null ? duration : normalizedDuration; }; const getScrollPosition = (scrollContainer, direction) => scrollContainer[direction === "horizontal" ? "scrollLeft" : "scrollTop"]; const getScrollOffset = (scrollContainer, targetElement, direction) => { if (scrollContainer === window.document.documentElement) { const scrollPosition = getScrollPosition(scrollContainer, direction); const positionOnScreen = targetElement.getBoundingClientRect()[direction === "horizontal" ? "left" : "top"]; return positionOnScreen + scrollPosition; } let totalOffset = 0; let currentElement = targetElement; const offsetKey = direction === "horizontal" ? "offsetLeft" : "offsetTop"; while (scrollContainer.contains(currentElement) || currentElement === scrollContainer) { totalOffset += currentElement[offsetKey]; const { offsetParent } = currentElement; if (!(offsetParent instanceof HTMLElement)) { break; } currentElement = offsetParent; } return totalOffset; }; const scrollTo = (scrollContainer, direction, to) => { scrollContainer[direction === "horizontal" ? "scrollLeft" : "scrollTop"] = to; }; const getPossibleScroll = (scrollContainer, direction) => scrollContainer[direction === "horizontal" ? "scrollWidth" : "scrollHeight"] - (scrollContainer === window.document.documentElement ? 0 : scrollContainer[direction === "horizontal" ? "offsetWidth" : "offsetHeight"]); const limitNumberToRange = (number, min, max) => { if (number < min) { return min; } if (number > max) { return max; } return number; }; const scroll = (scrollContainer, direction, to, { duration = null, speed = 2, minDuration = 0 }, callback) => { const startTime = Date.now(); const initial = getScrollPosition(scrollContainer, direction); const possibleScroll = getPossibleScroll(scrollContainer, direction); const targetScrollPosition = limitNumberToRange(to, 0, possibleScroll); const total = Math.abs(targetScrollPosition - initial); const expectedTime = getExpectedTime(total, duration, speed, minDuration); const scrollForwards = targetScrollPosition > initial; const step = () => { requestAnimationFrame(() => { const timePassed = Date.now() - startTime; const progress = timePassed / expectedTime; const distance = easeModifier(progress) * total; const newPosition = Math.floor( scrollForwards ? initial + distance : initial - distance ); const isComplete = scrollForwards ? newPosition >= targetScrollPosition : newPosition <= targetScrollPosition; if (isComplete) { scrollTo(scrollContainer, direction, targetScrollPosition); if (callback) { callback(); } } else { scrollTo(scrollContainer, direction, newPosition); step(); } }); }; if (targetScrollPosition !== initial) { step(); } else if (callback) { callback(); } }; const smoothScroll = (element, { scrollContainer = window.document.documentElement, direction = "vertical", offset = 0, position = "start", ...scrollOptions } = {}) => new Promise((resolve) => { const scrollOffset = getScrollOffset(scrollContainer, element, direction); const scrollPosition = position === "end" ? scrollOffset - scrollContainer.offsetWidth + element.offsetWidth + offset : scrollOffset - offset; scroll(scrollContainer, direction, scrollPosition, scrollOptions, resolve); }); const smoothScrollIntoView = (element, options) => { const { scrollContainer = window.document.documentElement, direction = "vertical", offset = 0 } = options; const containerWidth = scrollContainer.offsetWidth; const scrollOffset = getScrollOffset(scrollContainer, element, direction); const positionOnScreen = scrollOffset - getScrollPosition(scrollContainer, direction); if (positionOnScreen < offset) { smoothScroll(element, { ...options, position: "start" }); } else if (positionOnScreen > containerWidth - element.offsetWidth - offset) { smoothScroll(element, { ...options, position: "end" }); } }; exports.smoothScroll = smoothScroll; exports.smoothScrollIntoView = smoothScrollIntoView;