commit fdd6b36f01b34cdf8c978ce2144784baf995fc57 Author: Hakim El Hattab Date: Tue Jun 7 21:10:59 2011 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..dec0ea42 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +.svn +log/*.log +tmp/** diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..28cfd7d4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2011 Hakim El Hattab, http://hakim.se + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..923ddb88 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# CSS 3D Slideshow diff --git a/assets/fonts/leaguegothic/league_gothic-webfont.ttf b/assets/fonts/leaguegothic/league_gothic-webfont.ttf new file mode 100644 index 00000000..29f896a7 Binary files /dev/null and b/assets/fonts/leaguegothic/league_gothic-webfont.ttf differ diff --git a/assets/images/breakdom.jpg b/assets/images/breakdom.jpg new file mode 100644 index 00000000..64dc3f38 Binary files /dev/null and b/assets/images/breakdom.jpg differ diff --git a/css/main.css b/css/main.css new file mode 100644 index 00000000..494cfc21 --- /dev/null +++ b/css/main.css @@ -0,0 +1,218 @@ +/** + * @author Hakim El Hattab + */ + + +/********************************************* + * FONT-FACE DEFINITIONS + *********************************************/ + +@font-face { + font-family: 'League Gothic'; + src: url('../assets/fonts/leaguegothic/league_gothic-webfont.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + + +/********************************************* + * GLOBAL STYLES + *********************************************/ + +html, body { + padding: 0; + margin: 0; + overflow: hidden; + + font-family: 'Crimson Text', Times, 'Times New Roman', serif; + font-size: 36px; + + background: #fff; + color: #222; + + width: 100%; + height: 100%; + + background-image: -webkit-gradient( + radial, + 50% 50%, 0, + 50% 50%, 1000, + from(rgba(245,245,245,1.0)), + to(rgba(100,100,100,1.0)) + ); + + background-image: -moz-radial-gradient( + 50% 50% 90deg, + rgba(245,245,245,1.0) 0%, + rgba(100,100,100,1.0) 100% + ); + +} + + +/********************************************* + * HEADERS + *********************************************/ +h1, h2, h3, h4 { + margin: 0 0 20px 0; + font-family: 'League Gothic', Arial, Helvetica, sans-serif; + line-height: 0.9em; + letter-spacing: 0.02em; + text-transform: uppercase; + color: #222; + text-shadow: 0px 0px 2px #fff, 0px 0px 4px #bbb; +} + +h1 { font-size: 136px; } +h2 { font-size: 76px; } +h3 { font-size: 56px; } +h4 { font-size: 36px; } + +h1.inverted, +h2.inverted, +h3.inverted, +h4.inverted { + color: #fff; + text-shadow: 0px 0px 2px #fff, 0px 0px 2px #888; +} + + +/********************************************* + * SLIDES + *********************************************/ +#main { + position: absolute; + width: 800px; + height: 600px; + + left: 50%; + top: 50%; + margin-left: -400px; + margin-top: -320px; + + text-align: center; + + -webkit-perspective: 600px; + -webkit-perspective-origin: 50% 25%; +} + +#main>section, +#main>section>section { + display: none; + + position: absolute; + width: 100%; + min-height: 600px; + + -webkit-transform-style: preserve-3d; + + -webkit-transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + -moz-transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + -o-transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); +} + +#main section.past { + display: block; + opacity: 0; + + -webkit-transform: translate3d(-100%, 0, 0) + rotateY(-90deg) + translate3d(-100%, 0, 0); +} + +#main section.present { + display: block; +} + +#main section.future { + display: block; + opacity: 0; + + -webkit-transform: translate3d(100%, 0, 0) + rotateY(90deg) + translate3d(100%, 0, 0); +} + +#main section>section.past { + display: block; + opacity: 0; + + -webkit-transform: translate3d(0, -50%, 0) + rotateX(70deg) + translate3d(0, -50%, 0); +} +#main section>section.future { + display: block; + opacity: 0; + + -webkit-transform: translate3d(0, 50%, 0) + rotateX(-70deg) + translate3d(0, 50%, 0); +} + + +/********************************************* + * DEFAULT ELEMENT STYLES + *********************************************/ + +#main>section { + line-height: 1.2em; + text-shadow: 0px 0px 2px #fff, 0px 0px 4px #bbb; + font-weight: 600; +} + +ol { + list-style: decimal; + list-style-position: inside; +} + +li, p { + margin-bottom: 10px; +} + +a:not(.image) { + color: #1b6263; + text-decoration: none; + border-bottom: 1px dashed rgba(0,0,0,0.3); + padding: 1px 3px; +} + + a:not(.image):hover { + color: #fff; + background: #2fa794; + text-shadow: none; + border: none; + } + +img { + margin: 30px 0 0 0; + background: rgba(255,255,255,0.12); + border: 4px solid #eee; + + -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); + + -webkit-transition: all .11s linear; + -moz-transition: all .11s linear; + -o-transition: all .11s linear; + transition: all .11s linear; +} + + a.image:hover img { + background: rgba(255,255,255,0.2); + + -webkit-box-shadow: 0 0 20px rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 0 20px rgba(0, 0, 0, 0.25); + box-shadow: 0 0 20px rgba(0, 0, 0, 0.25); + } + + + + + + + + + diff --git a/css/reset.css b/css/reset.css new file mode 100644 index 00000000..68f227a4 --- /dev/null +++ b/css/reset.css @@ -0,0 +1,57 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + + +/* HTML5BP: + These selection declarations have to be separate. + No text-shadow: twitter.com/miketaylr/status/12228805301 + Also: hot pink. */ +::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; } +::selection { background:#FF5E99; color:#fff; text-shadow: none; } + diff --git a/index.html b/index.html new file mode 100644 index 00000000..5c712509 --- /dev/null +++ b/index.html @@ -0,0 +1,151 @@ + + + + + + + CSS 3D Slideshow + + + + + + + + + + +
+ +
+

Slideshow

+

With 3D effects. And stuff.

+ +
+ +
+

Heads Up

+

+ This requires a browser with support for CSS3 3D transforms, such as Mobile Safari. +

+
+ + +
+
+

Vertical Slides

+

+ Slides can be nested inside of other slides,
+ try pressing down. +

+ + + +
+
+

Basement Level 1

+

Press down or up to navigate.

+
+
+

Basement Level 2

+

This is totally the Google logo:

+ +
+
+

Basement Level 3

+

That's it, time to go back up.

+ + + +
+
+ +
+

Marvelous Unordered List

+
    +
  • No order here
  • +
  • Or here
  • +
  • Or here
  • +
  • Or here
  • +
+
+ +
+

Fantastic Ordered List

+
    +
  1. One is smaller than...
  2. +
  3. Two is smaller than...
  4. +
  5. Three!
  6. +
+
+ +
+

Intergalactic Interconnections

+

+ You can link between slides internally,
+ like this. +

+
+ +
+

Spectacular image!

+ + + +
+ +
+

Stellar Links

+ +
+ +
+

THE END

+

BY Hakim El Hattab / hakim.se

+
+ +
+ + + + + + + +
+ + +
+ + + + + + + + + \ No newline at end of file diff --git a/js/slideshow.js b/js/slideshow.js new file mode 100644 index 00000000..8165a40b --- /dev/null +++ b/js/slideshow.js @@ -0,0 +1,282 @@ +/** + * Copyright (C) 2011 Hakim El Hattab, http://hakim.se + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Handles the very minimal navigation logic involved in the + * slideshow. Including keyboard navigation, touch interaction + * and URL history behavior. + * + * Slides are given unique hash based URL's so that they can be + * opened directly. I didn't use the HTML5 History API for this + * as it would have required the addition of server side rewrite + * rules and hence require more effort for anyone to set up. + * + * This component can be called from other scripts via a tiny API: + * - Slideshow.navigateTo( indexh, indexv ); + * - Slideshow.navigateLeft(); + * - Slideshow.navigateRight(); + * - Slideshow.navigateUp(); + * - Slideshow.navigateDown(); + * + * + * version 0.1: + * - First release + * + * version 0.2: + * - Refactored code and added inline documentation + * - Slides now have unique URL's + * - A basic API to invoke navigation was added + * + * version 0.3: + * - Added licensing terms + * + * + * @author Hakim El Hattab + * @version 0.3 + */ +var Slideshow = (function(){ + + var indexh = 0, + indexv = 0; + + /** + * Activates the main program logic. + */ + function initialize() { + document.addEventListener('keydown', onDocumentKeyDown, false); + document.addEventListener('touchstart', onDocumentTouchStart, false); + window.addEventListener('hashchange', onWindowHashChange, false); + + // Read the initial state of the URL (hash) + readURL(); + } + + /** + * Handler for the document level 'keydown' event. + * + * @param {Object} event + */ + function onDocumentKeyDown( event ) { + + if( event.keyCode >= 37 && event.keyCode <= 40 ) { + + switch( event.keyCode ) { + case 37: navigateLeft(); break; // left + case 39: navigateRight(); break; // right + case 38: navigateUp(); break; // up + case 40: navigateDown(); break; // down + } + + slide(); + + event.preventDefault(); + + } + } + + /** + * Handler for the document level 'touchstart' event. + * + * This enables very basic tap interaction for touch + * devices. Added mainly for performance testing of 3D + * transforms on iOS but was so happily surprised with + * how smoothly it runs so I left it in here. Apple +1 + * + * @param {Object} event + */ + function onDocumentTouchStart( event ) { + + // We're only interested in one point taps + if (event.touches.length == 1) { + event.preventDefault(); + + var point = { + x: event.touches[0].clientX, + y: event.touches[0].clientY + }; + + // Define the extent of the areas that may be tapped + // to navigate + var wt = window.innerWidth * 0.3; + var ht = window.innerHeight * 0.3; + + if( point.x < wt ) { + navigateLeft(); + } + else if( point.x > window.innerWidth - wt ) { + navigateRight(); + } + else if( point.y < ht ) { + navigateUp(); + } + else if( point.y > window.innerHeight - ht ) { + navigateDown(); + } + + slide(); + + } + } + + + /** + * Handler for the window level 'hashchange' event. + * + * @param {Object} event + */ + function onWindowHashChange( event ) { + readURL(); + } + + /** + * Updates one dimension of slides by showing the slide + * with the specified index. + * + * @param {String} selector A CSS selector that will fetch + * the group of slides we are working with + * @param {Number} index The index of the slide that should be + * shown + * + * @return {Number} The index of the slide that is now shown, + * might differ from the passed in index if it was out of + * bounds. + */ + function updateSlides( selector, index ) { + + // Select all slides and convert the NodeList result to + // an array + var slides = Array.prototype.slice.call( document.querySelectorAll( selector ) ); + + if( slides.length ) { + // Enforce max and minimum index bounds + index = Math.max(Math.min(index, slides.length - 1), 0); + + slides[index].setAttribute('class', 'present'); + + // Any element previous to index is given the 'past' class + slides.slice(0, index).map(function(element){ + element.setAttribute('class', 'past'); + }); + + // Any element subsequent to index is given the 'future' class + slides.slice(index + 1).map(function(element){ + element.setAttribute('class', 'future'); + }); + } + else { + // Since there are no slides we can't be anywhere beyond the + // zeroth index + index = 0; + } + + return index; + + } + + /** + * Updates the visual slides to represent the currently + * set indices. + */ + function slide() { + indexh = updateSlides( '#main>section', indexh ); + indexv = updateSlides( 'section.present>section', indexv ); + + writeURL(); + } + + /** + * Reads the current URL (hash) and navigates accordingly. + */ + function readURL() { + // Break the hash down to separate components + var bits = window.location.hash.slice(2).split('/'); + + // Read the index components of the hash + indexh = bits[0] ? parseInt( bits[0] ) : 0; + indexv = bits[1] ? parseInt( bits[1] ) : 0; + + navigateTo( indexh, indexv ); + } + + /** + * Updates the page URL (hash) to reflect the current + * navigational state. + */ + function writeURL() { + var url = '/'; + + // Only include the minimum possible number of components in + // the URL + if( indexh > 0 || indexv > 0 ) url += indexh + if( indexv > 0 ) url += '/' + indexv + + window.location.hash = url; + } + + /** + * Triggers a navigation to the specified indices. + * + * @param {Number} h The horizontal index of the slide to show + * @param {Number} v The vertical index of the slide to show + */ + function navigateTo( h, v ) { + indexh = h === undefined ? indexh : h; + indexv = v === undefined ? indexv : v; + + slide(); + } + + function navigateLeft() { + indexh --; + indexv = 0; + slide(); + } + function navigateRight() { + indexh ++; + indexv = 0; + slide(); + } + function navigateUp() { + indexv --; + slide(); + } + function navigateDown() { + indexv ++; + slide(); + } + + // Initialize the program. Done right before returning to ensure + // that any inline variable definitions are available to all + // functions + initialize(); + + // Expose some methods publicly + return { + navigateTo: navigateTo, + navigateLeft: navigateLeft, + navigateRight: navigateRight, + navigateUp: navigateUp, + navigateDown: navigateDown + }; + +})(); +