Created
August 22, 2012 03:30
-
-
Save stevommmm/3422079 to your computer and use it in GitHub Desktop.
A three dimensional and space efficient menu concept. Could easily be made CSS-only via :hover but touch is cool so I went for JavaScript.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- Menu element that rolls in from the left --> | |
<div class="meny"> | |
<h2>More Experiments</h2> | |
<ul> | |
<li><a href="http://lab.hakim.se/radar/">Radar</a></li> | |
<li><a href="http://lab.hakim.se/forkit-js/">forkit.js</a></li> | |
<li><a href="http://lab.hakim.se/scroll-effects/">stroll.js</a></li> | |
<li><a href="http://lab.hakim.se/zoom-js">zoom.js</a></li> | |
<li><a href="http://lab.hakim.se/reveal-js">reveal.js</a></li> | |
<li><a href="http://itunes.apple.com/us/app/sinuous/id543097218">Sinuous iOS</a></li> | |
<li><a href="http://hakim.se/experiments/css/domtree/">DOM Tree</a></li> | |
<li><a href="http://hakim.se/experiments/css/holobox/">Holobox</a></li> | |
<li><a href="http://hakim.se/experiments/html5/404/netmag.html">404</a></li> | |
</ul> | |
</div> | |
<!-- Arrow that appears from the left when menu is collapsed --> | |
<div class="meny-arrow">►</div> | |
<!-- Contents that will be rotated when the meny is expanded --> | |
<div class="meny-contents"> | |
<div class="cover"></div> | |
<article> | |
<h1>Meny</h1> | |
<p> | |
A three dimensional and space efficient menu concept. | |
Move your mouse to the left edge of this page to expand the menu. | |
<p> | |
<p> | |
CSS 3D transforms are used for the transition effect and JavaScript is used to track mouse/touch movement. | |
</p> | |
<p> | |
The name, <em>Meny</em>, is swedish. | |
</p> | |
<small> | |
Created by <a href="http://twitter.com/hakimel">@hakimel</a> / <a href="http://hakim.se/">http://hakim.se</a> | |
</small> | |
</article> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(){ | |
var meny = document.querySelector( '.meny' ); | |
// Avoid throwing errors if the script runs on a page with | |
// no .meny | |
if( !meny || !meny.parentNode ) { return; } | |
var menyWrapper = meny.parentNode; | |
// Add a class to identify the parent of the meny parts | |
menyWrapper.className += ' meny-wrapper'; | |
var indentX = menyWrapper.offsetLeft, | |
activateX = 40, | |
deactivateX = meny.offsetWidth || 200, | |
touchStartX = null, | |
touchMoveX = null, | |
isActive = false, | |
isMouseDown = false; | |
var supports3DTransforms = 'WebkitPerspective' in document.body.style || | |
'MozPerspective' in document.body.style || | |
'msPerspective' in document.body.style || | |
'OPerspective' in document.body.style || | |
'perspective' in document.body.style; | |
document.addEventListener( 'mousedown', onMouseDown, false ); | |
document.addEventListener( 'mouseup', onMouseUp, false ); | |
document.addEventListener( 'mousemove', onMouseMove, false ); | |
document.addEventListener( 'touchstart', onTouchStart, false ); | |
document.addEventListener( 'touchend', onTouchEnd, false ); | |
// Fall back to more basic CSS | |
if( !supports3DTransforms ) { | |
document.documentElement.className += ' meny-no-transform'; | |
} | |
document.documentElement.className += ' meny-ready'; | |
function onMouseDown( event ) { | |
isMouseDown = true; | |
} | |
function onMouseMove( event ) { | |
// Prevent opening/closing when mouse is down since | |
// the user may be selecting text | |
if( !isMouseDown ) { | |
var x = event.clientX - indentX; | |
if( x > deactivateX ) { | |
deactivate(); | |
} | |
else if( x < activateX ) { | |
activate(); | |
} | |
} | |
} | |
function onMouseUp( event ) { | |
isMouseDown = false; | |
} | |
function onTouchStart( event ) { | |
touchStartX = event.touches[0].clientX - indentX; | |
touchMoveX = null; | |
if( isActive || touchStartX < activateX ) { | |
document.addEventListener( 'touchmove', onTouchMove, false ); | |
} | |
} | |
function onTouchMove( event ) { | |
touchMoveX = event.touches[0].clientX - indentX; | |
if( isActive && touchMoveX < touchStartX - activateX ) { | |
deactivate(); | |
event.preventDefault(); | |
} | |
else if( touchStartX < activateX && touchMoveX > touchStartX + activateX ) { | |
activate(); | |
event.preventDefault(); | |
} | |
} | |
function onTouchEnd( event ) { | |
document.addEventListener( 'touchmove', onTouchMove, false ); | |
// If there was no movement this was a tap | |
if( touchMoveX === null ) { | |
// Hide the menu when tapping on the content area | |
if( touchStartX > deactivateX ) { | |
deactivate(); | |
} | |
// Show the meny when tapping on the left edge | |
else if( touchStartX < activateX * 2 ) { | |
activate(); | |
} | |
} | |
} | |
function activate() { | |
if( isActive === false ) { | |
isActive = true; | |
// Add the meny-active class and clean up whitespace | |
document.documentElement.className = document.documentElement.className.replace( /\s+$/gi, '' ) + ' meny-active'; | |
} | |
} | |
function deactivate() { | |
if( isActive === true ) { | |
isActive = false; | |
// Remove the meny-active class | |
document.documentElement.className = document.documentElement.className.replace( 'meny-active', '' ); | |
} | |
} | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* { | |
margin: 0; | |
padding: 0; | |
} | |
html, | |
body { | |
height: 100%; | |
overflow: hidden; | |
} | |
body { | |
background-color: #222; | |
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAGklEQVQIW2NkYGD4D8SMQAwGcAY2AbBKDBUAVuYCBQPd34sAAAAASUVORK5CYII=); | |
background-repeat: repeat; | |
font-family: 'Lato', Helvetica, sans-serif; | |
font-size: 16px; | |
color: #222; | |
} | |
/** | |
* Element which wraps all of the other .meny parts | |
*/ | |
.meny-wrapper { | |
-webkit-perspective: 800px; | |
-moz-perspective: 800px; | |
-ms-perspective: 800px; | |
-o-perspective: 800px; | |
perspective: 800px; | |
-webkit-perspective-origin: 0% 50%; | |
-moz-perspective-origin: 0% 50%; | |
-ms-perspective-origin: 0% 50%; | |
-o-perspective-origin: 0% 50%; | |
perspective-origin: 0% 50%; | |
} | |
.meny, | |
.meny-contents { | |
-webkit-box-sizing: border-box; | |
-moz-box-sizing: border-box; | |
box-sizing: border-box; | |
-webkit-transition: -webkit-transform .4s ease; | |
-moz-transition: -moz-transform .4s ease; | |
-ms-transition: -ms-transform .4s ease; | |
-o-transition: -o-transform .4s ease; | |
transition: transform .4s ease; | |
-webkit-transform-origin: 0% 50%; | |
-moz-transform-origin: 0% 50%; | |
-ms-transform-origin: 0% 50%; | |
-o-transform-origin: 0% 50%; | |
transform-origin: 0% 50%; | |
} | |
/** | |
* The menu element which expands out from the left. | |
*/ | |
.meny { | |
display: none; | |
position: fixed; | |
height: 100%; | |
width: 200px; | |
z-index: 1; | |
margin: 0px; | |
padding: 15px; | |
-webkit-transform: rotateY( -30deg ) translateX( -97% ); | |
-moz-transform: rotateY( -30deg ) translateX( -97% ); | |
-ms-transform: rotateY( -30deg ) translateX( -97% ); | |
-o-transform: rotateY( -30deg ) translateX( -97% ); | |
transform: rotateY( -30deg ) translateX( -97% ); | |
} | |
.meny-ready .meny { | |
display: block; | |
} | |
.meny-active .meny { | |
-webkit-transform: rotateY(0deg); | |
-moz-transform: rotateY(0deg); | |
-ms-transform: rotateY(0deg); | |
-o-transform: rotateY(0deg); | |
transform: rotateY(0deg); | |
} | |
/** | |
* Page contents which gets pushed aside while meny is active. | |
*/ | |
.meny-contents { | |
background: #eee; | |
padding: 20px 40px; | |
width: 100%; | |
height: 100%; | |
overflow-y: auto; | |
} | |
.meny-active .meny-contents { | |
-webkit-transform: translateX( 200px ) rotateY( 15deg ); | |
-moz-transform: translateX( 200px ) rotateY( 15deg ); | |
-ms-transform: translateX( 200px ) rotateY( 15deg ); | |
-o-transform: translateX( 200px ) rotateY( 15deg ); | |
transform: translateX( 200px ) rotateY( 15deg ); | |
} | |
/** | |
* A shadow-like element placed on top of the contents while | |
* meny is active. | |
*/ | |
.meny-contents .cover { | |
display: none; | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
top: 0; | |
left: 0; | |
visibility: hidden; | |
z-index: 1000; | |
opacity: 0; | |
background: -moz-linear-gradient(left, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.65) 100%); | |
background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(0,0,0,0.15)), color-stop(100%,rgba(0,0,0,0.65))); | |
background: -webkit-linear-gradient(left, rgba(0,0,0,0.15) 0%,rgba(0,0,0,0.65) 100%); | |
background: -o-linear-gradient(left, rgba(0,0,0,0.15) 0%,rgba(0,0,0,0.65) 100%); | |
background: -ms-linear-gradient(left, rgba(0,0,0,0.15) 0%,rgba(0,0,0,0.65) 100%); | |
background: linear-gradient(to right, rgba(0,0,0,0.15) 0%,rgba(0,0,0,0.65) 100%); | |
-webkit-transition: all .4s ease; | |
-moz-transition: all .4s ease; | |
-ms-transition: all .4s ease; | |
-o-transition: all .4s ease; | |
transition: all .4s ease; | |
} | |
.meny-ready .meny-contents .cover { | |
display: block; | |
} | |
.meny-active .meny-contents .cover { | |
visibility: visible; | |
opacity: 1; | |
} | |
/** | |
* Graphic that highlights menu availability while inactive. | |
*/ | |
.meny-arrow { | |
position: absolute; | |
top: 45%; | |
left: 12px; | |
z-index: 2; | |
font-family: sans-serif; | |
font-size: 20px; | |
color: #333; | |
-webkit-transition: left 0.8s cubic-bezier(0.680, -0.550, 0.265, 1.550); | |
-moz-transition: left 0.8s cubic-bezier(0.680, -0.550, 0.265, 1.550); | |
-ms-transition: left 0.8s cubic-bezier(0.680, -0.550, 0.265, 1.550); | |
-o-transition: left 0.8s cubic-bezier(0.680, -0.550, 0.265, 1.550); | |
transition: left 0.8s cubic-bezier(0.680, -0.550, 0.265, 1.550); | |
} | |
.meny-active .meny-arrow { | |
left: -40px; | |
opacity: 0; | |
} | |
/** | |
* Fallback for browsers that don't support transforms. | |
*/ | |
.meny-no-transform .meny, | |
.meny-no-transform .meny-contents { | |
-webkit-transition: left .4s ease; | |
-moz-transition: left .4s ease; | |
-ms-transition: left .4s ease; | |
-o-transition: left .4s ease; | |
transition: left .4s ease; | |
} | |
.meny-no-transform .meny { | |
left: -300px; | |
} | |
.meny-no-transform.meny-active .meny { | |
left: 0px; | |
} | |
.meny-no-transform .meny-contents { | |
position: absolute; | |
} | |
.meny-no-transform.meny-active .meny-contents { | |
left: 300px; | |
} | |
/** | |
* Styles that are more or less specific to the demo page: | |
*/ | |
a { | |
color: #c2575b; | |
text-decoration: none; | |
-webkit-transition: 0.15s color ease; | |
-moz-transition: 0.15s color ease; | |
-ms-transition: 0.15s color ease; | |
-o-transition: 0.15s color ease; | |
transition: 0.15s color ease; | |
} | |
a:hover { | |
color: #f76f76; | |
} | |
h1, | |
h2 { | |
font-size: 18px; | |
} | |
.meny { | |
background: #333; | |
color: #eee; | |
} | |
.meny ul { | |
margin-top: 10px; | |
} | |
.meny ul li { | |
list-style: none; | |
font-size: 18px; | |
padding: 3px 5px; | |
} | |
.meny ul li:before { | |
content: '-'; | |
margin-right: 5px; | |
color: rgba( 255, 255, 255, 0.2 ); | |
} | |
.meny-contents>article { | |
max-width: 400px; | |
} | |
.meny-contents p { | |
margin: 10px 0 10px 0; | |
font-size: 16px; | |
line-height: 1.32; | |
} | |
.meny-contents small { | |
display: block; | |
margin-top: 10px; | |
padding-top: 10px; | |
color: #333; | |
font-size: 0.85em; | |
border-top: 1px dashed #ccc; | |
-webkit-text-size-adjust: none; | |
} | |
.meny-contents .sharing { | |
position: absolute; | |
bottom: 20px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment