You are reading a translation of an old blog post published on my previous blog in French.
No more boring slides with Reveal.js. How does this library based on HTML5 and CSS3 technologies challenge the existence of PowerPoint? We will find out by rewriting from scratch a minimal version.
Reveal.js is published under an OpenSource license. The code presented in this article has been simplified for obvious reasons and must not be used outside this learning context. This article is based on the latest version at the moment of publication.
A First Example
Here is the rendered result of our presentation:
Reveal.js is far more powerful than what this simple presentation suggests. The lists of features supported by Reveal is huge: a ton of animations, overview, fragments, mode speaker, PDF export, … We are not going to implement all of these features but instead focus on the navigation between slides (horizontal et vertical), the links, the keyboard shortcuts, and also a few animations that make reveal.js so attractive.
Slide by slide
The initialization of Reveal starts with the method Reveal.initialize()
. Here is the skeleton of our implementation defined in a file reveal.lite.js
:
- 1
- We predefine main CSS selectors to retrieve all slides or just the horizontal and vertical ones. These constants will be reused several times during the next steps.
- 2
- We define two variables
indexh
andindexv
to represent our current position inside the presentation, like a slide number. - 3
- We end by requesting the display of the first slide (i.e., the first horizontal and vertical slide). For the moment, everything remains to be implemented in this method.
Before going further, we need to define a basic CSS stylesheet; otherwise, the browser will display all slides at once.
The page now looks like this:
The parent tag with the CSS class .reveal
(called wrapper
in the code) is positioned to use all available space on the screen. This allows slides inside this wrapper to occupy the screen. Note that the relative position will be useful to position slides using absolute values when defining CSS animations.
All slides are thus superimposed on top of each other. The JavaScript code will use a few CSS classes (past
, present
, future
) to change the slides on screen. For example, to display the current slide:
The transition from one slide to a different one is done by the method slide
:
The code operates two translations, on the horizontal and vertical axis. The core logic resides in the method updateSlides
:
This is the first method with a lot of implementation details. The code logic is relatively simple. The first parameter is a CSS selector. In practice, this parameter is mainly used to indicate if we are sliding horizontally or vertically. The second parameter is the index on the chosen axis. The code traverses each slide on this axis to configure the right CSS classes. Note the presence of a class stack
assigned on slides of type parent (i.e., the slides having vertical slides inside).
The value returned by this method updateSlides
is the new index used to adjust the previous variables indexh
and indexv
in the method slide
.
element.classList
Supported by modern browsers, the property classList
defined in the ’object Element
offers the same convenience as the jQuery API. We no longer need to parse the attribute className
to add or remove CSS classes, as the interface DOMTokenList
defines methods like add
, remove
, toggle
, …
Automatic Resizing
You have probably noticed on the Reveal.js demo, the size of the presentation (i.e., the slides) automatically adjusts when you resize your browser window. With our current implementation, the slides occupy the full screen, but their content does not scale in consequence:
How can the content of the slides be adapted to the screen size? How to reduce/enlarge the font size, images, and videos shown? The solution is elegant, using CSS animations like the function scale()
. The calculations are grouped inside the method layout
:
- 1
- We compare the default size for a slide (960x700) with the effective screen size. We obtain the ratio to apply to scale the slide to match the full screen.
Let’s modify the method slide
to use this new method:
The result is immediately more satisfactory. The slides resize to match the window size, except if you are trying to resize the browser window. This is easy to fix by reusing the method layout
and listening for this event:
Keyboard Navigation
For now, only the first slide is displayed. Using the directional keys, we will allow the user to change the current slide. We start by listening for events of type keydown
. We also take the opportunity to refactor the method initialize
:
The handler uses standard codes to determine the direction to follow in the presentation:
- 1
- We use the two variables
indexh
andindexv
to determine our current position, before calling the methodslide
to move in the right direction.
The navigation is now operational but the code does not block the user from moving beyond the last slide. Using CSS selectors, we will determine the maximum number of slides and compare with our current position to determine if the move is possible:
Animations
Reveal.js would not be the same without animations. Under the hood, those animations use CSS animations. Using the already defined CSS classes, only a few lines of CSS is necessary to animate the slides.
Let’s start with the most simple effect: fade
.
The fade effect
(Demo)
As a reminder, here are the CSS declarations that will be enriched:
The fade
effect consists in defining a transition for the property opacity
:
Every time the user changes the current slide, the previous one disappears in half a second while the new slide appears simultaneously. Easy? Let’s try to implement the slide
effect.
The slide effect
(Demo)
When using this effect, the previous slide disappears on the left while the next one appears on the right of the screen. For vertical slides, the principle is the same, except we are using the vertical axis.
Here are the CSS declarations to support this effect:
- 1
- We configure the animation to start slowly.
- 2
- We rely on the function
translate
. Using a large percent, we are sure the slide will completely exit the screen.
An immersion in 3D to finish? Let’s finish with the concave
effect.
The concave effect
(Demo)
This effect is the 3D equivalent of the previous slide
.
The CSS declarations is slightly more advanced but only a few lines are required to support this effect:
cubier-bezier
The CSS property transition
supports what is called a timing function (or easing function). Several functions are predefined (linear
, ease-in
, …). Using the function cubic-bezier
, we can define new custom functions using, as its name suggests, a Bezier curve, well-known to users of Adobe Illustrator. Bezier curves are not adapted in every context but they are very flexible and easy to use.
The website cubic-bezier.com allows you to create visually your curve and generates the corresponding CSS code. This site is the work of Lea Verou, to whom we also owe the projects -prefix-free and prism.
Links
Before closing this article, let’s look at the links between slides.
The solution relies on the URL fragment (the optional hash value following the character #
in an URL) to identify the slide to display. For example #/1/2
represents the second vertical slide below the first horizontal slide. When clicking on a link, the code modifies the hash like this:
In JavaScript, we need to listen for changes using the event hashchange
, and extract the value to delegates to the method slide
to move to the destination:
Using the hash has many advantages. It also allows the user to bookmark a given slide. This requires a slight modification to read the hash during initialization:
- 1
- The method
slide
is wrapped inside a new methodreadURL
. If no hash is present, we simply display the first slide as before.
Congratulations, you have written a minimal and operational version of Reveal.js in less than 200 lines of JavaScript code, with just 100 lines of CSS code. The example presentation is available here, just like the complete source code.
To Go Further
AMD Support & Node
Reveal.js, like many existing libraries, started by exposing a single global variable (Reveal
). With the introduction of AMD modules and following the success of Node.js, Reveal needs to support these new use cases. The solution is not new and has been well documented through the UMD Pattern (Universal Module Definition).
The UMD pattern makes possible to interoperate with existing loaders. As often in JavaScript, we inspect the objects present in the global namespace to detect the environment. Here is a preview of the code:
- 1
- Functions are the only possible scope in JavaScript. This is why we use an immediate function to not pollute the global namespace uselessly.
- 2
- We test for a method
define
having a propertyamd
to determine if RequireJS is available. Reveal.js declares itself as an anonymous module without any dependency. - 3
- Like for AMD, we test for an object
exports
. Reveals declares itself like any other Node module. - 4
- As before, our module is defined as a global variable, or more precisely a property of the object
window
.
- Only a few hundred of lines of code are enough to make PowerPoint a remembering of the past. (PowerPoint totals millions of lines de code.)
- CSS animations make accessible 3D effects.
- The URL hash is the favorite solution to preserve the navigation in a Single-Page Application (SPA).
Reveal.js is a complete solution. Just look at the speaker mode to convince you. We have omit a lot of features in this article. Why not adventure in the original source code to discover how these advanced features are implemented. For example:
- Reveal.js supports the navigation between slides using also buttons and gesture movements. As the code is modular, adding these controls is very easy. Note: The complete code source of this article supports the mouse.
- The integrality of every slide content is displayed immediately with our implementation. Using fragments, Reveal.js supports the display of a slide content step by step. How does it work?
- Presentations created with Reveal.js can be exported in PDF. This requires a few calculations in JavaScript and a few CSS declarations. Check the method
setupPDF
. - The overview mode displays a global picture of all your slides. This feature is implemented by the method
activateOverview
. Hint: The implementation also relies on CSS animations to create 3D effects.