diff options
author | Bartek Szopka <bartek.szopka+github@gmail.com> | 2012-02-15 21:20:02 +0100 |
---|---|---|
committer | Bartek Szopka <bartek.szopka+github@gmail.com> | 2012-02-15 21:20:02 +0100 |
commit | 224c75b4636d26e02c005ea0c0b0cf17659a480a (patch) | |
tree | 9cd2650bd712c6aee6d494eef87ebde8f84b2c65 | |
parent | a62f419cb9f9e7eb30a735002c98390dafac595f (diff) | |
parent | 14a86ef9efcd234e2fd11704130254df07730db6 (diff) | |
download | impress.js-224c75b4636d26e02c005ea0c0b0cf17659a480a.tar.gz |
impressive api merged
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | index.html | 37 | ||||
-rw-r--r-- | js/impress.js | 422 |
3 files changed, 283 insertions, 181 deletions
@@ -24,7 +24,12 @@ VERSION HISTORY **CONTAINS UNRELEASED CHANGES, MAY BE UNSTABLE** +<<<<<<< HEAD * minor CSS 3D fixes +======= +* basic API to control the presentation flow from JavaScript +* touch event support +>>>>>>> api ### 0.2 ([browse](http://github.com/bartaz/impress.js/tree/0.2), [zip](http://github.com/bartaz/impress.js/zipball/0.2), [tar](http://github.com/bartaz/impress.js/tarball/0.2)) @@ -280,12 +280,40 @@ Last, but not least. To make all described above really work, you need to include impress.js in the page. + I strongly encourage to minify it first. + + In here I just include full source of the script to make it more readable. + + You also need to call a `impress()` function to initialize impress.js presentation. And you should do it in the end of your document. Not only because it's a good practice, but also - because I was lazy, haven't wrapped the code in any kind of "DOM ready" event, so it will not work - if included too early in the source ;) + because I should be done when the whole document is ready. + Of course you can wrap it in any kind of "DOM ready" event, but I was to lazy to do so ;) --> <script src="js/impress.js"></script> +<script>impress();</script> + +<!-- + + The `impress()` function also gives you access to API to control the presentation. + + Just store the result of the call: + + var api = impress(); + + and you will get three functions you can call: + + `api.next()` - moves to next step of the presentation, + `api.prev()` - moves to previous step of the presentation + `api.goto( stepElement ) - moves the presentation to given step element (the DOM element of the step). + + You can also simply call `impress()` again to get the API, so `impress().next()` is also allowed. + Don't worry, it wont initialize the presentation again. + + For some example uses of this API check the last part of the source of impress.js where the API + is used in event handlers. + +--> </body> </html> @@ -328,5 +356,10 @@ I'm impressed! Feel free to let me know that you got that far (I'm @bartaz on Twitter), 'cause I'd like to congratulate you personally :) + But you don't have to do it now. Take my advice and take some time off. Make yourself a cup of coffee, tea, + or anything you like to drink. And raise a glass for me ;) + + Cheers! + --> diff --git a/js/impress.js b/js/impress.js index c96f91b..a6be86b 100644 --- a/js/impress.js +++ b/js/impress.js @@ -86,7 +86,13 @@ var scale = function ( s ) { return " scale(" + s + ") "; - } + }; + + var getElementFromUrl = function () { + // get id from url # by removing `#` or `#/` from the beginning, + // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work + return byId( window.location.hash.replace(/^#\/?/,"") ); + }; // CHECK SUPPORT @@ -94,216 +100,255 @@ var impressSupported = ( pfx("perspective") != null ) && ( ua.search(/(iphone)|(ipod)|(ipad)|(android)/) == -1 ); - // DOM ELEMENTS - - var impress = byId("impress"); - - if (!impressSupported) { - impress.className = "impress-not-supported"; - return; - } else { - impress.className = ""; - } - - var canvas = document.createElement("div"); - canvas.className = "canvas"; - - arrayify( impress.childNodes ).forEach(function ( el ) { - canvas.appendChild( el ); - }); - impress.appendChild(canvas); - - var steps = $$(".step", impress); - - // SETUP - // set initial values and defaults + var roots = {}; - document.documentElement.style.height = "100%"; - - css(document.body, { - height: "100%", - overflow: "hidden" - }); + var impress = window.impress = function ( rootId ) { - var props = { - position: "absolute", - transformOrigin: "top left", - transition: "all 0s ease-in-out", - transformStyle: "preserve-3d" - } - - css(impress, props); - css(impress, { - top: "50%", - left: "50%", - perspective: "1000px" - }); - css(canvas, props); - - var current = { - translate: { x: 0, y: 0, z: 0 }, - rotate: { x: 0, y: 0, z: 0 }, - scale: 1 - }; - - steps.forEach(function ( el, idx ) { - var data = el.dataset, - step = { - translate: { - x: data.x || 0, - y: data.y || 0, - z: data.z || 0 - }, - rotate: { - x: data.rotateX || 0, - y: data.rotateY || 0, - z: data.rotateZ || data.rotate || 0 - }, - scale: data.scale || 1 - }; + rootId = rootId || "impress"; + + // if already initialized just return the API + if (roots["impress-root-" + rootId]) { + return roots["impress-root-" + rootId]; + } + + // DOM ELEMENTS - el.stepData = step; + var root = byId( rootId ); - if ( !el.id ) { - el.id = "step-" + (idx + 1); + if (!impressSupported) { + root.className = "impress-not-supported"; + return; + } else { + root.className = ""; } - css(el, { - position: "absolute", - transform: "translate(-50%,-50%)" + - translate(step.translate) + - rotate(step.rotate) + - scale(step.scale), - transformStyle: "preserve-3d" + var canvas = document.createElement("div"); + canvas.className = "canvas"; + + arrayify( root.childNodes ).forEach(function ( el ) { + canvas.appendChild( el ); }); + root.appendChild(canvas); - }); - - // making given step active - - var active = null; - var hashTimeout = null; - - var select = function ( el ) { - if ( !el || !el.stepData || el == active) { - // selected element is not defined as step or is already active - return false; - } + var steps = $$(".step", root); - // Sometimes it's possible to trigger focus on first link with some keyboard action. - // Browser in such a case tries to scroll the page to make this element visible - // (even that body overflow is set to hidden) and it breaks our careful positioning. - // - // So, as a lousy (and lazy) workaround we will make the page scroll back to the top - // whenever slide is selected - // - // If you are reading this and know any better way to handle it, I'll be glad to hear about it! - window.scrollTo(0, 0); + // SETUP + // set initial values and defaults - var step = el.stepData; + document.documentElement.style.height = "100%"; - if ( active ) { - active.classList.remove("active"); + css(document.body, { + height: "100%", + overflow: "hidden" + }); + + var props = { + position: "absolute", + transformOrigin: "top left", + transition: "all 0s ease-in-out", + transformStyle: "preserve-3d" } - el.classList.add("active"); - - impress.className = "step-" + el.id; - // `#/step-id` is used instead of `#step-id` to prevent default browser - // scrolling to element in hash - // - // and it has to be set after animation finishes, because in chrome it - // causes transtion being laggy - window.clearTimeout( hashTimeout ); - hashTimeout = window.setTimeout(function () { - window.location.hash = "#/" + el.id; - }, 1000); + css(root, props); + css(root, { + top: "50%", + left: "50%", + perspective: "1000px" + }); + css(canvas, props); - var target = { - rotate: { - x: -parseInt(step.rotate.x, 10), - y: -parseInt(step.rotate.y, 10), - z: -parseInt(step.rotate.z, 10) - }, - translate: { - x: -step.translate.x, - y: -step.translate.y, - z: -step.translate.z - }, - scale: 1 / parseFloat(step.scale) + var current = { + translate: { x: 0, y: 0, z: 0 }, + rotate: { x: 0, y: 0, z: 0 }, + scale: 1 }; + + var stepData = {}; - // check if the transition is zooming in or not - var zoomin = target.scale >= current.scale; - - // if presentation starts (nothing is active yet) - // don't animate (set duration to 0) - var duration = (active) ? "1s" : "0"; + var isStep = function ( el ) { + return !!(el && el.id && stepData["impress-" + el.id]); + } - css(impress, { - // to keep the perspective look similar for different scales - // we need to 'scale' the perspective, too - perspective: step.scale * 1000 + "px", - transform: scale(target.scale), - transitionDuration: duration, - transitionDelay: (zoomin ? "500ms" : "0ms") + steps.forEach(function ( el, idx ) { + var data = el.dataset, + step = { + translate: { + x: data.x || 0, + y: data.y || 0, + z: data.z || 0 + }, + rotate: { + x: data.rotateX || 0, + y: data.rotateY || 0, + z: data.rotateZ || data.rotate || 0 + }, + scale: data.scale || 1, + el: el + }; + + if ( !el.id ) { + el.id = "step-" + (idx + 1); + } + + stepData["impress-" + el.id] = step; + + css(el, { + position: "absolute", + transform: "translate(-50%,-50%)" + + translate(step.translate) + + rotate(step.rotate) + + scale(step.scale), + transformStyle: "preserve-3d" + }); + }); + + // making given step active + + var active = null; + var hashTimeout = null; - css(canvas, { - transform: rotate(target.rotate, true) + translate(target.translate), - transitionDuration: duration, - transitionDelay: (zoomin ? "0ms" : "500ms") - }); + var goto = function ( el ) { + if ( !isStep(el) || el == active) { + // selected element is not defined as step or is already active + return false; + } + + // Sometimes it's possible to trigger focus on first link with some keyboard action. + // Browser in such a case tries to scroll the page to make this element visible + // (even that body overflow is set to hidden) and it breaks our careful positioning. + // + // So, as a lousy (and lazy) workaround we will make the page scroll back to the top + // whenever slide is selected + // + // If you are reading this and know any better way to handle it, I'll be glad to hear about it! + window.scrollTo(0, 0); + + var step = stepData["impress-" + el.id]; + + if ( active ) { + active.classList.remove("active"); + } + el.classList.add("active"); + + root.className = "step-" + el.id; + + // `#/step-id` is used instead of `#step-id` to prevent default browser + // scrolling to element in hash + // + // and it has to be set after animation finishes, because in chrome it + // causes transtion being laggy + window.clearTimeout( hashTimeout ); + hashTimeout = window.setTimeout(function () { + window.location.hash = "#/" + el.id; + }, 1000); + + var target = { + rotate: { + x: -parseInt(step.rotate.x, 10), + y: -parseInt(step.rotate.y, 10), + z: -parseInt(step.rotate.z, 10) + }, + translate: { + x: -step.translate.x, + y: -step.translate.y, + z: -step.translate.z + }, + scale: 1 / parseFloat(step.scale) + }; + + // check if the transition is zooming in or not + var zoomin = target.scale >= current.scale; + + // if presentation starts (nothing is active yet) + // don't animate (set duration to 0) + var duration = (active) ? "1s" : "0"; + + css(root, { + // to keep the perspective look similar for different scales + // we need to 'scale' the perspective, too + perspective: step.scale * 1000 + "px", + transform: scale(target.scale), + transitionDuration: duration, + transitionDelay: (zoomin ? "500ms" : "0ms") + }); + + css(canvas, { + transform: rotate(target.rotate, true) + translate(target.translate), + transitionDuration: duration, + transitionDelay: (zoomin ? "0ms" : "500ms") + }); + + current = target; + active = el; + + return el; + }; - current = target; - active = el; + var prev = function () { + var prev = steps.indexOf( active ) - 1; + prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ]; + + return goto(prev); + }; - return el; - }; - - var selectPrev = function () { - var prev = steps.indexOf( active ) - 1; - prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ]; + var next = function () { + var next = steps.indexOf( active ) + 1; + next = next < steps.length ? steps[ next ] : steps[ 0 ]; + + return goto(next); + }; - return select(prev); - }; - - var selectNext = function () { - var next = steps.indexOf( active ) + 1; - next = next < steps.length ? steps[ next ] : steps[ 0 ]; + window.addEventListener("hashchange", function () { + goto( getElementFromUrl() ); + }, false); - return select(next); - }; - - // EVENTS + // START + // by selecting step defined in url or first step of the presentation + goto(getElementFromUrl() || steps[0]); + + return (roots[ "impress-root-" + rootId ] = { + goto: goto, + next: next, + prev: prev + }); + + } +})(document, window); + +// EVENTS + +(function ( document, window ) { + 'use strict'; + // keyboard navigation handler document.addEventListener("keydown", function ( event ) { if ( event.keyCode == 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) { switch( event.keyCode ) { case 33: ; // pg up case 37: ; // left case 38: // up - selectPrev(); + impress().prev(); break; case 9: ; // tab case 32: ; // space case 34: ; // pg down case 39: ; // right case 40: // down - selectNext(); + impress().next(); break; } event.preventDefault(); } }, false); - + + // delegated handler for clicking on the links to presentation steps document.addEventListener("click", function ( event ) { // event delegation with "bubbling" - // check if event target (or any of its parents is a link or a step) + // check if event target (or any of its parents is a link) var target = event.target; while ( (target.tagName != "A") && - (!target.stepData) && (target != document.body) ) { target = target.parentNode; } @@ -313,28 +358,47 @@ // if it's a link to presentation step, target this step if ( href && href[0] == '#' ) { - target = byId( href.slice(1) ); + target = document.getElementById( href.slice(1) ); } } - if ( select(target) ) { + if ( impress().goto(target) ) { + event.stopImmediatePropagation(); event.preventDefault(); } }, false); - var getElementFromUrl = function () { - // get id from url # by removing `#` or `#/` from the beginning, - // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work - return byId( window.location.hash.replace(/^#\/?/,"") ); - } - - window.addEventListener("hashchange", function () { - select( getElementFromUrl() ); + // delegated handler for clicking on step elements + document.addEventListener("click", function ( event ) { + var target = event.target; + // find closest step element + while ( !target.classList.contains("step") && + (target != document.body) ) { + target = target.parentNode; + } + + if ( impress().goto(target) ) { + event.preventDefault(); + } }, false); - // START - // by selecting step defined in url or first step of the presentation - select(getElementFromUrl() || steps[0]); - + // touch handler to detect taps on the left and right side of the screen + document.addEventListener("touchstart", function ( event ) { + if (event.touches.length === 1) { + var x = event.touches[0].clientX, + width = window.innerWidth * 0.3, + result = null; + + if ( x < width ) { + result = impress().prev(); + } else if ( x > window.innerWidth - width ) { + result = impress().next(); + } + + if (result) { + event.preventDefault(); + } + } + }, false); })(document, window); |