Skip to content

Commit a77688b

Browse files
committed
improve layout and scroll-view performance
1 parent b5b37a5 commit a77688b

2 files changed

Lines changed: 60 additions & 32 deletions

File tree

js/controllers/scrollview.js

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export default class ScrollView {
1717
this.Reveal = Reveal;
1818

1919
this.active = false;
20+
this.activeProgressBarPage = null;
21+
this.activeProgressBarTrigger = null;
2022
this.activatedCallbacks = [];
2123

2224
this.onScroll = this.onScroll.bind( this );
@@ -171,6 +173,11 @@ export default class ScrollView {
171173
this.viewportElement.removeEventListener( 'scroll', this.onScroll );
172174
this.viewportElement.classList.remove( 'reveal-scroll' );
173175

176+
if( this.pendingScrollRaf ) {
177+
cancelAnimationFrame( this.pendingScrollRaf );
178+
this.pendingScrollRaf = 0;
179+
}
180+
174181
this.removeProgressBar();
175182

176183
this.Reveal.getSlidesElement().innerHTML = this.slideHTMLBeforeActivation;
@@ -262,6 +269,9 @@ export default class ScrollView {
262269
this.progressBar = null;
263270
}
264271

272+
this.activeProgressBarPage = null;
273+
this.activeProgressBarTrigger = null;
274+
265275
}
266276

267277
layout() {
@@ -551,6 +561,13 @@ export default class ScrollView {
551561
syncProgressBar() {
552562

553563
this.progressBarInner.querySelectorAll( '.scrollbar-slide' ).forEach( slide => slide.remove() );
564+
this.activeProgressBarPage = null;
565+
this.activeProgressBarTrigger = null;
566+
567+
this.getAllPages().forEach( page => {
568+
page.progressBarSlide = null;
569+
page.scrollTriggers.forEach( trigger => trigger.progressBarElement = null );
570+
} );
554571

555572
const scrollHeight = this.viewportElement.scrollHeight;
556573
const viewportHeight = this.viewportElement.offsetHeight;
@@ -581,7 +598,7 @@ export default class ScrollView {
581598
this.progressBarInner.appendChild( page.progressBarSlide );
582599

583600
// Visual representations of each scroll trigger
584-
page.scrollTriggerElements = page.scrollTriggers.map( ( trigger, i ) => {
601+
page.scrollTriggers.forEach( ( trigger, i ) => {
585602

586603
const triggerElement = document.createElement( 'div' );
587604
triggerElement.className = 'scrollbar-trigger';
@@ -591,18 +608,13 @@ export default class ScrollView {
591608

592609
if( i === 0 ) triggerElement.style.display = 'none';
593610

594-
return triggerElement;
611+
trigger.progressBarElement = triggerElement;
595612

596613
} );
597614

598615
} );
599616

600617
}
601-
else {
602-
603-
this.pages.forEach( page => page.progressBarSlide = null );
604-
605-
}
606618

607619
}
608620

@@ -621,6 +633,7 @@ export default class ScrollView {
621633
const scrollProgressMid = Math.max( Math.min( ( scrollTop + viewportHeight / 2 ) / this.viewportElement.scrollHeight, 1 ), 0 );
622634

623635
let activePage;
636+
let activeScrollTrigger = null;
624637

625638
this.slideTriggers.forEach( ( trigger ) => {
626639
const { page } = trigger;
@@ -655,6 +668,7 @@ export default class ScrollView {
655668
activePage.scrollTriggers.forEach( ( trigger ) => {
656669
if( scrollProgressMid >= trigger.range[0] && scrollProgressMid <= trigger.range[1] ) {
657670
this.activateTrigger( trigger );
671+
activeScrollTrigger = trigger;
658672
}
659673
else if( trigger.active ) {
660674
this.deactivateTrigger( trigger );
@@ -663,30 +677,38 @@ export default class ScrollView {
663677
}
664678

665679
// Update our visual progress indication
666-
this.setProgressBarValue( scrollTop / ( this.viewportElement.scrollHeight - viewportHeight ) );
680+
this.setProgressBarValue(
681+
scrollTop / ( this.viewportElement.scrollHeight - viewportHeight ),
682+
activePage,
683+
activeScrollTrigger
684+
);
667685

668686
}
669687

670688
/**
671689
* Moves the progress bar playhead to the specified position.
672690
*
673691
* @param {number} progress 0-1
692+
* @param {object} activePage
693+
* @param {object} activeScrollTrigger
674694
*/
675-
setProgressBarValue( progress ) {
695+
setProgressBarValue( progress, activePage = null, activeScrollTrigger = null ) {
676696

677697
if( this.progressBar ) {
678698

679699
this.progressBarPlayhead.style.transform = `translateY(${progress * this.progressBarScrollableHeight}px)`;
680700

681-
this.getAllPages()
682-
.filter( page => page.progressBarSlide )
683-
.forEach( ( page ) => {
684-
page.progressBarSlide.classList.toggle( 'active', page.active === true );
701+
if( this.activeProgressBarPage !== activePage ) {
702+
this.activeProgressBarPage?.progressBarSlide?.classList.remove( 'active' );
703+
activePage?.progressBarSlide?.classList.add( 'active' );
704+
this.activeProgressBarPage = activePage;
705+
}
685706

686-
page.scrollTriggers.forEach( ( trigger, i ) => {
687-
page.scrollTriggerElements[i].classList.toggle( 'active', page.active === true && trigger.active === true );
688-
} );
689-
} );
707+
if( this.activeProgressBarTrigger !== activeScrollTrigger ) {
708+
this.activeProgressBarTrigger?.progressBarElement?.classList.remove( 'active' );
709+
activeScrollTrigger?.progressBarElement?.classList.add( 'active' );
710+
this.activeProgressBarTrigger = activeScrollTrigger;
711+
}
690712

691713
this.showProgressBar();
692714

@@ -909,8 +931,13 @@ export default class ScrollView {
909931

910932
onScroll() {
911933

912-
this.syncScrollPosition();
913-
this.storeScrollPosition();
934+
if( this.pendingScrollRaf ) return;
935+
936+
this.pendingScrollRaf = requestAnimationFrame( () => {
937+
this.pendingScrollRaf = 0;
938+
this.syncScrollPosition();
939+
this.storeScrollPosition();
940+
} );
914941

915942
}
916943

js/reveal.js

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -859,31 +859,32 @@ export default function( revealElement, options ) {
859859
transformSlides( { layout: 'translate(-50%, -50%) scale('+ scale +')' } );
860860
}
861861

862-
// Select all slides, vertical and horizontal
863-
const slides = Array.from( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) );
862+
const visibleSlides = Array.from( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) )
863+
.filter( slide => slide.style.display !== 'none' );
864864

865-
for( let i = 0, len = slides.length; i < len; i++ ) {
866-
const slide = slides[ i ];
865+
// Pass 1: read sizes of visible slides
866+
const tops = new Array( visibleSlides.length );
867+
for( let i = 0, len = visibleSlides.length; i < len; i++ ) {
868+
const slide = visibleSlides[ i ];
867869

868-
// Don't bother updating invisible slides
869-
if( slide.style.display === 'none' ) {
870-
continue;
871-
}
872-
873-
if( ( config.center || slide.classList.contains( 'center' ) ) ) {
870+
if( config.center || slide.classList.contains( 'center' ) ) {
874871
// Vertical stacks are not centred since their section
875872
// children will be
876873
if( slide.classList.contains( 'stack' ) ) {
877-
slide.style.top = 0;
874+
tops[ i ] = 0;
878875
}
879876
else {
880-
slide.style.top = Math.max( ( size.height - slide.scrollHeight ) / 2, 0 ) + 'px';
877+
tops[ i ] = Math.max( ( size.height - slide.scrollHeight ) / 2, 0 ) + 'px';
881878
}
882879
}
883880
else {
884-
slide.style.top = '';
881+
tops[ i ] = '';
885882
}
883+
}
886884

885+
// Pass 2: write top values to visible slides
886+
for( let i = 0, len = visibleSlides.length; i < len; i++ ) {
887+
visibleSlides[ i ].style.top = tops[ i ];
887888
}
888889

889890
if( oldScale !== scale ) {

0 commit comments

Comments
 (0)