aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBartek Szopka <bartek.szopka+github@gmail.com>2012-02-15 21:20:02 +0100
committerBartek Szopka <bartek.szopka+github@gmail.com>2012-02-15 21:20:02 +0100
commit224c75b4636d26e02c005ea0c0b0cf17659a480a (patch)
tree9cd2650bd712c6aee6d494eef87ebde8f84b2c65
parenta62f419cb9f9e7eb30a735002c98390dafac595f (diff)
parent14a86ef9efcd234e2fd11704130254df07730db6 (diff)
downloadimpress.js-224c75b4636d26e02c005ea0c0b0cf17659a480a.tar.gz
impressive api merged
-rw-r--r--README.md5
-rw-r--r--index.html37
-rw-r--r--js/impress.js422
3 files changed, 283 insertions, 181 deletions
diff --git a/README.md b/README.md
index 9b87c1a..2e2852a 100644
--- a/README.md
+++ b/README.md
@@ -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))
diff --git a/index.html b/index.html
index 65bc93c..6649992 100644
--- a/index.html
+++ b/index.html
@@ -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);