1
0
mirror of https://github.com/hakimel/reveal.js.git synced 2025-07-31 03:40:28 +02:00

refactoring

This commit is contained in:
Hakim El Hattab
2023-10-10 14:47:08 +02:00
parent a6abd0423e
commit 2c5a83c945
7 changed files with 172 additions and 128 deletions

View File

@@ -33,6 +33,8 @@ html.reveal-full-page {
background-color: #fff; background-color: #fff;
color: #000; color: #000;
--r-reader-progress-width: 8px;
} }
// Force the presentation to cover the full viewport when we // Force the presentation to cover the full viewport when we
@@ -2003,7 +2005,8 @@ $notesWidthPercent: 25%;
} }
} }
.reveal-viewport.reveal-reader::-webkit-scrollbar { .reveal-viewport.reveal-reader[data-reader-scroll-bar="true"]::-webkit-scrollbar,
.reveal-viewport.reveal-reader[data-reader-scroll-bar="auto"]::-webkit-scrollbar {
display: none; display: none;
} }
@@ -2021,45 +2024,45 @@ $notesWidthPercent: 25%;
.reader-progress-inner { .reader-progress-inner {
position: absolute; position: absolute;
width: 8px; width: var(--r-reader-progress-width);
height: 90vh; height: 90vh;
right: $controlsSpacing; right: $controlsSpacing;
top: 0; top: 0;
transform: translateY(-50%); transform: translateY(-50%);
border-radius: 8px; border-radius: var(--r-reader-progress-width);
z-index: 10; z-index: 10;
// Hit area }
&:after {
content: ''; // Hit area
position: absolute; .reader-progress-inner:after {
width: 200%; content: '';
height: 100%; position: absolute;
top: 0; width: 200%;
left: -50%; height: 100%;
background: rgba( 0, 0, 0, 0 ); top: 0;
z-index: -1; left: -50%;
} background: rgba( 0, 0, 0, 0 );
z-index: -1;
} }
.reader-progress-playhead { .reader-progress-playhead {
position: absolute; position: absolute;
width: 8px; width: var(--r-reader-progress-width);
height: 8px; height: var(--r-reader-progress-width);
top: 0; top: 0;
left: 0; left: 0;
border-radius: 8px; border-radius: var(--r-reader-progress-width);
background-color: #fff; background-color: #fff;
transition: all 0.1s ease;
z-index: 2; z-index: 2;
} }
.reader-progress-slide { .reader-progress-slide {
position: absolute; position: absolute;
width: 100%; width: 100%;
transition: all 0.2s ease;
background-color: rgba( 255, 255, 255, 0.2 ); background-color: rgba( 255, 255, 255, 0.2 );
border-radius: 8px; border-radius: var(--r-reader-progress-width);
transition: all 0.2s ease;
} }
.reader-progress-slide.active { .reader-progress-slide.active {

2
dist/reveal.css vendored

File diff suppressed because one or more lines are too long

2
dist/reveal.esm.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/reveal.js vendored

File diff suppressed because one or more lines are too long

2
dist/reveal.js.map vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,10 @@
import { HORIZONTAL_SLIDES_SELECTOR, SLIDES_SELECTOR } from '../utils/constants.js' import { HORIZONTAL_SLIDES_SELECTOR } from '../utils/constants.js'
import { queryAll, createStyleSheet } from '../utils/util.js' import { queryAll } from '../utils/util.js'
const HIDE_SCROLLBAR_TIMEOUT = 500; const HIDE_SCROLLBAR_TIMEOUT = 500;
const PROGRESS_SPACING = 4;
const MIN_PROGRESS_SEGMENT_HEIGHT = 6;
const MIN_PLAYHEAD_HEIGHT = 18;
/** /**
* The reader mode lets you read a reveal.js presentation * The reader mode lets you read a reveal.js presentation
@@ -37,7 +40,7 @@ export default class Reader {
const horizontalSlides = queryAll( this.Reveal.getRevealElement(), HORIZONTAL_SLIDES_SELECTOR ); const horizontalSlides = queryAll( this.Reveal.getRevealElement(), HORIZONTAL_SLIDES_SELECTOR );
this.viewportElement.classList.add( 'loading-scroll-mode', 'reveal-reader' ); this.viewportElement.classList.add( 'loading-scroll-mode', 'reveal-reader' );
this.viewportElement.addEventListener( 'scroll', this.onScroll ); this.viewportElement.addEventListener( 'scroll', this.onScroll, { passive: true } );
let presentationBackground; let presentationBackground;
@@ -137,7 +140,6 @@ export default class Reader {
this.Reveal.getSlidesElement().innerHTML = this.slideHTMLBeforeActivation; this.Reveal.getSlidesElement().innerHTML = this.slideHTMLBeforeActivation;
this.Reveal.sync(); this.Reveal.sync();
this.Reveal.setState( state ); this.Reveal.setState( state );
} }
@@ -178,11 +180,84 @@ export default class Reader {
} }
/**
* Renders the progress bar component.
*/
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 );
const handleMouseDown = ( event ) => {
event.preventDefault();
this.draggingProgressBar = true;
document.addEventListener( 'mousemove', handleDocumentMouseMove );
document.addEventListener( 'mouseup', handleDocumentMouseUp );
handleDocumentMouseMove( event );
};
const handleDocumentMouseMove = ( event ) => {
let progress = ( event.clientY - this.progressBarInner.getBoundingClientRect().top ) / this.progressBarHeight;
progress = Math.max( Math.min( progress, 1 ), 0 );
this.viewportElement.scrollTop = progress * ( this.viewportElement.scrollHeight - this.viewportElement.offsetHeight );
};
const handleDocumentMouseUp = ( event ) => {
this.draggingProgressBar = false;
this.showProgressBar();
document.removeEventListener( 'mousemove', handleDocumentMouseMove );
document.removeEventListener( 'mouseup', handleDocumentMouseUp );
};
this.progressBarInner.addEventListener( 'mousedown', handleMouseDown );
}
removeProgressBar() {
if( this.progressBar ) {
this.progressBar.remove();
this.progressBar = null;
}
}
layout() {
if( this.isActive() ) {
this.syncPages();
this.onScroll();
}
}
/** /**
* Updates our reader pages to match the latest configuration and * Updates our reader pages to match the latest configuration and
* presentation size. * presentation size.
*/ */
sync() { syncPages() {
const config = this.Reveal.getConfig(); const config = this.Reveal.getConfig();
@@ -280,9 +355,13 @@ export default class Reader {
return page; return page;
} ); } );
this.viewportElement.setAttribute( 'data-reader-scroll-bar', config.readerScrollBar )
if( config.readerScrollBar ) { if( config.readerScrollBar ) {
this.createProgressBar(); // Create the progress bar if it doesn't already exist
this.createProgressBarSlides(); if( !this.progressBar ) this.createProgressBar();
this.syncProgressBar();
} }
else { else {
this.removeProgressBar(); this.removeProgressBar();
@@ -290,125 +369,75 @@ export default class Reader {
} }
createProgressBar() { /**
* Rerenders progress bar segments so that they match the current
if( this.progressBar ) return; * reveal.js config and size.
*/
this.progressBar = document.createElement( 'div' ); syncProgressBar() {
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 );
const handleMouseDown = ( event ) => {
event.preventDefault();
this.draggingProgressBar = true;
document.addEventListener( 'mousemove', handleDocumentMouseMove );
document.addEventListener( 'mouseup', handleDocumentMouseUp );
handleDocumentMouseMove( event );
};
const handleDocumentMouseMove = ( event ) => {
let progress = ( event.clientY - this.progressBarInner.getBoundingClientRect().top ) / this.progressBarHeight;
progress = Math.max( Math.min( progress, 1 ), 0 );
this.viewportElement.scrollTop = progress * ( this.viewportElement.scrollHeight - this.viewportElement.offsetHeight );
};
const handleDocumentMouseUp = ( event ) => {
this.draggingProgressBar = false;
this.showProgressBar();
document.removeEventListener( 'mousemove', handleDocumentMouseMove );
document.removeEventListener( 'mouseup', handleDocumentMouseUp );
};
this.progressBarInner.addEventListener( 'mousedown', handleMouseDown );
}
removeProgressBar() {
if( this.progressBar ) {
this.progressBar.remove();
this.progressBar = null;
}
}
createProgressBarSlides() {
this.progressBarInner.querySelectorAll( '.reader-progress-slide' ).forEach( slide => slide.remove() ); this.progressBarInner.querySelectorAll( '.reader-progress-slide' ).forEach( slide => slide.remove() );
const spacing = 4;
const viewportHeight = this.viewportElement.offsetHeight; const viewportHeight = this.viewportElement.offsetHeight;
const scrollHeight = this.viewportElement.scrollHeight; const scrollHeight = this.viewportElement.scrollHeight;
this.progressBarHeight = this.progressBarInner.offsetHeight; this.progressBarHeight = this.progressBarInner.offsetHeight;
this.playheadHeight = viewportHeight / scrollHeight * this.progressBarHeight; this.playheadHeight = Math.max( viewportHeight / scrollHeight * this.progressBarHeight, MIN_PLAYHEAD_HEIGHT );
this.progressBarScrollableHeight = this.progressBarHeight - this.playheadHeight; this.progressBarScrollableHeight = this.progressBarHeight - this.playheadHeight;
this.progressBarPlayhead.style.height = this.playheadHeight - spacing + 'px'; this.progressBarPlayhead.style.height = this.playheadHeight - PROGRESS_SPACING + 'px';
this.pages.forEach( page => { const progressSegmentHeight = viewportHeight / scrollHeight * this.progressBarHeight;
page.progressBarSlide = document.createElement( 'div' ); // Don't show individual segments if they're too small
page.progressBarSlide.className = 'reader-progress-slide'; if( progressSegmentHeight > MIN_PROGRESS_SEGMENT_HEIGHT ) {
page.progressBarSlide.classList.toggle( 'has-triggers', page.scrollTriggers.length > 0 );
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 );
page.scrollTriggers.forEach( trigger => { this.pages.forEach( page => {
const triggerElement = document.createElement( 'div' ); page.progressBarSlide = document.createElement( 'div' );
triggerElement.className = 'reader-progress-trigger'; page.progressBarSlide.className = 'reader-progress-slide';
triggerElement.style.top = trigger.range[0] * page.totalHeight / scrollHeight * this.progressBarHeight + 'px'; page.progressBarSlide.classList.toggle( 'has-triggers', page.scrollTriggers.length > 0 );
triggerElement.style.height = ( trigger.range[1] - trigger.range[0] ) * page.totalHeight / scrollHeight * this.progressBarHeight - spacing + 'px'; page.progressBarSlide.style.top = page.top / scrollHeight * this.progressBarHeight + 'px';
page.progressBarSlide.appendChild( triggerElement ); page.progressBarSlide.style.height = page.totalHeight / scrollHeight * this.progressBarHeight - PROGRESS_SPACING + 'px';
this.progressBarInner.appendChild( page.progressBarSlide );
// Create visual representations for each scroll trigger
page.scrollTriggers.forEach( trigger => {
const triggerElement = document.createElement( 'div' );
triggerElement.className = 'reader-progress-trigger';
triggerElement.style.top = trigger.range[0] * page.totalHeight / scrollHeight * this.progressBarHeight + 'px';
triggerElement.style.height = ( trigger.range[1] - trigger.range[0] ) * page.totalHeight / scrollHeight * this.progressBarHeight - PROGRESS_SPACING + 'px';
page.progressBarSlide.appendChild( triggerElement );
} );
} ); } );
} ); }
else {
} this.pages.forEach( page => page.progressBarSlide = null );
layout() {
if( this.isActive() ) {
this.sync();
this.onScroll();
} }
} }
moveProgressBarTo( progress ) { /**
* Moves the progress bar playhead to the specified position.
*
* @param {number} progress 0-1
*/
setProgressBarValue( progress ) {
if( this.progressBar ) { if( this.progressBar ) {
this.progressBarPlayhead.style.transform = `translateY(${progress * this.progressBarScrollableHeight}px)`; this.progressBarPlayhead.style.transform = `translateY(${progress * this.progressBarScrollableHeight}px)`;
this.pages.forEach( ( page ) => { this.pages
page.progressBarSlide.classList.toggle( 'active', !!page.active ); .filter( page => page.progressBarSlide )
} ); .forEach( ( page ) => {
page.progressBarSlide.classList.toggle( 'active', !!page.active );
} );
this.showProgressBar(); this.showProgressBar();
@@ -416,6 +445,10 @@ export default class Reader {
} }
/**
* Show the progress bar and, if configured, automatically hide
* it after a delay.
*/
showProgressBar() { showProgressBar() {
this.progressBar.classList.add( 'visible' ); this.progressBar.classList.add( 'visible' );
@@ -432,6 +465,11 @@ export default class Reader {
} }
/**
* Scrolls the given slide element into view.
*
* @param {HTMLElement} slideElement
*/
scrollToSlide( slideElement ) { scrollToSlide( slideElement ) {
if( !this.active ) { if( !this.active ) {
@@ -451,6 +489,7 @@ export default class Reader {
// Find the page closest to the center of the viewport, this // Find the page closest to the center of the viewport, this
// is the page we want to focus and activate // is the page we want to focus and activate
const activePage = this.pages.reduce( ( closestPage, page ) => { const activePage = this.pages.reduce( ( closestPage, page ) => {
// For tall pages with multiple scroll triggers we need to // For tall pages with multiple scroll triggers we need to
// check the distnace from both the top of the page and the // check the distnace from both the top of the page and the
// bottom // bottom
@@ -458,7 +497,9 @@ export default class Reader {
Math.abs( ( page.top + page.pageHeight / 2 ) - scrollTop - viewportHeight / 2 ), Math.abs( ( page.top + page.pageHeight / 2 ) - scrollTop - viewportHeight / 2 ),
Math.abs( ( page.top + ( page.totalHeight - page.pageHeight / 2 ) ) - scrollTop - viewportHeight / 2 ) Math.abs( ( page.top + ( page.totalHeight - page.pageHeight / 2 ) ) - scrollTop - viewportHeight / 2 )
); );
return distance < closestPage.distance ? { page, distance } : closestPage; return distance < closestPage.distance ? { page, distance } : closestPage;
}, { page: this.pages[0], distance: Infinity } ).page; }, { page: this.pages[0], distance: Infinity } ).page;
this.pages.forEach( ( page, pageIndex ) => { this.pages.forEach( ( page, pageIndex ) => {
@@ -477,9 +518,9 @@ export default class Reader {
this.Reveal.slideContent.unload( page.slideElement ); this.Reveal.slideContent.unload( page.slideElement );
} }
// Activate the current page — there can only be one active page at // Activate the current page
// a time.
if( page === activePage ) { if( page === activePage ) {
// Ignore if the page is already active
if( !page.active ) { if( !page.active ) {
page.active = true; page.active = true;
page.pageElement.classList.add( 'present' ); page.pageElement.classList.add( 'present' );
@@ -505,7 +546,7 @@ export default class Reader {
} }
} }
// Handle scroll freezing and triggers for slides in view // Handle scroll triggers for slides in view
if( isPartiallyVisible && page.totalHeight > page.pageHeight ) { if( isPartiallyVisible && page.totalHeight > page.pageHeight ) {
let scrollProgress = ( scrollTop - page.top ) / page.scrollPadding; let scrollProgress = ( scrollTop - page.top ) / page.scrollPadding;
scrollProgress = Math.max( Math.min( scrollProgress, 1 ), 0 ); scrollProgress = Math.max( Math.min( scrollProgress, 1 ), 0 );
@@ -524,7 +565,7 @@ export default class Reader {
} }
} ); } );
this.moveProgressBarTo( scrollTop / ( this.viewportElement.scrollHeight - viewportHeight ) ); this.setProgressBarValue( scrollTop / ( this.viewportElement.scrollHeight - viewportHeight ) );
} }