UNPKG

uikit

Version:

UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.

154 lines (115 loc) 4.53 kB
import {css} from './style'; import {Promise} from './promise'; import {isVisible, parents} from './filter'; import {offset, offsetPosition} from './dimensions'; import {clamp, findIndex, intersectRect, isDocument, isWindow, toNode, toWindow} from './lang'; export function isInView(element, offsetTop = 0, offsetLeft = 0) { if (!isVisible(element)) { return false; } return intersectRect(...scrollParents(element).map(parent => { const {top, left, bottom, right} = offset(getViewport(parent)); return { top: top - offsetTop, left: left - offsetLeft, bottom: bottom + offsetTop, right: right + offsetLeft }; }).concat(offset(element))); } export function scrollTop(element, top) { if (isWindow(element) || isDocument(element)) { element = getScrollingElement(element); } else { element = toNode(element); } element.scrollTop = top; } export function scrollIntoView(element, {offset: offsetBy = 0} = {}) { if (!isVisible(element)) { return; } const parents = scrollParents(element); let diff = 0; return parents.reduce((fn, scrollElement, i) => { const {scrollTop, scrollHeight} = scrollElement; const maxScroll = scrollHeight - getViewportClientHeight(scrollElement); let top = Math.ceil( offset(parents[i - 1] || element).top - offset(getViewport(scrollElement)).top - offsetBy + diff + scrollTop ); if (top > maxScroll) { diff = top - maxScroll; top = maxScroll; } else { diff = 0; } return () => scrollTo(scrollElement, top - scrollTop).then(fn); }, () => Promise.resolve())(); function scrollTo(element, top) { return new Promise(resolve => { const scroll = element.scrollTop; const duration = getDuration(Math.abs(top)); const start = Date.now(); (function step() { const percent = ease(clamp((Date.now() - start) / duration)); scrollTop(element, scroll + top * percent); // scroll more if we have not reached our destination if (percent !== 1) { requestAnimationFrame(step); } else { resolve(); } })(); }); } function getDuration(dist) { return 40 * Math.pow(dist, .375); } function ease(k) { return 0.5 * (1 - Math.cos(Math.PI * k)); } } export function scrolledOver(element, heightOffset = 0) { if (!isVisible(element)) { return 0; } const [scrollElement] = scrollParents(element, /auto|scroll/, true); const {scrollHeight, scrollTop} = scrollElement; const clientHeight = getViewportClientHeight(scrollElement); const viewportTop = offsetPosition(element)[0] - scrollTop - offsetPosition(scrollElement)[0]; const viewportDist = Math.min(clientHeight, viewportTop + scrollTop); const top = viewportTop - viewportDist; const dist = Math.min( element.offsetHeight + heightOffset + viewportDist, scrollHeight - (viewportTop + scrollTop), scrollHeight - clientHeight ); return clamp(-1 * top / dist); } export function scrollParents(element, overflowRe = /auto|scroll|hidden/, scrollable = false) { const scrollEl = getScrollingElement(element); let ancestors = parents(element).reverse(); ancestors = ancestors.slice(ancestors.indexOf(scrollEl) + 1); const fixedIndex = findIndex(ancestors, el => css(el, 'position') === 'fixed'); if (~fixedIndex) { ancestors = ancestors.slice(fixedIndex); } return [scrollEl].concat(ancestors.filter(parent => overflowRe.test(css(parent, 'overflow')) && (!scrollable || parent.scrollHeight > getViewportClientHeight(parent))) ).reverse(); } export function getViewport(scrollElement) { return scrollElement === getScrollingElement(scrollElement) ? window : scrollElement; } // iOS 12 returns <body> as scrollingElement export function getViewportClientHeight(scrollElement) { return (scrollElement === getScrollingElement(scrollElement) ? document.documentElement : scrollElement).clientHeight; } function getScrollingElement(element) { const {document} = toWindow(element); return document.scrollingElement || document.documentElement; }