You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

255 line
6.4 KiB

  1. import { SLIDES_SELECTOR } from '../utils/constants.js'
  2. import { extend, queryAll, 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. queryAll( this.Reveal.getRevealElement(), 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({
  49. type: 'overviewshown',
  50. data: {
  51. 'indexh': indices.h,
  52. 'indexv': indices.v,
  53. 'currentSlide': this.Reveal.getCurrentSlide()
  54. }
  55. });
  56. }
  57. }
  58. /**
  59. * Uses CSS transforms to position all slides in a grid for
  60. * display inside of the overview mode.
  61. */
  62. layout() {
  63. // Layout slides
  64. this.Reveal.getHorizontalSlides().forEach( ( hslide, h ) => {
  65. hslide.setAttribute( 'data-index-h', h );
  66. transformElement( hslide, 'translate3d(' + ( h * this.overviewSlideWidth ) + 'px, 0, 0)' );
  67. if( hslide.classList.contains( 'stack' ) ) {
  68. queryAll( hslide, 'section' ).forEach( ( vslide, v ) => {
  69. vslide.setAttribute( 'data-index-h', h );
  70. vslide.setAttribute( 'data-index-v', v );
  71. transformElement( vslide, 'translate3d(0, ' + ( v * this.overviewSlideHeight ) + 'px, 0)' );
  72. } );
  73. }
  74. } );
  75. // Layout slide backgrounds
  76. Array.from( this.Reveal.getBackgroundsElement().childNodes ).forEach( ( hbackground, h ) => {
  77. transformElement( hbackground, 'translate3d(' + ( h * this.overviewSlideWidth ) + 'px, 0, 0)' );
  78. queryAll( hbackground, '.slide-background' ).forEach( ( vbackground, v ) => {
  79. transformElement( vbackground, 'translate3d(0, ' + ( v * this.overviewSlideHeight ) + 'px, 0)' );
  80. } );
  81. } );
  82. }
  83. /**
  84. * Moves the overview viewport to the current slides.
  85. * Called each time the current slide changes.
  86. */
  87. update() {
  88. const vmin = Math.min( window.innerWidth, window.innerHeight );
  89. const scale = Math.max( vmin / 5, 150 ) / vmin;
  90. const indices = this.Reveal.getIndices();
  91. this.Reveal.transformSlides( {
  92. overview: [
  93. 'scale('+ scale +')',
  94. 'translateX('+ ( -indices.h * this.overviewSlideWidth ) +'px)',
  95. 'translateY('+ ( -indices.v * this.overviewSlideHeight ) +'px)'
  96. ].join( ' ' )
  97. } );
  98. }
  99. /**
  100. * Exits the slide overview and enters the currently
  101. * active slide.
  102. */
  103. deactivate() {
  104. // Only proceed if enabled in config
  105. if( this.Reveal.getConfig().overview ) {
  106. this.active = false;
  107. this.Reveal.getRevealElement().classList.remove( 'overview' );
  108. // Temporarily add a class so that transitions can do different things
  109. // depending on whether they are exiting/entering overview, or just
  110. // moving from slide to slide
  111. this.Reveal.getRevealElement().classList.add( 'overview-deactivating' );
  112. setTimeout( () => {
  113. this.Reveal.getRevealElement().classList.remove( 'overview-deactivating' );
  114. }, 1 );
  115. // Move the background element back out
  116. this.Reveal.getRevealElement().appendChild( this.Reveal.getBackgroundsElement() );
  117. // Clean up changes made to slides
  118. queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR ).forEach( slide => {
  119. transformElement( slide, '' );
  120. slide.removeEventListener( 'click', this.onSlideClicked, true );
  121. } );
  122. // Clean up changes made to backgrounds
  123. queryAll( this.Reveal.getBackgroundsElement(), '.slide-background' ).forEach( background => {
  124. transformElement( background, '' );
  125. } );
  126. this.Reveal.transformSlides( { overview: '' } );
  127. const indices = this.Reveal.getIndices();
  128. this.Reveal.slide( indices.h, indices.v );
  129. this.Reveal.layout();
  130. this.Reveal.cueAutoSlide();
  131. // Notify observers of the overview hiding
  132. this.Reveal.dispatchEvent({
  133. type: 'overviewhidden',
  134. data: {
  135. 'indexh': indices.h,
  136. 'indexv': indices.v,
  137. 'currentSlide': this.Reveal.getCurrentSlide()
  138. }
  139. });
  140. }
  141. }
  142. /**
  143. * Toggles the slide overview mode on and off.
  144. *
  145. * @param {Boolean} [override] Flag which overrides the
  146. * toggle logic and forcibly sets the desired state. True means
  147. * overview is open, false means it's closed.
  148. */
  149. toggle( override ) {
  150. if( typeof override === 'boolean' ) {
  151. override ? this.activate() : this.deactivate();
  152. }
  153. else {
  154. this.isActive() ? this.deactivate() : this.activate();
  155. }
  156. }
  157. /**
  158. * Checks if the overview is currently active.
  159. *
  160. * @return {Boolean} true if the overview is active,
  161. * false otherwise
  162. */
  163. isActive() {
  164. return this.active;
  165. }
  166. /**
  167. * Invoked when a slide is and we're in the overview.
  168. *
  169. * @param {object} event
  170. */
  171. onSlideClicked( event ) {
  172. if( this.isActive() ) {
  173. event.preventDefault();
  174. let element = event.target;
  175. while( element && !element.nodeName.match( /section/gi ) ) {
  176. element = element.parentNode;
  177. }
  178. if( element && !element.classList.contains( 'disabled' ) ) {
  179. this.deactivate();
  180. if( element.nodeName.match( /section/gi ) ) {
  181. let h = parseInt( element.getAttribute( 'data-index-h' ), 10 ),
  182. v = parseInt( element.getAttribute( 'data-index-v' ), 10 );
  183. this.Reveal.slide( h, v );
  184. }
  185. }
  186. }
  187. }
  188. }