2023-10-06 11:37:58 +02:00
|
|
|
import { HORIZONTAL_SLIDES_SELECTOR, SLIDES_SELECTOR } from '../utils/constants.js'
|
2020-03-16 15:18:47 +01:00
|
|
|
import { queryAll, createStyleSheet } from '../utils/util.js'
|
2020-03-14 08:27:29 +01:00
|
|
|
|
|
|
|
/**
|
2023-09-14 13:00:31 +02:00
|
|
|
* The reader mode lets you read a reveal.js presentation
|
|
|
|
* as a linear scrollable page.
|
2020-03-14 08:27:29 +01:00
|
|
|
*/
|
2023-09-12 17:00:56 +02:00
|
|
|
export default class Reader {
|
2020-03-14 08:27:29 +01:00
|
|
|
|
|
|
|
constructor( Reveal ) {
|
|
|
|
|
|
|
|
this.Reveal = Reveal;
|
|
|
|
|
2023-09-19 11:52:54 +02:00
|
|
|
this.active = false;
|
2023-09-14 15:03:23 +02:00
|
|
|
this.activatedCallbacks = [];
|
|
|
|
|
2023-10-06 09:14:23 +02:00
|
|
|
this.onScroll = this.onScroll.bind( this );
|
|
|
|
|
2020-03-14 08:27:29 +01:00
|
|
|
}
|
|
|
|
|
2023-10-05 14:06:06 +02:00
|
|
|
/**
|
|
|
|
* Activates the reader mode. This rearranges the presentation DOM
|
|
|
|
* by—among other things—wrapping each slide in a page element.
|
|
|
|
*/
|
2023-10-06 09:52:21 +02:00
|
|
|
activate() {
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-09-19 11:52:54 +02:00
|
|
|
if( this.active ) return;
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
const state = this.Reveal.getState();
|
|
|
|
|
2023-09-19 11:52:54 +02:00
|
|
|
this.active = true;
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-09-19 11:52:54 +02:00
|
|
|
this.slideHTMLBeforeActivation = this.Reveal.getSlidesElement().innerHTML;
|
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
const horizontalSlides = queryAll( this.Reveal.getRevealElement(), HORIZONTAL_SLIDES_SELECTOR );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
this.viewportElement.classList.add( 'loading-scroll-mode', 'reveal-reader' );
|
|
|
|
this.viewportElement.addEventListener( 'scroll', this.onScroll );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2021-11-16 14:10:29 +01:00
|
|
|
let presentationBackground;
|
2023-10-06 10:07:19 +02:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
const viewportStyles = window.getComputedStyle( this.viewportElement );
|
2023-10-06 10:07:19 +02:00
|
|
|
if( viewportStyles && viewportStyles.background ) {
|
|
|
|
presentationBackground = viewportStyles.background;
|
2021-11-16 14:10:29 +01:00
|
|
|
}
|
|
|
|
|
2023-09-20 15:00:15 +02:00
|
|
|
const pageElements = [];
|
2023-10-06 11:37:58 +02:00
|
|
|
const pageContainer = horizontalSlides[0].parentNode;
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
function createPage( slide, h, v ) {
|
2021-11-16 14:10:29 +01:00
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
// Wrap the slide in a page element and hide its overflow
|
|
|
|
// so that no page ever flows onto another
|
|
|
|
const page = document.createElement( 'div' );
|
|
|
|
page.className = 'reader-page';
|
|
|
|
pageElements.push( page );
|
2023-09-20 15:00:15 +02:00
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
// Copy the presentation-wide background to each page
|
|
|
|
if( presentationBackground ) {
|
|
|
|
page.style.background = presentationBackground;
|
|
|
|
}
|
2023-09-20 15:00:15 +02:00
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
const stickyContainer = document.createElement( 'div' );
|
|
|
|
stickyContainer.className = 'reader-page-sticky';
|
|
|
|
page.appendChild( stickyContainer );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
const contentContainer = document.createElement( 'div' );
|
|
|
|
contentContainer.className = 'reader-page-content';
|
|
|
|
stickyContainer.appendChild( contentContainer );
|
2023-09-25 12:32:46 +02:00
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
contentContainer.appendChild( slide );
|
|
|
|
|
|
|
|
slide.classList.remove( 'past', 'future' );
|
|
|
|
|
|
|
|
if( typeof h === 'number' ) slide.setAttribute( 'data-index-h', h );
|
|
|
|
if( typeof v === 'number' ) slide.setAttribute( 'data-index-v', v );
|
|
|
|
|
|
|
|
if( slide.slideBackgroundElement ) {
|
|
|
|
slide.slideBackgroundElement.remove( 'past', 'future' );
|
|
|
|
contentContainer.insertBefore( slide.slideBackgroundElement, slide );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
// Slide and slide background layout
|
|
|
|
horizontalSlides.forEach( ( horizontalSlide, h ) => {
|
|
|
|
|
|
|
|
if( this.Reveal.isVerticalStack( horizontalSlide ) ) {
|
|
|
|
horizontalSlide.querySelectorAll( 'section' ).forEach( ( verticalSlide, v ) => {
|
|
|
|
createPage( verticalSlide, h, v );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
createPage( horizontalSlide, h, 0 );
|
2023-09-14 13:00:31 +02:00
|
|
|
}
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
}, this );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
this.createProgressBar();
|
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
// Remove leftover stacks
|
|
|
|
queryAll( this.Reveal.getRevealElement(), '.stack' ).forEach( stack => stack.remove() );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-09-20 15:00:15 +02:00
|
|
|
pageElements.forEach( page => pageContainer.appendChild( page ) );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
// Re-run JS-based content layout after the slide is added to page DOM
|
|
|
|
this.Reveal.slideContent.layout( this.Reveal.getSlidesElement() );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
this.Reveal.layout();
|
2023-10-06 11:37:58 +02:00
|
|
|
this.Reveal.setState( state );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
this.viewportElement.classList.remove( 'loading-scroll-mode' );
|
2023-09-19 11:52:54 +02:00
|
|
|
|
2023-09-14 15:03:23 +02:00
|
|
|
this.activatedCallbacks.forEach( callback => callback() );
|
|
|
|
this.activatedCallbacks = [];
|
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
}
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
createProgressBar() {
|
|
|
|
|
|
|
|
this.progressBar = document.createElement( 'div' );
|
|
|
|
this.progressBar.className = 'reader-progress';
|
|
|
|
|
|
|
|
this.progressBarInner = document.createElement( 'div' );
|
|
|
|
this.progressBarInner.className = 'reader-progress-inner';
|
|
|
|
this.progressBar.appendChild( this.progressBarInner );
|
|
|
|
|
|
|
|
this.progressBarPlayhead = document.createElement( 'div' );
|
|
|
|
this.progressBarPlayhead.className = 'reader-progress-playhead';
|
|
|
|
this.progressBarInner.appendChild( this.progressBarPlayhead );
|
|
|
|
|
|
|
|
this.viewportElement.insertBefore( this.progressBar, this.viewportElement.firstChild );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-10-05 14:06:06 +02:00
|
|
|
/**
|
|
|
|
* Deactivates the reader mode and restores the standard slide-based
|
|
|
|
* presentation.
|
|
|
|
*/
|
2023-09-19 11:52:54 +02:00
|
|
|
deactivate() {
|
|
|
|
|
|
|
|
if( !this.active ) return;
|
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
const state = this.Reveal.getState();
|
|
|
|
|
2023-09-19 11:52:54 +02:00
|
|
|
this.active = false;
|
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
this.viewportElement.removeEventListener( 'scroll', this.onScroll );
|
|
|
|
this.viewportElement.classList.remove( 'reveal-reader' );
|
2023-10-06 09:14:23 +02:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
this.progressBar.remove();
|
2023-10-06 09:14:23 +02:00
|
|
|
|
2023-09-19 11:52:54 +02:00
|
|
|
this.Reveal.getSlidesElement().innerHTML = this.slideHTMLBeforeActivation;
|
|
|
|
this.Reveal.sync();
|
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
this.Reveal.setState( state );
|
2023-09-19 11:52:54 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-10-06 09:52:21 +02:00
|
|
|
toggle( override ) {
|
2023-09-19 11:52:54 +02:00
|
|
|
|
2023-10-06 09:52:21 +02:00
|
|
|
if( typeof override === 'boolean' ) {
|
|
|
|
override ? this.activate() : this.deactivate();
|
2023-09-19 11:52:54 +02:00
|
|
|
}
|
|
|
|
else {
|
2023-10-06 09:52:21 +02:00
|
|
|
this.isActive() ? this.deactivate() : this.activate();
|
2023-09-19 11:52:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
/**
|
2023-09-19 11:52:54 +02:00
|
|
|
* Checks if the reader mode is currently active.
|
2023-09-14 13:00:31 +02:00
|
|
|
*/
|
|
|
|
isActive() {
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-09-19 11:52:54 +02:00
|
|
|
return this.active;
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
}
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
/**
|
|
|
|
* Retrieve a slide by its original h/v index (i.e. the indices the
|
|
|
|
* slide had before being linearized).
|
|
|
|
*
|
|
|
|
* @param {number} h
|
|
|
|
* @param {number} v
|
|
|
|
* @returns {HTMLElement}
|
|
|
|
*/
|
2023-10-06 11:37:58 +02:00
|
|
|
getSlideByIndices( h, v ) {
|
|
|
|
|
|
|
|
const page = this.pages.find( page => page.indexh === h && page.indexv === v );
|
|
|
|
|
|
|
|
return page ? page.slideElement : null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-09-21 13:35:49 +02:00
|
|
|
/**
|
|
|
|
* Updates our reader pages to match the latest configuration and
|
|
|
|
* presentation size.
|
|
|
|
*/
|
|
|
|
sync() {
|
2022-04-04 15:33:44 +02:00
|
|
|
|
2023-09-21 13:35:49 +02:00
|
|
|
const config = this.Reveal.getConfig();
|
2023-09-14 15:03:23 +02:00
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
const slideSize = this.Reveal.getComputedSlideSize( window.innerWidth, window.innerHeight );
|
|
|
|
const scale = this.Reveal.getScale();
|
2023-09-21 13:35:49 +02:00
|
|
|
const readerLayout = config.readerLayout;
|
2023-09-20 15:00:15 +02:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
const viewportHeight = this.viewportElement.offsetHeight;
|
2023-09-21 13:35:49 +02:00
|
|
|
const compactHeight = slideSize.height * scale;
|
|
|
|
const pageHeight = readerLayout === 'full' ? viewportHeight : compactHeight;
|
2022-04-04 15:33:44 +02:00
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
// The height that needs to be scrolled between scroll triggers
|
2023-09-20 15:00:15 +02:00
|
|
|
const scrollTriggerHeight = viewportHeight / 2;
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
this.viewportElement.style.setProperty( '--page-height', pageHeight + 'px' );
|
|
|
|
this.viewportElement.style.scrollSnapType = typeof config.readerScrollSnap === 'string' ?
|
2023-09-21 13:35:49 +02:00
|
|
|
`y ${config.readerScrollSnap}` : '';
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-09-20 15:00:15 +02:00
|
|
|
const pageElements = Array.from( this.Reveal.getRevealElement().querySelectorAll( '.reader-page' ) );
|
2023-09-14 15:03:23 +02:00
|
|
|
|
2023-09-20 15:00:15 +02:00
|
|
|
this.pages = pageElements.map( pageElement => {
|
2023-09-14 13:00:31 +02:00
|
|
|
const page = {
|
|
|
|
pageElement: pageElement,
|
2023-09-20 15:00:15 +02:00
|
|
|
stickyElement: pageElement.querySelector( '.reader-page-sticky' ),
|
2023-09-14 13:00:31 +02:00
|
|
|
slideElement: pageElement.querySelector( 'section' ),
|
2023-09-20 15:00:15 +02:00
|
|
|
backgroundElement: pageElement.querySelector( '.slide-background' ),
|
|
|
|
top: pageElement.offsetTop,
|
2023-10-06 11:37:58 +02:00
|
|
|
scrollTriggers: [],
|
2023-09-14 13:00:31 +02:00
|
|
|
};
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
page.indexh = parseInt( page.slideElement.getAttribute( 'data-index-h' ), 10 );
|
|
|
|
page.indexv = parseInt( page.slideElement.getAttribute( 'data-index-v' ), 10 );
|
|
|
|
|
2023-09-21 13:35:49 +02:00
|
|
|
page.slideElement.style.width = slideSize.width + 'px';
|
|
|
|
page.slideElement.style.height = config.center === true ? '' : slideSize.height + 'px';
|
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
// Each fragment 'group' is an array containing one or more
|
|
|
|
// fragments. Multiple fragments that appear at the same time
|
|
|
|
// are part of the same group.
|
2023-09-20 15:00:15 +02:00
|
|
|
page.fragments = this.Reveal.fragments.sort( pageElement.querySelectorAll( '.fragment:not(.disabled)' ) );
|
2023-09-14 13:00:31 +02:00
|
|
|
page.fragmentGroups = this.Reveal.fragments.sort( pageElement.querySelectorAll( '.fragment' ), true );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
// Create scroll triggers that show/hide fragments
|
|
|
|
if( page.fragmentGroups.length ) {
|
|
|
|
const segmentSize = 1 / ( page.fragmentGroups.length + 1 );
|
|
|
|
page.scrollTriggers.push(
|
|
|
|
// Trigger for the initial state with no fragments visible
|
|
|
|
{ range: [ 0, segmentSize ], fragmentIndex: -1 },
|
2020-11-15 22:08:13 +01:00
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
// Triggers for each fragment group
|
|
|
|
...page.fragmentGroups.map( ( fragments, i ) => ({
|
|
|
|
range: [ segmentSize * ( i + 1 ), segmentSize * ( i + 2 ) ],
|
|
|
|
fragmentIndex: i
|
|
|
|
}))
|
|
|
|
);
|
2023-09-21 13:35:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add scroll padding based on how many scroll triggers we have
|
|
|
|
page.scrollPadding = scrollTriggerHeight * page.scrollTriggers.length;
|
|
|
|
|
|
|
|
// In the compact layout, only slides with scroll triggers cover the
|
|
|
|
// full viewport height. This helps avoid empty gaps before or after
|
|
|
|
// a sticky slide.
|
|
|
|
if( readerLayout === 'compact' && page.scrollTriggers.length > 0 ) {
|
|
|
|
page.pageHeight = viewportHeight;
|
|
|
|
page.pageElement.style.setProperty( '--page-height', viewportHeight + 'px' );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
page.pageHeight = pageHeight;
|
|
|
|
page.pageElement.style.removeProperty( '--page-height' );
|
|
|
|
}
|
|
|
|
|
|
|
|
page.pageElement.style.scrollSnapAlign = page.pageHeight < viewportHeight ? 'center' : 'start';
|
|
|
|
|
|
|
|
// This variable is used to pad the height of our page in CSS
|
|
|
|
page.pageElement.style.setProperty( '--page-scroll-padding', page.scrollPadding + 'px' );
|
|
|
|
|
|
|
|
// The total height including scrollable space
|
|
|
|
page.totalHeight = page.pageHeight + page.scrollPadding;
|
|
|
|
|
|
|
|
page.bottom = page.top + page.totalHeight;
|
|
|
|
|
|
|
|
// If this is a sticky page, stick it to the vertical center
|
|
|
|
if( page.scrollTriggers.length > 0 ) {
|
|
|
|
page.stickyElement.style.position = 'sticky';
|
|
|
|
page.stickyElement.style.top = Math.max( ( viewportHeight - page.pageHeight ) / 2, 0 ) + 'px';
|
2023-09-14 15:03:23 +02:00
|
|
|
|
|
|
|
// Make this page freeze at the vertical center of the viewport
|
|
|
|
page.top -= ( viewportHeight - page.pageHeight ) / 2;
|
2023-09-14 13:00:31 +02:00
|
|
|
}
|
2023-09-21 13:35:49 +02:00
|
|
|
else {
|
|
|
|
page.stickyElement.style.position = 'relative';
|
|
|
|
}
|
2022-05-31 11:50:26 +02:00
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
return page;
|
|
|
|
} );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
this.createProgressBarSlides();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
createProgressBarSlides() {
|
|
|
|
|
|
|
|
this.progressBarInner.querySelectorAll( '.reader-progress-slide' ).forEach( slide => slide.remove() );
|
|
|
|
|
|
|
|
const spacing = 2;
|
|
|
|
|
|
|
|
const viewportHeight = this.viewportElement.offsetHeight;
|
|
|
|
const scrollHeight = this.viewportElement.scrollHeight;
|
|
|
|
|
|
|
|
this.progressBarHeight = this.progressBarInner.offsetHeight;
|
|
|
|
this.playheadHeight = viewportHeight / scrollHeight * this.progressBarHeight;
|
|
|
|
this.progressBarScrollableHeight = this.progressBarHeight - this.playheadHeight;
|
|
|
|
|
|
|
|
this.progressBarPlayhead.style.height = this.playheadHeight - spacing + 'px';
|
|
|
|
|
|
|
|
this.pages.forEach( page => {
|
|
|
|
|
|
|
|
page.progressBarSlide = document.createElement( 'div' );
|
|
|
|
page.progressBarSlide.className = 'reader-progress-slide';
|
|
|
|
page.progressBarSlide.style.top = page.top / scrollHeight * this.progressBarHeight + 'px';
|
|
|
|
page.progressBarSlide.style.height = page.totalHeight / scrollHeight * this.progressBarHeight - spacing + 'px';
|
|
|
|
this.progressBarInner.appendChild( page.progressBarSlide );
|
|
|
|
|
|
|
|
} );
|
|
|
|
|
2020-03-14 08:27:29 +01:00
|
|
|
}
|
|
|
|
|
2023-09-14 15:03:23 +02:00
|
|
|
layout() {
|
2023-09-12 17:00:56 +02:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
if( this.isActive() ) {
|
|
|
|
this.sync();
|
|
|
|
this.onScroll();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
moveProgressBarTo( progress ) {
|
|
|
|
|
|
|
|
this.progressBarPlayhead.style.transform = `translateY(${progress * this.progressBarScrollableHeight}px)`;
|
|
|
|
|
|
|
|
this.pages.forEach( ( page ) => {
|
|
|
|
page.progressBarSlide.classList.toggle( 'active', !!page.active );
|
|
|
|
page.scrollTriggers.forEach( trigger => {
|
|
|
|
// page.progressBarSlide.classList.toggle( 'active', !!trigger.active );
|
|
|
|
} );
|
|
|
|
} );
|
2023-09-12 17:00:56 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-09-14 15:03:23 +02:00
|
|
|
scrollToSlide( slideElement ) {
|
|
|
|
|
2023-09-19 11:52:54 +02:00
|
|
|
if( !this.active ) {
|
2023-09-14 15:03:23 +02:00
|
|
|
this.activatedCallbacks.push( () => this.scrollToSlide( slideElement ) );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
slideElement.parentNode.scrollIntoView();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-09-14 13:00:31 +02:00
|
|
|
onScroll() {
|
2023-09-12 17:00:56 +02:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
const viewportHeight = this.viewportElement.offsetHeight;
|
|
|
|
const scrollTop = this.viewportElement.scrollTop;
|
2023-09-12 17:00:56 +02:00
|
|
|
|
2023-10-05 14:06:06 +02:00
|
|
|
// Find the page closest to the center of the viewport, this
|
|
|
|
// is the page we want to focus and activate
|
|
|
|
const activePage = this.pages.reduce( ( closestPage, page ) => {
|
|
|
|
const distance = Math.abs( ( page.top + page.pageHeight / 2 ) - scrollTop - viewportHeight / 2 );
|
|
|
|
return distance < closestPage.distance ? { page, distance } : closestPage;
|
|
|
|
}, { page: this.pages[0], distance: Infinity } ).page;
|
|
|
|
|
2023-09-25 12:32:46 +02:00
|
|
|
this.pages.forEach( ( page, pageIndex ) => {
|
2023-09-14 15:17:07 +02:00
|
|
|
const isWithinPreloadRange = scrollTop + viewportHeight >= page.top - viewportHeight && scrollTop < page.top + page.bottom + viewportHeight;
|
|
|
|
const isPartiallyVisible = scrollTop + viewportHeight >= page.top && scrollTop < page.top + page.bottom;
|
2023-09-12 17:00:56 +02:00
|
|
|
|
2023-09-14 15:17:07 +02:00
|
|
|
// Preload content when it appears within range
|
|
|
|
if( isWithinPreloadRange ) {
|
|
|
|
if( !page.preloaded ) {
|
|
|
|
page.preloaded = true;
|
2023-09-14 13:00:31 +02:00
|
|
|
this.Reveal.slideContent.load( page.slideElement );
|
2023-09-14 15:17:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( page.preloaded ) {
|
|
|
|
page.preloaded = false;
|
|
|
|
this.Reveal.slideContent.unload( page.slideElement );
|
|
|
|
}
|
|
|
|
|
2023-10-05 14:06:06 +02:00
|
|
|
// Activate the current page — there can only be one active page at
|
|
|
|
// a time.
|
|
|
|
if( page === activePage ) {
|
2023-09-22 10:31:34 +02:00
|
|
|
if( !page.active ) {
|
|
|
|
page.active = true;
|
2023-09-20 15:00:15 +02:00
|
|
|
page.pageElement.classList.add( 'present' );
|
|
|
|
page.slideElement.classList.add( 'present' );
|
2023-09-25 12:32:46 +02:00
|
|
|
|
2023-10-06 11:37:58 +02:00
|
|
|
this.Reveal.setCurrentReaderPage( page.pageElement, page.indexh, page.indexv );
|
2023-09-14 13:00:31 +02:00
|
|
|
this.Reveal.slideContent.startEmbeddedContent( page.slideElement );
|
2023-09-20 15:00:15 +02:00
|
|
|
|
|
|
|
if( page.backgroundElement ) {
|
|
|
|
this.Reveal.slideContent.startEmbeddedContent( page.backgroundElement );
|
|
|
|
}
|
2023-09-14 13:00:31 +02:00
|
|
|
}
|
2023-09-14 15:17:07 +02:00
|
|
|
}
|
2023-10-05 14:06:06 +02:00
|
|
|
// Deactivate previously active pages
|
2023-09-22 10:31:34 +02:00
|
|
|
else if( page.active ) {
|
|
|
|
page.active = false;
|
2023-09-20 15:00:15 +02:00
|
|
|
page.pageElement.classList.remove( 'present' );
|
|
|
|
page.slideElement.classList.remove( 'present' );
|
2023-09-14 15:17:07 +02:00
|
|
|
this.Reveal.slideContent.stopEmbeddedContent( page.slideElement );
|
2023-09-20 15:00:15 +02:00
|
|
|
|
|
|
|
if( page.backgroundElement ) {
|
|
|
|
this.Reveal.slideContent.stopEmbeddedContent( page.backgroundElement );
|
|
|
|
}
|
2023-09-14 15:17:07 +02:00
|
|
|
}
|
2023-09-14 13:00:31 +02:00
|
|
|
|
2023-09-14 15:17:07 +02:00
|
|
|
// Handle scroll freezing and triggers for slides in view
|
|
|
|
if( isPartiallyVisible && page.totalHeight > page.pageHeight ) {
|
2023-09-20 15:00:15 +02:00
|
|
|
let scrollProgress = ( scrollTop - page.top ) / page.scrollPadding;
|
2023-09-14 15:17:07 +02:00
|
|
|
scrollProgress = Math.max( Math.min( scrollProgress, 1 ), 0 );
|
2023-09-14 13:00:31 +02:00
|
|
|
|
2023-09-14 15:17:07 +02:00
|
|
|
page.scrollTriggers.forEach( trigger => {
|
|
|
|
if( scrollProgress >= trigger.range[0] && scrollProgress < trigger.range[1] ) {
|
|
|
|
if( !trigger.active ) {
|
|
|
|
trigger.active = true;
|
|
|
|
this.Reveal.fragments.update( trigger.fragmentIndex, page.fragments, page.slideElement );
|
2023-09-14 13:00:31 +02:00
|
|
|
}
|
2023-09-14 15:17:07 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
trigger.active = false;
|
|
|
|
}
|
|
|
|
} );
|
2023-09-14 13:00:31 +02:00
|
|
|
}
|
|
|
|
} );
|
2020-03-14 08:27:29 +01:00
|
|
|
|
2023-10-10 10:24:02 +02:00
|
|
|
this.moveProgressBarTo( scrollTop / ( this.viewportElement.scrollHeight - viewportHeight ) );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
get viewportElement() {
|
|
|
|
|
|
|
|
return this.Reveal.getViewportElement();
|
|
|
|
|
2020-03-14 08:27:29 +01:00
|
|
|
}
|
|
|
|
|
2020-11-15 22:08:13 +01:00
|
|
|
}
|