refactor(core): ♻️ set up scroll direction observer in setupObservers

This is needed for centralizing the observers.
This commit is contained in:
alistair3149 2024-11-06 19:31:49 -05:00
parent 241ef66893
commit 3b8022c3c1
No known key found for this signature in database
4 changed files with 66 additions and 55 deletions

View file

@ -4,36 +4,47 @@
* @param {Function} onScrollDown - Function to be called when scrolling down. * @param {Function} onScrollDown - Function to be called when scrolling down.
* @param {Function} onScrollUp - Function to be called when scrolling up. * @param {Function} onScrollUp - Function to be called when scrolling up.
* @param {number} threshold - The threshold for significant scroll position change. * @param {number} threshold - The threshold for significant scroll position change.
* @return {Object}
*/ */
function initDirectionObserver( onScrollDown, onScrollUp, threshold ) { function initDirectionObserver( onScrollDown, onScrollUp, threshold = 0 ) {
let lastScrollTop = 0; let lastScrollTop = 0;
let lastScrollDirection = ''; let lastScrollDirection = '';
let isScrolling = false; let isScrolling = false;
window.addEventListener( 'scroll', () => { const handleScroll = () => {
const currentScrollTop = window.scrollY;
if ( Math.abs( currentScrollTop - lastScrollTop ) < threshold ) {
isScrolling = false;
return;
}
if ( currentScrollTop > lastScrollTop && lastScrollDirection !== 'down' ) {
lastScrollDirection = 'down';
onScrollDown();
} else if ( currentScrollTop < lastScrollTop && lastScrollDirection !== 'up' ) {
lastScrollDirection = 'up';
onScrollUp();
}
lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop;
isScrolling = false;
};
const onScroll = () => {
if ( !isScrolling ) { if ( !isScrolling ) {
window.requestAnimationFrame( () => { window.requestAnimationFrame( handleScroll );
const currentScrollTop = window.scrollY;
if ( Math.abs( currentScrollTop - lastScrollTop ) < threshold ) {
isScrolling = false;
return;
}
if ( currentScrollTop > lastScrollTop && lastScrollDirection !== 'down' ) {
lastScrollDirection = 'down';
onScrollDown();
} else if ( currentScrollTop < lastScrollTop && lastScrollDirection !== 'up' ) {
lastScrollDirection = 'up';
onScrollUp();
}
// For Mobile or negative scrolling
lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop;
isScrolling = false;
} );
isScrolling = true; isScrolling = true;
} }
} ); };
return {
resume: () => {
window.addEventListener( 'scroll', onScroll );
},
pause: () => {
window.removeEventListener( 'scroll', onScroll );
}
};
} }
/** /**

View file

@ -3,6 +3,7 @@ const
scrollObserver = require( './scrollObserver.js' ), scrollObserver = require( './scrollObserver.js' ),
resizeObserver = require( './resizeObserver.js' ), resizeObserver = require( './resizeObserver.js' ),
initSectionObserver = require( './sectionObserver.js' ), initSectionObserver = require( './sectionObserver.js' ),
stickyHeader = require( './stickyHeader.js' ),
initTableOfContents = require( './tableOfContents.js' ), initTableOfContents = require( './tableOfContents.js' ),
deferUntilFrame = require( './deferUntilFrame.js' ), deferUntilFrame = require( './deferUntilFrame.js' ),
TOC_ID = 'citizen-toc', TOC_ID = 'citizen-toc',
@ -16,7 +17,9 @@ const
.map( ( sel ) => `.mw-parser-output ${ sel }` ).join( ', ' ), .map( ( sel ) => `.mw-parser-output ${ sel }` ).join( ', ' ),
HEADLINE_SELECTOR = [ '.mw-headline', ...HEADING_TAGS.map( ( tag ) => `${ tag }[id]` ) ] HEADLINE_SELECTOR = [ '.mw-headline', ...HEADING_TAGS.map( ( tag ) => `${ tag }[id]` ) ]
.map( ( sel ) => `.mw-parser-output ${ sel }` ).join( ', ' ), .map( ( sel ) => `.mw-parser-output ${ sel }` ).join( ', ' ),
TOC_SECTION_ID_PREFIX = 'toc-'; TOC_SECTION_ID_PREFIX = 'toc-',
SCROLL_DOWN_CLASS = 'citizen-scroll--down',
SCROLL_UP_CLASS = 'citizen-scroll--up';
/** /**
* @callback OnIntersection * @callback OnIntersection
@ -194,7 +197,27 @@ const main = () => {
const tableOfContents = isToCUpdatingAllowed ? const tableOfContents = isToCUpdatingAllowed ?
setupTableOfContents( tocElement, bodyContent, initSectionObserver ) : null; setupTableOfContents( tocElement, bodyContent, initSectionObserver ) : null;
const pagetitleObserver = scrollObserver.initScrollObserver( const stickyIntersection = document.getElementById( 'citizen-page-header-sticky-sentinel' );
const isStickyHeaderAllowed = !!stickyIntersection;
const scrollDirectionObserver = scrollObserver.initDirectionObserver(
() => {
document.body.classList.remove( SCROLL_UP_CLASS );
document.body.classList.add( SCROLL_DOWN_CLASS );
},
() => {
document.body.classList.remove( SCROLL_DOWN_CLASS );
document.body.classList.add( SCROLL_UP_CLASS );
},
50
);
if ( isStickyHeaderAllowed ) {
stickyHeader.init();
scrollDirectionObserver.resume();
}
const pageHeaderObserver = scrollObserver.initScrollObserver(
() => { () => {
// TODO: Below page header // TODO: Below page header
}, },
@ -203,15 +226,20 @@ const main = () => {
} }
); );
pageHeaderObserver.observe( stickyIntersection );
const bodyObserver = resizeObserver.initResizeObserver( const bodyObserver = resizeObserver.initResizeObserver(
() => { () => {
// Disable all CSS transition during resize // Disable all CSS animation during resize
if ( document.documentElement.classList.contains( 'citizen-animations-ready' ) ) { if ( document.documentElement.classList.contains( 'citizen-animations-ready' ) ) {
document.documentElement.classList.remove( 'citizen-animations-ready' ); document.documentElement.classList.remove( 'citizen-animations-ready' );
} }
scrollDirectionObserver.pause();
}, },
() => { () => {
// Enable CSS animation after resize is finished
document.documentElement.classList.add( 'citizen-animations-ready' ); document.documentElement.classList.add( 'citizen-animations-ready' );
scrollDirectionObserver.resume();
} }
); );
bodyObserver.observe( document.body ); bodyObserver.observe( document.body );

View file

@ -50,14 +50,12 @@ function main( window ) {
search = require( './search.js' ), search = require( './search.js' ),
dropdown = require( './dropdown.js' ), dropdown = require( './dropdown.js' ),
setupObservers = require( './setupObservers.js' ), setupObservers = require( './setupObservers.js' ),
stickyHeader = require( './stickyHeader.js' ),
lastModified = require( './lastModified.js' ), lastModified = require( './lastModified.js' ),
share = require( './share.js' ); share = require( './share.js' );
dropdown.init(); dropdown.init();
search.init( window ); search.init( window );
echo(); echo();
stickyHeader.init();
lastModified.init(); lastModified.init();
share.init(); share.init();

View file

@ -1,25 +1,5 @@
const SCROLL_DOWN_CLASS = 'citizen-scroll--down';
const SCROLL_UP_CLASS = 'citizen-scroll--up';
const STICKY_CLASS = 'citizen-page-header--sticky'; const STICKY_CLASS = 'citizen-page-header--sticky';
const { initDirectionObserver, initScrollObserver } = require( './scrollObserver.js' ); const { initScrollObserver } = require( './scrollObserver.js' );
/**
* Observes the scroll direction and adds/removes corresponding classes to the body element.
*
* @return {void}
*/
function observeScrollDirection() {
const toggleScrollClass = ( removeClass, addClass ) => () => {
window.requestAnimationFrame( () => {
document.body.classList.remove( removeClass );
document.body.classList.add( addClass );
} );
};
const addScrollDownClass = toggleScrollClass( SCROLL_UP_CLASS, SCROLL_DOWN_CLASS );
const addScrollUpClass = toggleScrollClass( SCROLL_DOWN_CLASS, SCROLL_UP_CLASS );
initDirectionObserver( addScrollDownClass, addScrollUpClass, 50 );
}
/** /**
* Initializes the sticky header functionality for Citizen * Initializes the sticky header functionality for Citizen
@ -27,13 +7,6 @@ function observeScrollDirection() {
* @return {void} * @return {void}
*/ */
function init() { function init() {
const sentinel = document.getElementById( 'citizen-page-header-sticky-sentinel' );
const shouldStickyHeader = getComputedStyle( sentinel ).getPropertyValue( 'display' ) !== 'none';
if ( !shouldStickyHeader ) {
return;
}
observeScrollDirection();
const header = document.querySelector( '.citizen-page-header' ); const header = document.querySelector( '.citizen-page-header' );
const placeholder = document.getElementById( 'citizen-page-header-sticky-placeholder' ); const placeholder = document.getElementById( 'citizen-page-header-sticky-placeholder' );
let staticHeaderHeight = header.getBoundingClientRect().height; let staticHeaderHeight = header.getBoundingClientRect().height;
@ -89,6 +62,7 @@ function init() {
window.removeEventListener( 'resize', onResizeEnd ); window.removeEventListener( 'resize', onResizeEnd );
} }
); );
const sentinel = document.getElementById( 'citizen-page-header-sticky-sentinel' );
observer.observe( sentinel ); observer.observe( sentinel );
} }