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:
@@ -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;
|
||||||
}
|
}
|
||||||
|
137
css/reveal.scss
137
css/reveal.scss
@@ -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
|
||||||
*********************************************/
|
*********************************************/
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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 )
|
||||||
@@ -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.documentElement.classList.add( 'reveal-print', 'print-pdf' );
|
||||||
document.body.style.width = pageWidth + 'px';
|
document.body.style.width = pageWidth + 'px';
|
||||||
document.body.style.height = pageHeight + '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
|
||||||
|
if( printing ) {
|
||||||
slide.style.left = left + 'px';
|
slide.style.left = left + 'px';
|
||||||
slide.style.top = top + '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() );
|
||||||
|
|
||||||
|
if( printing ) {
|
||||||
// Notify subscribers that the PDF layout is good to go
|
// Notify subscribers that the PDF layout is good to go
|
||||||
this.Reveal.dispatchEvent({ type: 'pdf-ready' });
|
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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
41
js/reveal.js
41
js/reveal.js
@@ -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,
|
||||||
|
Reference in New Issue
Block a user