UNPKG

quasar

Version:

Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time

203 lines (164 loc) 5.31 kB
import { hasScrollbar, getVerticalScrollPosition, getHorizontalScrollPosition } from './scroll.js' import { getEventPath, listenOpts, stopAndPrevent } from '../event/event.js' import { client } from '../../plugins/platform/Platform.js' let registered = 0, scrollPositionX, scrollPositionY, maxScrollTop, vpPendingUpdate = false, bodyLeft, bodyTop, href, closeTimer = null function onWheel (e) { if (shouldPreventScroll(e)) { stopAndPrevent(e) } } function shouldPreventScroll (e) { if (e.target === document.body || e.target.classList.contains('q-layout__backdrop')) { return true } const path = getEventPath(e), shift = e.shiftKey && !e.deltaX, scrollY = !shift && Math.abs(e.deltaX) <= Math.abs(e.deltaY), delta = shift || scrollY ? e.deltaY : e.deltaX for (let index = 0; index < path.length; index++) { const el = path[ index ] if (hasScrollbar(el, scrollY)) { return scrollY ? ( delta < 0 && el.scrollTop === 0 ? true : delta > 0 && el.scrollTop + el.clientHeight === el.scrollHeight ) : ( delta < 0 && el.scrollLeft === 0 ? true : delta > 0 && el.scrollLeft + el.clientWidth === el.scrollWidth ) } } return true } function onAppleScroll (e) { if (e.target === document) { // required, otherwise iOS blocks further scrolling // until the mobile scrollbar dissappears document.scrollingElement.scrollTop = document.scrollingElement.scrollTop // eslint-disable-line } } function onAppleResize (evt) { if (vpPendingUpdate === true) { return } vpPendingUpdate = true requestAnimationFrame(() => { vpPendingUpdate = false const { height } = evt.target, { clientHeight, scrollTop } = document.scrollingElement if (maxScrollTop === void 0 || height !== window.innerHeight) { maxScrollTop = clientHeight - height document.scrollingElement.scrollTop = scrollTop } if (scrollTop > maxScrollTop) { document.scrollingElement.scrollTop -= Math.ceil((scrollTop - maxScrollTop) / 8) } }) } function apply (action) { const body = document.body, hasViewport = window.visualViewport !== void 0 if (action === 'add') { const { overflowY, overflowX } = window.getComputedStyle(body) scrollPositionX = getHorizontalScrollPosition(window) scrollPositionY = getVerticalScrollPosition(window) bodyLeft = body.style.left bodyTop = body.style.top href = window.location.href body.style.left = `-${ scrollPositionX }px` body.style.top = `-${ scrollPositionY }px` if (overflowX !== 'hidden' && (overflowX === 'scroll' || body.scrollWidth > window.innerWidth)) { body.classList.add('q-body--force-scrollbar-x') } if (overflowY !== 'hidden' && (overflowY === 'scroll' || body.scrollHeight > window.innerHeight)) { body.classList.add('q-body--force-scrollbar-y') } body.classList.add('q-body--prevent-scroll') document.qScrollPrevented = true if (client.is.ios === true) { if (hasViewport === true) { window.scrollTo(0, 0) window.visualViewport.addEventListener('resize', onAppleResize, listenOpts.passiveCapture) window.visualViewport.addEventListener('scroll', onAppleResize, listenOpts.passiveCapture) window.scrollTo(0, 0) } else { window.addEventListener('scroll', onAppleScroll, listenOpts.passiveCapture) } } } if (client.is.desktop === true && client.is.mac === true) { // ref. https://developers.google.com/web/updates/2017/01/scrolling-intervention window[ `${ action }EventListener` ]('wheel', onWheel, listenOpts.notPassive) } if (action === 'remove') { if (client.is.ios === true) { if (hasViewport === true) { window.visualViewport.removeEventListener('resize', onAppleResize, listenOpts.passiveCapture) window.visualViewport.removeEventListener('scroll', onAppleResize, listenOpts.passiveCapture) } else { window.removeEventListener('scroll', onAppleScroll, listenOpts.passiveCapture) } } body.classList.remove('q-body--prevent-scroll') body.classList.remove('q-body--force-scrollbar-x') body.classList.remove('q-body--force-scrollbar-y') document.qScrollPrevented = false body.style.left = bodyLeft body.style.top = bodyTop // scroll back only if route has not changed if (window.location.href === href) { window.scrollTo(scrollPositionX, scrollPositionY) } maxScrollTop = void 0 } } export default function (state) { let action = 'add' if (state === true) { registered++ if (closeTimer !== null) { clearTimeout(closeTimer) closeTimer = null return } if (registered > 1) { return } } else { if (registered === 0) { return } registered-- if (registered > 0) { return } action = 'remove' if (client.is.ios === true && client.is.nativeMobile === true) { closeTimer !== null && clearTimeout(closeTimer) closeTimer = setTimeout(() => { apply(action) closeTimer = null }, 100) return } } apply(action) }