From 27bca0fc6624908f0294f14347238967147b1fee Mon Sep 17 00:00:00 2001 From: alistair3149 Date: Sat, 25 May 2024 19:26:38 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20improve=20scrolling=20perfo?= =?UTF-8?q?rmance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skins.citizen.scripts/scrollObserver.js | 7 +-- resources/skins.citizen.scripts/skin.js | 58 ++++++++++--------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/resources/skins.citizen.scripts/scrollObserver.js b/resources/skins.citizen.scripts/scrollObserver.js index 28f5761f..7bd0b43c 100644 --- a/resources/skins.citizen.scripts/scrollObserver.js +++ b/resources/skins.citizen.scripts/scrollObserver.js @@ -1,5 +1,5 @@ /** - * Create an observer based vertical scroll direction + * Create an observer based vertical scroll direction with debouncing * * @param {Function} onScrollDown functionality for when viewport is scrolled down * @param {Function} onScrollUp functionality for when viewport is scrolled up @@ -7,8 +7,6 @@ * @return {void} */ function initDirectionObserver( onScrollDown, onScrollUp, threshold ) { - const throttle = require( 'mediawiki.util' ).throttle; - let lastScrollTop = window.scrollY; const onScroll = () => { @@ -26,7 +24,8 @@ function initDirectionObserver( onScrollDown, onScrollUp, threshold ) { lastScrollTop = scrollTop; }; - window.addEventListener( 'scroll', throttle( onScroll, 250 ) ); + const debouncedOnScroll = mw.util.debounce( onScroll, 100 ); + window.addEventListener( 'scroll', mw.util.throttle( debouncedOnScroll, 250 ) ); } /** diff --git a/resources/skins.citizen.scripts/skin.js b/resources/skins.citizen.scripts/skin.js index 857a7510..9f879de2 100644 --- a/resources/skins.citizen.scripts/skin.js +++ b/resources/skins.citizen.scripts/skin.js @@ -1,3 +1,8 @@ +const SCROLL_DOWN_CLASS = 'citizen-scroll--down'; +const SCROLL_UP_CLASS = 'citizen-scroll--up'; +const STICKY_SENTINEL_ID = 'citizen-page-header-sticky-sentinel'; +const STICKY_CLASS = 'citizen-page-header--sticky'; + /** * Wait for first paint before calling this function. * (see T234570#5779890, T246419). @@ -10,40 +15,39 @@ function enableCssAnimations( document ) { } /** - * Add a class to indicate that sticky header is active + * Initializes the sticky header functionality by setting up scroll observers and intersection observers. * - * @param {Document} document - * @return {void} + * @param {Document} document - The document object representing the webpage. */ function initStickyHeader( document ) { - const scrollObserver = require( './scrollObserver.js' ); + const { initDirectionObserver, initIntersectionObserver } = require( './scrollObserver.js' ); - // Detect scroll direction and add the right class - scrollObserver.initDirectionObserver( - () => { - document.body.classList.remove( 'citizen-scroll--up' ); - document.body.classList.add( 'citizen-scroll--down' ); - }, - () => { - document.body.classList.remove( 'citizen-scroll--down' ); - document.body.classList.add( 'citizen-scroll--up' ); - }, - 10 - ); + const toggleScrollClass = ( removeClass, addClass ) => { + return () => { + window.requestAnimationFrame( () => { + document.body.classList.remove( removeClass ); + document.body.classList.add( addClass ); + } ); + }; + }; - const sentinel = document.getElementById( 'citizen-page-header-sticky-sentinel' ); + const addScrollDownClass = toggleScrollClass( SCROLL_UP_CLASS, SCROLL_DOWN_CLASS ); + const addScrollUpClass = toggleScrollClass( SCROLL_DOWN_CLASS, SCROLL_UP_CLASS ); + + const toggleStickyClass = () => { + return ( state ) => { + window.requestAnimationFrame( () => { + document.body.classList.toggle( STICKY_CLASS, state ); + } ); + }; + }; + + initDirectionObserver( addScrollDownClass, addScrollUpClass, 10 ); + + const sentinel = document.getElementById( STICKY_SENTINEL_ID ); - // In some pages we use display:none to disable the sticky header - // Do not start observer if it is set to display:none if ( sentinel && getComputedStyle( sentinel ).getPropertyValue( 'display' ) !== 'none' ) { - const observer = scrollObserver.initIntersectionObserver( - () => { - document.body.classList.add( 'citizen-page-header--sticky' ); - }, - () => { - document.body.classList.remove( 'citizen-page-header--sticky' ); - } - ); + const observer = initIntersectionObserver( toggleStickyClass( true ), toggleStickyClass( false ) ); observer.observe( sentinel ); } }