Skip to content

Commit

Permalink
reader mode supports scroll snapping, sticky pages with scroll trigge…
Browse files Browse the repository at this point in the history
…rs are always full height
  • Loading branch information
hakimel committed Sep 21, 2023
1 parent f0950ba commit e49e89a
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 48 deletions.
1 change: 1 addition & 0 deletions css/reveal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1934,6 +1934,7 @@ $notesWidthPercent: 25%;
top: 50% !important;
left: 50% !important;
opacity: 1 !important;
transform: scale(var(--slide-scale)) translate(-50%, -50%) !important;
transform-style: flat !important;
transform-origin: 0 0 !important;
}
Expand Down
2 changes: 1 addition & 1 deletion dist/reveal.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/reveal.esm.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/reveal.esm.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/reveal.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/reveal.js.map

Large diffs are not rendered by default.

17 changes: 13 additions & 4 deletions js/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,19 @@ export default {
// triggered animations
view: null,

// When the presentation is in reader mode, this controls whether each
// page should be as tall as the presentation viewport. Set this to false
// for a more compact layout with multiple slides visible at a time.
readerFullPageHeight: true,
// Adjusts the height of each slide in reader mode
// - full: Each slide is as tall as the viewport
// - compact: Slides are as small as possible, allowing multiple slides
// to be visible in parallel on tall devices
readerLayout: 'full',

// Control how scroll snapping works in reader mode.
// - false: No snapping, scrolling is continuous
// - proximity: Snap when close to a slide
// - mandatory: Always snap to the closest slide
//
// Only applies to presentations in reader mode.
readerScrollSnap: 'proximity',

// The maximum number of pages a single slide can expand onto when printing
// to PDF, unlimited by default
Expand Down
90 changes: 52 additions & 38 deletions js/controllers/reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,6 @@ export default class Reader {
page.className = 'reader-page';
pageElements.push( page );

slide.style.width = slideWidth + 'px';
// slide.style.height = slideHeight + 'px';

// Copy the presentation-wide background to each individual
// page when printing
if( presentationBackground ) {
Expand Down Expand Up @@ -143,21 +140,29 @@ export default class Reader {

}

generatePageMap() {
/**
* Updates our reader pages to match the latest configuration and
* presentation size.
*/
sync() {

const viewportElement = this.Reveal.getViewportElement();
const viewportHeight = viewportElement.offsetHeight;
const config = this.Reveal.getConfig();

const slideSize = this.Reveal.getComputedSlideSize( window.innerWidth, window.innerHeight );
const scale = this.Reveal.getScale();
const fullPageHeight = this.Reveal.getConfig().readerFullPageHeight;
const readerLayout = config.readerLayout;

const pageHeight = fullPageHeight === true ? viewportHeight : slideSize.height * scale;
const viewportElement = this.Reveal.getViewportElement();
const viewportHeight = viewportElement.offsetHeight;
const compactHeight = slideSize.height * scale;
const pageHeight = readerLayout === 'full' ? viewportHeight : compactHeight;

// The height that needs to be scrolled between scroll triggers
const scrollTriggerHeight = viewportHeight / 2;

viewportElement.style.setProperty( '--page-height', pageHeight + 'px' );
viewportElement.style.scrollSnapType = typeof config.readerScrollSnap === 'string' ?
`y ${config.readerScrollSnap}` : '';

const pageElements = Array.from( this.Reveal.getRevealElement().querySelectorAll( '.reader-page' ) );

Expand All @@ -168,36 +173,18 @@ export default class Reader {
slideElement: pageElement.querySelector( 'section' ),
backgroundElement: pageElement.querySelector( '.slide-background' ),
top: pageElement.offsetTop,
pageHeight: pageHeight,
scrollTriggers: []
};

page.slideElement.style.width = slideSize.width + 'px';
page.slideElement.style.height = config.center === true ? '' : slideSize.height + 'px';

// 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.
page.fragments = this.Reveal.fragments.sort( pageElement.querySelectorAll( '.fragment:not(.disabled)' ) );
page.fragmentGroups = this.Reveal.fragments.sort( pageElement.querySelectorAll( '.fragment' ), true );

// The amount of empty scrollable space that has been append
page.scrollPadding = scrollTriggerHeight * Math.max( page.fragmentGroups.length - 1, 0 );

// 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.scrollPadding > 0 ) {
page.stickyElement.style.position = 'sticky';
page.stickyElement.style.top = Math.max( ( viewportHeight - page.pageHeight ) / 2, 0 ) + 'px';
}
else {
page.stickyElement.style.position = 'relative';
}

// Create scroll triggers that show/hide fragments
if( page.fragmentGroups.length ) {
const segmentSize = 1 / ( page.fragmentGroups.length + 1 );
Expand All @@ -211,10 +198,45 @@ export default class Reader {
fragmentIndex: i
}))
);
}


// 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';

// Make this page freeze at the vertical center of the viewport
page.top -= ( viewportHeight - page.pageHeight ) / 2;
}
else {
page.stickyElement.style.position = 'relative';
}

return page;
} );
Expand All @@ -223,15 +245,7 @@ export default class Reader {

layout() {

this.generatePageMap();

const scale = this.Reveal.getScale();

this.pages.forEach( ( page ) => {
page.slideElement.style.transform = `scale(${scale}) translate(-50%, -50%)`;
} );


this.sync();
this.onScroll();

}
Expand Down
2 changes: 1 addition & 1 deletion js/reveal.js
Original file line number Diff line number Diff line change
Expand Up @@ -2744,7 +2744,7 @@ export default function( revealElement, options ) {
isFocused: focus.isFocused.bind( focus ),

isReaderMode: reader.isActive.bind( reader ),
isPrintMode: print.isActive.bind( print ),
isPrinting: print.isActive.bind( print ),

// Checks if reveal.js has been loaded and is ready for use
isReady: () => ready,
Expand Down

0 comments on commit e49e89a

Please sign in to comment.