1
0
mirror of https://github.com/hakimel/reveal.js.git synced 2025-08-06 06:38:08 +02:00

foundation for reader mode, activate via 'mode=reader/print' config param

This commit is contained in:
Hakim El Hattab
2023-09-12 17:00:56 +02:00
parent 487cc860f8
commit 6aa1eae796
5 changed files with 224 additions and 32 deletions

View File

@@ -5,7 +5,7 @@
* https://revealjs.com/pdf-export/ * https://revealjs.com/pdf-export/
*/ */
html.print-pdf { html.reveal-print {
* { * {
-webkit-print-color-adjust: exact; -webkit-print-color-adjust: exact;
} }

View File

@@ -1864,6 +1864,143 @@ $notesWidthPercent: 25%;
} }
/*********************************************
* READER MODE
*********************************************/
html.reveal-reader {
width: 100%;
height: 100%;
overflow: visible;
.reveal-viewport, body {
margin: 0 auto !important;
overflow: auto;
}
.reveal .controls,
.reveal .progress,
.reveal .playback {
display: none !important;
}
.reveal {
display: flex;
justify-content: center;
width: auto !important;
height: auto !important;
overflow: visible !important;
}
.reveal .slides {
position: static;
zoom: 1 !important;
pointer-events: initial;
transform-origin: 50% 0;
left: auto;
top: auto;
margin: 0 !important;
padding: 0 !important;
overflow: visible;
display: block;
perspective: none;
perspective-origin: 50% 50%;
}
.reveal .slides .reader-page {
display: grid;
place-items: center;
position: relative;
overflow: hidden;
z-index: 1;
page-break-after: always;
}
.reveal .slides .reader-page section {
visibility: visible !important;
display: block !important;
position: relative !important;
margin: 0 !important;
padding: 0 !important;
box-sizing: border-box !important;
min-height: 1px;
opacity: 1 !important;
transform-style: flat !important;
transform: none !important;
}
.reveal section.stack {
position: relative !important;
margin: 0 !important;
padding: 0 !important;
page-break-after: avoid !important;
height: auto !important;
min-height: auto !important;
}
/* Slide backgrounds are nested inside of the page in reader mode */
.reveal .backgrounds {
display: none;
}
.reveal .slide-background {
display: block !important;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: auto !important;
visibility: visible;
opacity: 1;
}
/* Display slide speaker notes when 'showNotes' is enabled */
.reveal.show-notes {
max-width: none;
max-height: none;
}
.reveal .speaker-notes-pdf {
display: block;
width: 100%;
height: auto;
max-height: none;
top: auto;
right: auto;
bottom: auto;
left: auto;
z-index: 100;
}
/* Layout option which makes notes appear on a separate page */
.reveal .speaker-notes-pdf[data-layout="separate-page"] {
position: relative;
color: inherit;
background-color: transparent;
padding: 20px;
page-break-after: always;
border: 0;
}
/* Display slide numbers when 'slideNumber' is enabled */
.reveal .slide-number-pdf {
display: block;
position: absolute;
font-size: 14px;
}
/* This accessibility tool is not useful in PDF and breaks it visually */
.aria-status {
display: none;
}
}
/********************************************* /*********************************************
* PRINT STYLES * PRINT STYLES
*********************************************/ *********************************************/

View File

@@ -38,7 +38,7 @@ export default class Notes {
*/ */
update() { update() {
if( this.Reveal.getConfig().showNotes && this.element && this.Reveal.getCurrentSlide() && !this.Reveal.print.isPrintingPDF() ) { if( this.Reveal.getConfig().showNotes && this.element && this.Reveal.getCurrentSlide() && !this.Reveal.reader.isActive() ) {
this.element.innerHTML = this.getSlideNotes() || '<span class="notes-placeholder">No notes on this slide.</span>'; this.element.innerHTML = this.getSlideNotes() || '<span class="notes-placeholder">No notes on this slide.</span>';
@@ -54,7 +54,7 @@ export default class Notes {
*/ */
updateVisibility() { updateVisibility() {
if( this.Reveal.getConfig().showNotes && this.hasNotes() && !this.Reveal.print.isPrintingPDF() ) { if( this.Reveal.getConfig().showNotes && this.hasNotes() && !this.Reveal.reader.isActive() ) {
this.Reveal.getRevealElement().classList.add( 'show-notes' ); this.Reveal.getRevealElement().classList.add( 'show-notes' );
} }
else { else {

View File

@@ -4,7 +4,7 @@ import { queryAll, createStyleSheet } from '../utils/util.js'
/** /**
* Setups up our presentation for printing/exporting to PDF. * Setups up our presentation for printing/exporting to PDF.
*/ */
export default class Print { export default class Reader {
constructor( Reveal ) { constructor( Reveal ) {
@@ -13,10 +13,11 @@ export default class Print {
} }
/** /**
* Configures the presentation for printing to a static * Configures the presentation for printing to a static.
* PDF.
*/ */
async setupPDF() { async setup() {
const printing = this.isPrintMode();
const config = this.Reveal.getConfig(); const config = this.Reveal.getConfig();
const slides = queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR ) const slides = queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR )
@@ -28,11 +29,11 @@ export default class Print {
// Dimensions of the PDF pages // Dimensions of the PDF pages
const pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ), const pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ),
pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) ); pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) );
// Dimensions of slides within the pages // Dimensions of slides within the pages
const slideWidth = slideSize.width, const slideWidth = slideSize.width,
slideHeight = slideSize.height; slideHeight = slideSize.height;
await new Promise( requestAnimationFrame ); await new Promise( requestAnimationFrame );
@@ -42,9 +43,14 @@ export default class Print {
// Limit the size of certain elements to the dimensions of the slide // Limit the size of certain elements to the dimensions of the slide
createStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' ); createStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' );
document.documentElement.classList.add( 'print-pdf' ); if( printing ) {
document.body.style.width = pageWidth + 'px'; document.documentElement.classList.add( 'reveal-print', 'print-pdf' );
document.body.style.height = pageHeight + 'px'; document.body.style.width = pageWidth + 'px';
document.body.style.height = pageHeight + 'px';
}
else {
document.documentElement.classList.add( 'reveal-reader' );
}
const viewportElement = document.querySelector( '.reveal-viewport' ); const viewportElement = document.querySelector( '.reveal-viewport' );
let presentationBackground; let presentationBackground;
@@ -94,7 +100,7 @@ export default class Print {
const page = document.createElement( 'div' ); const page = document.createElement( 'div' );
pages.push( page ); pages.push( page );
page.className = 'pdf-page'; page.className = printing ? 'pdf-page' : 'reader-page';
page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px'; page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px';
// Copy the presentation-wide background to each individual // Copy the presentation-wide background to each individual
@@ -106,8 +112,11 @@ export default class Print {
page.appendChild( slide ); page.appendChild( slide );
// Position the slide inside of the page // Position the slide inside of the page
slide.style.left = left + 'px'; if( printing ) {
slide.style.top = top + 'px'; slide.style.left = left + 'px';
slide.style.top = top + 'px';
}
slide.style.width = slideWidth + 'px'; slide.style.width = slideWidth + 'px';
this.Reveal.slideContent.layout( slide ); this.Reveal.slideContent.layout( slide );
@@ -213,6 +222,9 @@ export default class Print {
}, this ); }, this );
// Remove leftover stacks
queryAll( pageContainer, '.reveal .stack' ).forEach( stack => stack.remove() );
await new Promise( requestAnimationFrame ); await new Promise( requestAnimationFrame );
pages.forEach( page => pageContainer.appendChild( page ) ); pages.forEach( page => pageContainer.appendChild( page ) );
@@ -220,17 +232,43 @@ export default class Print {
// Re-run JS-based content layout after the slide is added to page DOM // Re-run JS-based content layout after the slide is added to page DOM
this.Reveal.slideContent.layout( this.Reveal.getSlidesElement() ); this.Reveal.slideContent.layout( this.Reveal.getSlidesElement() );
// Notify subscribers that the PDF layout is good to go if( printing ) {
this.Reveal.dispatchEvent({ type: 'pdf-ready' }); // Notify subscribers that the PDF layout is good to go
this.Reveal.dispatchEvent({ type: 'pdf-ready' });
}
} }
/** /**
* Checks if this instance is being used to print a PDF. * Checks if reveal.js was initialized in printing mode.
*/ */
isPrintingPDF() { isPrintMode() {
return ( /print-pdf/gi ).test( window.location.search ); if( typeof this._isPrintMode === 'undefined' ) {
this._isPrintMode = this.Reveal.getConfig().mode === 'pdf' ||
( /print-pdf/gi ).test( window.location.search );
}
return this._isPrintMode;
}
/**
* Checks if reveal.js was initialized in reader mode.
*/
isReaderMode() {
if( typeof this._isReaderMode === 'undefined' ) {
this._isReaderMode = this.Reveal.getConfig().mode === 'reader';
}
return this._isReaderMode;
}
isActive() {
return this.isPrintMode() || this.isReaderMode();
} }

View File

@@ -11,7 +11,7 @@ import Controls from './controllers/controls.js'
import Progress from './controllers/progress.js' import Progress from './controllers/progress.js'
import Pointer from './controllers/pointer.js' import Pointer from './controllers/pointer.js'
import Plugins from './controllers/plugins.js' import Plugins from './controllers/plugins.js'
import Print from './controllers/print.js' import Reader from './controllers/reader.js'
import Touch from './controllers/touch.js' import Touch from './controllers/touch.js'
import Focus from './controllers/focus.js' import Focus from './controllers/focus.js'
import Notes from './controllers/notes.js' import Notes from './controllers/notes.js'
@@ -113,7 +113,7 @@ export default function( revealElement, options ) {
progress = new Progress( Reveal ), progress = new Progress( Reveal ),
pointer = new Pointer( Reveal ), pointer = new Pointer( Reveal ),
plugins = new Plugins( Reveal ), plugins = new Plugins( Reveal ),
print = new Print( Reveal ), reader = new Reader( Reveal ),
focus = new Focus( Reveal ), focus = new Focus( Reveal ),
touch = new Touch( Reveal ), touch = new Touch( Reveal ),
notes = new Notes( Reveal ); notes = new Notes( Reveal );
@@ -225,18 +225,25 @@ export default function( revealElement, options ) {
}); });
}, 1 ); }, 1 );
// Special setup and config is required when printing to PDF // Special setup and config is required when initializing a deck
if( print.isPrintingPDF() ) { // to be read or printed linearly
if( reader.isPrintMode() || reader.isReaderMode() ) {
removeEventListeners(); removeEventListeners();
window.addEventListener( 'resize', onWindowResize, false );
// Avoid content flickering during layout
revealElement.style.visibility = 'hidden';
// The document needs to have loaded for the PDF layout // The document needs to have loaded for the PDF layout
// measurements to be accurate // measurements to be accurate
if( document.readyState === 'complete' ) { if( document.readyState === 'complete' ) {
print.setupPDF(); reader.setup().then( () => layout() );
} }
else { else {
window.addEventListener( 'load', () => { window.addEventListener( 'load', () => {
print.setupPDF(); reader.setup().then( () => layout() );
} ); } );
} }
} }
@@ -861,7 +868,7 @@ export default function( revealElement, options ) {
*/ */
function layout() { function layout() {
if( dom.wrapper && !print.isPrintingPDF() ) { if( dom.wrapper && !reader.isPrintMode() ) {
if( !config.disableLayout ) { if( !config.disableLayout ) {
@@ -901,6 +908,15 @@ export default function( revealElement, options ) {
dom.slides.style.right = ''; dom.slides.style.right = '';
transformSlides( { layout: '' } ); transformSlides( { layout: '' } );
} }
else if( reader.isActive() ) {
dom.slides.style.zoom = '';
dom.slides.style.left = 'auto';
dom.slides.style.top = 'auto';
dom.slides.style.bottom = 'auto';
dom.slides.style.right = 'auto';
dom.slides.style.height = 'auto';
transformSlides( { layout: 'scale('+ scale +')' } );
}
else { else {
dom.slides.style.zoom = ''; dom.slides.style.zoom = '';
dom.slides.style.left = '50%'; dom.slides.style.left = '50%';
@@ -921,7 +937,7 @@ export default function( revealElement, options ) {
continue; continue;
} }
if( config.center || slide.classList.contains( 'center' ) ) { if( ( config.center || slide.classList.contains( 'center' ) ) && !reader.isActive() ) {
// Vertical stacks are not centred since their section // Vertical stacks are not centred since their section
// children will be // children will be
if( slide.classList.contains( 'stack' ) ) { if( slide.classList.contains( 'stack' ) ) {
@@ -1597,7 +1613,7 @@ export default function( revealElement, options ) {
let slides = Util.queryAll( dom.wrapper, selector ), let slides = Util.queryAll( dom.wrapper, selector ),
slidesLength = slides.length; slidesLength = slides.length;
let printMode = print.isPrintingPDF(); let printMode = reader.isActive();
let loopedForwards = false; let loopedForwards = false;
let loopedBackwards = false; let loopedBackwards = false;
@@ -1757,7 +1773,7 @@ export default function( revealElement, options ) {
} }
// All slides need to be visible when exporting to PDF // All slides need to be visible when exporting to PDF
if( print.isPrintingPDF() ) { if( reader.isPrintMode() || reader.isReaderMode() ) {
viewDistance = Number.MAX_VALUE; viewDistance = Number.MAX_VALUE;
} }
@@ -2696,7 +2712,8 @@ export default function( revealElement, options ) {
isSpeakerNotes: notes.isSpeakerNotesWindow.bind( notes ), isSpeakerNotes: notes.isSpeakerNotesWindow.bind( notes ),
isOverview: overview.isActive.bind( overview ), isOverview: overview.isActive.bind( overview ),
isFocused: focus.isFocused.bind( focus ), isFocused: focus.isFocused.bind( focus ),
isPrintingPDF: print.isPrintingPDF.bind( print ), isReaderMode: reader.isReaderMode.bind( reader ),
isPrintingPDF: reader.isPrintMode.bind( reader ),
// Checks if reveal.js has been loaded and is ready for use // Checks if reveal.js has been loaded and is ready for use
isReady: () => ready, isReady: () => ready,
@@ -2816,8 +2833,8 @@ export default function( revealElement, options ) {
getStatusText, getStatusText,
// Controllers // Controllers
print,
focus, focus,
reader,
progress, progress,
controls, controls,
location, location,