Skip to content

Instantly share code, notes, and snippets.

@stevommmm
Created August 22, 2012 03:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevommmm/3422079 to your computer and use it in GitHub Desktop.
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.
<!-- 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">&#x25BA;</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>
(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', '' );
}
}
})();
* {
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