From f0b99f81ac7bd6238c7548dd6f778acc77dc4020 Mon Sep 17 00:00:00 2001 From: Kurt Zenisek Date: Sat, 21 Oct 2017 13:37:48 +0300 Subject: [PATCH] Add Mobile plugin, which allows authors to hide most slides with CSS. The Mobile plugin adds CSS classes body.impress-mobile and div.prev, div.next. These can be used in CSS to hide non-active steps completely, in order to reduce memory consumption on small mobile devices. --- build.js | 1 + js/impress.js | 90 ++++++++++++++++++++++++++++++++++++ src/plugins/mobile/README.md | 38 +++++++++++++++ src/plugins/mobile/mobile.js | 89 +++++++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 src/plugins/mobile/README.md create mode 100644 src/plugins/mobile/mobile.js diff --git a/build.js b/build.js index 5f5a40e..79886a6 100644 --- a/build.js +++ b/build.js @@ -7,6 +7,7 @@ buildify() .concat(['src/lib/util.js']) // Plugins from src/plugins .concat(['src/plugins/goto/goto.js', + 'src/plugins/mobile/mobile.js', 'src/plugins/navigation/navigation.js', 'src/plugins/rel/rel.js', 'src/plugins/resize/resize.js', diff --git a/js/impress.js b/js/impress.js index af98f1d..0a03f72 100644 --- a/js/impress.js +++ b/js/impress.js @@ -1384,6 +1384,96 @@ } )( document, window ); +/** + * Mobile devices support + * + * Allow presentation creators to hide all but 3 slides, to save resources, particularly on mobile + * devices, using classes body.impress-mobile, .step.prev, .step.active and .step.next. + * + * Note: This plugin does not take into account possible redirections done with skip, goto etc + * plugins. Basically it wouldn't work as intended in such cases, but the active step will at least + * be correct. + * + * Adapted to a plugin from a submission by @Kzeni: + * https://github.com/impress/impress.js/issues/333 + */ +/* global document, navigator */ +( function( document ) { + "use strict"; + + var getNextStep = function( el ) { + var steps = document.querySelectorAll( ".step" ); + for ( var i = 0; i < steps.length; i++ ) { + if ( steps[ i ] === el ) { + if ( i + 1 < steps.length ) { + return steps[ i + 1 ]; + } else { + return steps[ 0 ]; + } + } + } + }; + var getPrevStep = function( el ) { + var steps = document.querySelectorAll( ".step" ); + for ( var i = steps.length - 1; i >= 0; i-- ) { + if ( steps[ i ] === el ) { + if ( i - 1 >= 0 ) { + return steps[ i - 1 ]; + } else { + return steps[ steps.length - 1 ]; + } + } + } + }; + + // Detect mobile browsers & add CSS class as appropriate. + document.addEventListener( "impress:init", function( event ) { + var body = document.body; + if ( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( + navigator.userAgent + ) ) { + body.classList.add( "impress-mobile" ); + } + + // Unset all this on teardown + var api = event.detail.api; + api.lib.gc.addCallback( function() { + document.body.classList.remove( "impress-mobile" ); + var prev = document.getElementsByClassName( "prev" )[ 0 ]; + var next = document.getElementsByClassName( "next" )[ 0 ]; + if ( typeof prev !== "undefined" ) { + prev.classList.remove( "prev" ); + } + if ( typeof next !== "undefined" ) { + next.classList.remove( "next" ); + } + } ); + } ); + + // Add prev and next classes to the siblings of the newly entered active step element + // Remove prev and next classes from their current step elements + // Note: As an exception we break namespacing rules, as these are useful general purpose + // classes. (Naming rules would require us to use css classes mobile-next and mobile-prev, + // based on plugin name.) + document.addEventListener( "impress:stepenter", function( event ) { + var oldprev = document.getElementsByClassName( "prev" )[ 0 ]; + var oldnext = document.getElementsByClassName( "next" )[ 0 ]; + + var prev = getPrevStep( event.target ); + prev.classList.add( "prev" ); + var next = getNextStep( event.target ); + next.classList.add( "next" ); + + if ( typeof oldprev !== "undefined" ) { + oldprev.classList.remove( "prev" ); + } + if ( typeof oldnext !== "undefined" ) { + oldnext.classList.remove( "next" ); + } + } ); +} )( document ); + + /** * Navigation events plugin * diff --git a/src/plugins/mobile/README.md b/src/plugins/mobile/README.md new file mode 100644 index 0000000..6db752a --- /dev/null +++ b/src/plugins/mobile/README.md @@ -0,0 +1,38 @@ +Mobile devices support +====================== + +Presentations with a lot of 3D effects and graphics can consume a lot of resources, especially on mobile devices. +This plugin provides some CSS classes that can be used to hide most of the slides, only showing the current, previous +and next slide. + +In particular, this plugin adds: + +`body.impress-mobile` class, if it detects running on a mobile OS. + +`div.prev` and `div.prev` to the adjacent steps to the current one. Note that the current slide is already identified +by `present` and `active` CSS classes. + +Example CSS +----------- + + body.impress-mobile .step { + display:none; + } + body.impress-mobile .step.active, + body.impress-mobile .step.present, + body.impress-mobile .step.next, + body.impress-mobile .step.prev { + display:block; + } + +Note +---- + +This plugin does not take into account redirects that could happen with skip, goto and other plugins. The active +step will of course always be correct, but "non-linear" transitions to anything else than the actual previous and next +steps will probably not look correct. + +Author +------ + +Kurt Zenisek (@KZeni) diff --git a/src/plugins/mobile/mobile.js b/src/plugins/mobile/mobile.js new file mode 100644 index 0000000..91c6261 --- /dev/null +++ b/src/plugins/mobile/mobile.js @@ -0,0 +1,89 @@ +/** + * Mobile devices support + * + * Allow presentation creators to hide all but 3 slides, to save resources, particularly on mobile + * devices, using classes body.impress-mobile, .step.prev, .step.active and .step.next. + * + * Note: This plugin does not take into account possible redirections done with skip, goto etc + * plugins. Basically it wouldn't work as intended in such cases, but the active step will at least + * be correct. + * + * Adapted to a plugin from a submission by @Kzeni: + * https://github.com/impress/impress.js/issues/333 + */ +/* global document, navigator */ +( function( document ) { + "use strict"; + + var getNextStep = function( el ) { + var steps = document.querySelectorAll( ".step" ); + for ( var i = 0; i < steps.length; i++ ) { + if ( steps[ i ] === el ) { + if ( i + 1 < steps.length ) { + return steps[ i + 1 ]; + } else { + return steps[ 0 ]; + } + } + } + }; + var getPrevStep = function( el ) { + var steps = document.querySelectorAll( ".step" ); + for ( var i = steps.length - 1; i >= 0; i-- ) { + if ( steps[ i ] === el ) { + if ( i - 1 >= 0 ) { + return steps[ i - 1 ]; + } else { + return steps[ steps.length - 1 ]; + } + } + } + }; + + // Detect mobile browsers & add CSS class as appropriate. + document.addEventListener( "impress:init", function( event ) { + var body = document.body; + if ( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( + navigator.userAgent + ) ) { + body.classList.add( "impress-mobile" ); + } + + // Unset all this on teardown + var api = event.detail.api; + api.lib.gc.addCallback( function() { + document.body.classList.remove( "impress-mobile" ); + var prev = document.getElementsByClassName( "prev" )[ 0 ]; + var next = document.getElementsByClassName( "next" )[ 0 ]; + if ( typeof prev !== "undefined" ) { + prev.classList.remove( "prev" ); + } + if ( typeof next !== "undefined" ) { + next.classList.remove( "next" ); + } + } ); + } ); + + // Add prev and next classes to the siblings of the newly entered active step element + // Remove prev and next classes from their current step elements + // Note: As an exception we break namespacing rules, as these are useful general purpose + // classes. (Naming rules would require us to use css classes mobile-next and mobile-prev, + // based on plugin name.) + document.addEventListener( "impress:stepenter", function( event ) { + var oldprev = document.getElementsByClassName( "prev" )[ 0 ]; + var oldnext = document.getElementsByClassName( "next" )[ 0 ]; + + var prev = getPrevStep( event.target ); + prev.classList.add( "prev" ); + var next = getNextStep( event.target ); + next.classList.add( "next" ); + + if ( typeof oldprev !== "undefined" ) { + oldprev.classList.remove( "prev" ); + } + if ( typeof oldnext !== "undefined" ) { + oldnext.classList.remove( "next" ); + } + } ); +} )( document ); +