Просмотр исходного кода

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

Hakim El Hattab 1 год назад
Родитель
Сommit
6aa1eae796
5 измененных файлов с 224 добавлено и 32 удалено
  1. 1 1
      css/print/pdf.scss
  2. 137 0
      css/reveal.scss
  3. 2 2
      js/controllers/notes.js
  4. 55 17
      js/controllers/reader.js
  5. 29 12
      js/reveal.js

+ 1 - 1
css/print/pdf.scss

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

+ 137 - 0
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
  *********************************************/

+ 2 - 2
js/controllers/notes.js

@@ -38,7 +38,7 @@ export default class Notes {
 	 */
 	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>';
 
@@ -54,7 +54,7 @@ export default class Notes {
 	 */
 	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' );
 		}
 		else {

+ 55 - 17
js/controllers/print.js → js/controllers/reader.js

@@ -4,7 +4,7 @@ import { queryAll, createStyleSheet } from '../utils/util.js'
 /**
  * Setups up our presentation for printing/exporting to PDF.
  */
-export default class Print {
+export default class Reader {
 
 	constructor( Reveal ) {
 
@@ -13,10 +13,11 @@ export default class Print {
 	}
 
 	/**
-	 * Configures the presentation for printing to a static
-	 * PDF.
+	 * Configures the presentation for printing to a static.
 	 */
-	async setupPDF() {
+	async setup() {
+
+		const printing = this.isPrintMode();
 
 		const config = this.Reveal.getConfig();
 		const slides = queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR )
@@ -28,11 +29,11 @@ export default class Print {
 
 		// Dimensions of the PDF pages
 		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
 		const slideWidth = slideSize.width,
-			slideHeight = slideSize.height;
+			  slideHeight = slideSize.height;
 
 		await new Promise( requestAnimationFrame );
 
@@ -42,9 +43,14 @@ export default class Print {
 		// 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}' );
 
-		document.documentElement.classList.add( 'print-pdf' );
-		document.body.style.width = pageWidth + 'px';
-		document.body.style.height = pageHeight + 'px';
+		if( printing ) {
+			document.documentElement.classList.add( 'reveal-print', 'print-pdf' );
+			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' );
 		let presentationBackground;
@@ -94,7 +100,7 @@ export default class Print {
 				const page = document.createElement( 'div' );
 				pages.push( page );
 
-				page.className = 'pdf-page';
+				page.className = printing ? 'pdf-page' : 'reader-page';
 				page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px';
 
 				// Copy the presentation-wide background to each individual
@@ -106,8 +112,11 @@ export default class Print {
 				page.appendChild( slide );
 
 				// Position the slide inside of the page
-				slide.style.left = left + 'px';
-				slide.style.top = top + 'px';
+				if( printing ) {
+					slide.style.left = left + 'px';
+					slide.style.top = top + 'px';
+				}
+
 				slide.style.width = slideWidth + 'px';
 
 				this.Reveal.slideContent.layout( slide );
@@ -213,6 +222,9 @@ export default class Print {
 
 		}, this );
 
+		// Remove leftover stacks
+		queryAll( pageContainer, '.reveal .stack' ).forEach( stack => stack.remove() );
+
 		await new Promise( requestAnimationFrame );
 
 		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
 		this.Reveal.slideContent.layout( this.Reveal.getSlidesElement() );
 
-		// Notify subscribers that the PDF layout is good to go
-		this.Reveal.dispatchEvent({ type: 'pdf-ready' });
+		if( printing ) {
+			// 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() {
+
+		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 ( /print-pdf/gi ).test( window.location.search );
+		return this.isPrintMode() || this.isReaderMode();
 
 	}
 

+ 29 - 12
js/reveal.js

@@ -11,7 +11,7 @@ import Controls from './controllers/controls.js'
 import Progress from './controllers/progress.js'
 import Pointer from './controllers/pointer.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 Focus from './controllers/focus.js'
 import Notes from './controllers/notes.js'
@@ -113,7 +113,7 @@ export default function( revealElement, options ) {
 		progress = new Progress( Reveal ),
 		pointer = new Pointer( Reveal ),
 		plugins = new Plugins( Reveal ),
-		print = new Print( Reveal ),
+		reader = new Reader( Reveal ),
 		focus = new Focus( Reveal ),
 		touch = new Touch( Reveal ),
 		notes = new Notes( Reveal );
@@ -225,18 +225,25 @@ export default function( revealElement, options ) {
 			});
 		}, 1 );
 
-		// Special setup and config is required when printing to PDF
-		if( print.isPrintingPDF() ) {
+		// Special setup and config is required when initializing a deck
+		// to be read or printed linearly
+		if( reader.isPrintMode() || reader.isReaderMode() ) {
+
 			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
 			// measurements to be accurate
 			if( document.readyState === 'complete' ) {
-				print.setupPDF();
+				reader.setup().then( () => layout() );
 			}
 			else {
 				window.addEventListener( 'load', () => {
-					print.setupPDF();
+					reader.setup().then( () => layout() );
 				} );
 			}
 		}
@@ -861,7 +868,7 @@ export default function( revealElement, options ) {
 	 */
 	function layout() {
 
-		if( dom.wrapper && !print.isPrintingPDF() ) {
+		if( dom.wrapper && !reader.isPrintMode() ) {
 
 			if( !config.disableLayout ) {
 
@@ -901,6 +908,15 @@ export default function( revealElement, options ) {
 					dom.slides.style.right = '';
 					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 {
 					dom.slides.style.zoom = '';
 					dom.slides.style.left = '50%';
@@ -921,7 +937,7 @@ export default function( revealElement, options ) {
 						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
 						// children will be
 						if( slide.classList.contains( 'stack' ) ) {
@@ -1597,7 +1613,7 @@ export default function( revealElement, options ) {
 		let slides = Util.queryAll( dom.wrapper, selector ),
 			slidesLength = slides.length;
 
-		let printMode = print.isPrintingPDF();
+		let printMode = reader.isActive();
 		let loopedForwards = false;
 		let loopedBackwards = false;
 
@@ -1757,7 +1773,7 @@ export default function( revealElement, options ) {
 			}
 
 			// All slides need to be visible when exporting to PDF
-			if( print.isPrintingPDF() ) {
+			if( reader.isPrintMode() || reader.isReaderMode() ) {
 				viewDistance = Number.MAX_VALUE;
 			}
 
@@ -2696,7 +2712,8 @@ export default function( revealElement, options ) {
 		isSpeakerNotes: notes.isSpeakerNotesWindow.bind( notes ),
 		isOverview: overview.isActive.bind( overview ),
 		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
 		isReady: () => ready,
@@ -2816,8 +2833,8 @@ export default function( revealElement, options ) {
 		getStatusText,
 
 		// Controllers
-		print,
 		focus,
+		reader,
 		progress,
 		controls,
 		location,