overview.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import { SLIDES_SELECTOR } from '../utils/constants.js'
  2. import { extend, toArray, transformElement } from '../utils/util.js'
  3. /**
  4. * Handles all logic related to the overview mode
  5. * (birds-eye view of all slides).
  6. */
  7. export default class Overview {
  8. constructor( Reveal ) {
  9. this.Reveal = Reveal;
  10. this.active = false;
  11. this.onSlideClicked = this.onSlideClicked.bind( this );
  12. }
  13. /**
  14. * Displays the overview of slides (quick nav) by scaling
  15. * down and arranging all slide elements.
  16. */
  17. activate() {
  18. // Only proceed if enabled in config
  19. if( this.Reveal.getConfig().overview && !this.isActive() ) {
  20. this.active = true;
  21. this.Reveal.getRevealElement().classList.add( 'overview' );
  22. // Don't auto-slide while in overview mode
  23. this.Reveal.cancelAutoSlide();
  24. // Move the backgrounds element into the slide container to
  25. // that the same scaling is applied
  26. this.Reveal.getSlidesElement().appendChild( this.Reveal.getBackgroundsElement() );
  27. // Clicking on an overview slide navigates to it
  28. toArray( this.Reveal.getRevealElement().querySelectorAll( SLIDES_SELECTOR ) ).forEach( slide => {
  29. if( !slide.classList.contains( 'stack' ) ) {
  30. slide.addEventListener( 'click', this.onSlideClicked, true );
  31. }
  32. } );
  33. // Calculate slide sizes
  34. const margin = 70;
  35. const slideSize = this.Reveal.getComputedSlideSize();
  36. this.overviewSlideWidth = slideSize.width + margin;
  37. this.overviewSlideHeight = slideSize.height + margin;
  38. // Reverse in RTL mode
  39. if( this.Reveal.getConfig().rtl ) {
  40. this.overviewSlideWidth = -this.overviewSlideWidth;
  41. }
  42. this.Reveal.updateSlidesVisibility();
  43. this.layout();
  44. this.update();
  45. this.Reveal.layout();
  46. const indices = this.Reveal.getIndices();
  47. // Notify observers of the overview showing
  48. this.Reveal.dispatchEvent( 'overviewshown', {
  49. 'indexh': indices.h,
  50. 'indexv': indices.v,
  51. 'currentSlide': this.Reveal.getCurrentSlide()
  52. } );
  53. }
  54. }
  55. /**
  56. * Uses CSS transforms to position all slides in a grid for
  57. * display inside of the overview mode.
  58. */
  59. layout() {
  60. // Layout slides
  61. this.Reveal.getHorizontalSlides().forEach( ( hslide, h ) => {
  62. hslide.setAttribute( 'data-index-h', h );
  63. transformElement( hslide, 'translate3d(' + ( h * this.overviewSlideWidth ) + 'px, 0, 0)' );
  64. if( hslide.classList.contains( 'stack' ) ) {
  65. toArray( hslide.querySelectorAll( 'section' ) ).forEach( ( vslide, v ) => {
  66. vslide.setAttribute( 'data-index-h', h );
  67. vslide.setAttribute( 'data-index-v', v );
  68. transformElement( vslide, 'translate3d(0, ' + ( v * this.overviewSlideHeight ) + 'px, 0)' );
  69. } );
  70. }
  71. } );
  72. // Layout slide backgrounds
  73. toArray( this.Reveal.getBackgroundsElement().childNodes ).forEach( ( hbackground, h ) => {
  74. transformElement( hbackground, 'translate3d(' + ( h * this.overviewSlideWidth ) + 'px, 0, 0)' );
  75. toArray( hbackground.querySelectorAll( '.slide-background' ) ).forEach( ( vbackground, v ) => {
  76. transformElement( vbackground, 'translate3d(0, ' + ( v * this.overviewSlideHeight ) + 'px, 0)' );
  77. } );
  78. } );
  79. }
  80. /**
  81. * Moves the overview viewport to the current slides.
  82. * Called each time the current slide changes.
  83. */
  84. update() {
  85. const vmin = Math.min( window.innerWidth, window.innerHeight );
  86. const scale = Math.max( vmin / 5, 150 ) / vmin;
  87. const indices = this.Reveal.getIndices();
  88. this.Reveal.transformSlides( {
  89. overview: [
  90. 'scale('+ scale +')',
  91. 'translateX('+ ( -indices.h * this.overviewSlideWidth ) +'px)',
  92. 'translateY('+ ( -indices.v * this.overviewSlideHeight ) +'px)'
  93. ].join( ' ' )
  94. } );
  95. }
  96. /**
  97. * Exits the slide overview and enters the currently
  98. * active slide.
  99. */
  100. deactivate() {
  101. // Only proceed if enabled in config
  102. if( this.Reveal.getConfig().overview ) {
  103. this.active = false;
  104. this.Reveal.getRevealElement().classList.remove( 'overview' );
  105. // Temporarily add a class so that transitions can do different things
  106. // depending on whether they are exiting/entering overview, or just
  107. // moving from slide to slide
  108. this.Reveal.getRevealElement().classList.add( 'overview-deactivating' );
  109. setTimeout( () => {
  110. this.Reveal.getRevealElement().classList.remove( 'overview-deactivating' );
  111. }, 1 );
  112. // Move the background element back out
  113. this.Reveal.getRevealElement().appendChild( this.Reveal.getBackgroundsElement() );
  114. // Clean up changes made to slides
  115. toArray( this.Reveal.getRevealElement().querySelectorAll( SLIDES_SELECTOR ) ).forEach( slide => {
  116. transformElement( slide, '' );
  117. slide.removeEventListener( 'click', this.onSlideClicked, true );
  118. } );
  119. // Clean up changes made to backgrounds
  120. toArray( this.Reveal.getBackgroundsElement().querySelectorAll( '.slide-background' ) ).forEach( background => {
  121. transformElement( background, '' );
  122. } );
  123. this.Reveal.transformSlides( { overview: '' } );
  124. const indices = this.Reveal.getIndices();
  125. this.Reveal.slide( indices.h, indices.v );
  126. this.Reveal.layout();
  127. this.Reveal.cueAutoSlide();
  128. // Notify observers of the overview hiding
  129. this.Reveal.dispatchEvent( 'overviewhidden', {
  130. 'indexh': indices.h,
  131. 'indexv': indices.v,
  132. 'currentSlide': this.Reveal.getCurrentSlide()
  133. } );
  134. }
  135. }
  136. /**
  137. * Toggles the slide overview mode on and off.
  138. *
  139. * @param {Boolean} [override] Flag which overrides the
  140. * toggle logic and forcibly sets the desired state. True means
  141. * overview is open, false means it's closed.
  142. */
  143. toggle( override ) {
  144. if( typeof override === 'boolean' ) {
  145. override ? this.activate() : this.deactivate();
  146. }
  147. else {
  148. this.isActive() ? this.deactivate() : this.activate();
  149. }
  150. }
  151. /**
  152. * Checks if the overview is currently active.
  153. *
  154. * @return {Boolean} true if the overview is active,
  155. * false otherwise
  156. */
  157. isActive() {
  158. return this.active;
  159. }
  160. /**
  161. * Invoked when a slide is and we're in the overview.
  162. *
  163. * @param {object} event
  164. */
  165. onSlideClicked( event ) {
  166. if( this.isActive() ) {
  167. event.preventDefault();
  168. let element = event.target;
  169. while( element && !element.nodeName.match( /section/gi ) ) {
  170. element = element.parentNode;
  171. }
  172. if( element && !element.classList.contains( 'disabled' ) ) {
  173. this.deactivate();
  174. if( element.nodeName.match( /section/gi ) ) {
  175. let h = parseInt( element.getAttribute( 'data-index-h' ), 10 ),
  176. v = parseInt( element.getAttribute( 'data-index-v' ), 10 );
  177. this.Reveal.slide( h, v );
  178. }
  179. }
  180. }
  181. }
  182. }