Recreando el menú de Google Nexus con jQuery y CSS3

Hace poco me encontré con un tutorial de Codrops en el que recreaban el menú de navegación de Google Nexus. Esto para mi fue muy inspirador, quería diseñar un sistema de navegación tan creativo como el de Google. Por tanto, he decidido crear una versión del menú en jQuery para desafiarme a mí mismo.

He estado urgando con el menú original de Google Nexus un tiempo. En base a mis observaciones, hice un resumen de su comportamiento, el cual puedes ver a continuación:

  • Cuando te posas sobre el botón del menú, sólo muestra los iconos del menú. Y cuando haces clic en él, muestra el menú completo.
  • Cuando se muestran solo los iconos, el menú se esconderá hasta que retires el ratón de la zona. Mientras que cuando se muestra el menú completo, no va a ocultarse hasta que hagas clic en alguna de las secciones.
  • El icono de menú no se mostrará en el menú secundario. El menú secundario se expandirá en el menú completo.
  • Utiliza el evento touchstart móvil y también es responsive.

Ahora que entendemos todas sus características, será mucho más fácil para codificarlo. Ya te digo que no es una réplica 100% real, solo intento capturar la esencia del menú de Google Nexus.

Antes que nada, al estar implementado con CSS3, sólo funcionará en navegadores que soporten transiciones de CSS3. Ya se que es posible hacerlo también con Javascript puro, pero me apetecía más hacerlo con CSS.

HTML

Como puedes ver, tenemos que desarrollar dos menús, y añadir interacciones y animaciones de transición en ellos. La estructura del menú es muy sencilla y estándar. Utilizaremos listas UL para los dos.

Menú principal

<nav>
	<ul>
		<li><a href="#" class="icon icon-menu" id="btn-menu">Menu</a></li>
	</ul>
</nav>

Menú secundario

<div id="sideNav">
	<ul>
		<li class="searchForm">
			<a href="#" class="icon icon-search">
				<span>
					<input type="text" placeholder="Search" class="search" />
				</span>
			</a>
		</li>
		<li><a href="#" class="icon icon-home"><span>Parent</span></a>
			<ul>
				<li><a href="#"><span>Children</span></a></li>
			</ul>
		</li>
	</ul>	
</div>	

CSS

Utilizamos unas cuantas características novedosas de CSS3 como las transiciones, el box-sizing y el diseño sensible. Nosotros vamos a utilizar transiciones de CSS3 para animarlo todo y sacar beneficio de la GPU. Personalmente, creo que las animaciones CSS3 son más suaves y sutiles que las de Javascript.

Ponemos todas las transiciones de CSS juntas.

#sideNav,
#sideNav.showHalfMenu,
#sideNav.showFullMenu,
#sideNav ul ul li,
#sideNav.showFullMenu ul ul li	 {
	-webkit-transition: 0.2s ease;
	-moz-transition: 0.2s ease;			
	-ms-transition: 0.2s ease;						
	transition: 0.2s ease;		
}

Y ahora el resto de estilos del menú.

html.cursor {
	cursor: pointer;
}

nav {
	font-family: 'Roboto', sans-serif;
	width: 100%;
	height: 59px;
	border-bottom:1px solid #ddd;
	position: fixed;
	top:0;
	left:0;
	z-index:20;
	background-color:#ffffff;
}

	nav ul,
	#sideNav ul,
	#sideNav ul ul	{
		margin:0;
		padding:0;
		list-style:none;
	}
	
		nav li {
			margin:0;
			float:left;
			border-right:1px solid #ddd;
			font-size:18px;
		}
		
		nav a,
		#sideNav a {
			color:#5b6064;
			text-decoration:none;
			display:block;
			padding:10px 30px;
			height:59px;
			-webkit-box-sizing: border-box;
			-moz-box-sizing: border-box;
			-o-box-sizing: border-box;
			line-height:35px;
		}
		
		nav a:hover,
		#sideNav a:hover {
			color:#ffffff;
			background-color: #5b6064;
		}	
		
	#sideNav {
		position: fixed;
		left:-60px;
		top:59px;
		width: 60px;
		height:100%;
		border-right:1px solid #ddd;						
		background-color:#ffffff;
		overflow-y: auto;	
	}
		
		#sideNav.showHalfMenu {
			left:0;			
		}
		
		#sideNav.showFullMenu {
			left:0;
			width: 311px;		
		}

			#sideNav.showFullMenu ul ul li {
				height:59px;				
			}	
	
	
		#sideNav > ul {
			width: 100%;		
			padding-bottom:60px;	
		}
	
		#sideNav ul li {
			width: 100%;
			margin:0;		
			font-weight:300;
		}
		
		#sideNav ul li a {
			border-bottom:1px solid #ddd;		
			padding-left:70px;
		}
		
		#sideNav ul li span {
			position: relative;
			top:3px;
		}		
		
		#sideNav ul ul li {
			overflow:hidden;
			height: 0;				
		}

Buscador

Una de las características del menú, es que el primer elemento del menú secundario cuenta con un campo de búsqueda que se mezcla con el menú. No estoy seguro de si es una buena práctica, pero le otorga un aspecto elegante.

#sideNav input.search {
			font-family: 'Roboto', sans-serif;			
			border:0;
			outline:0;
			font-weight:300;
			background:transparent;
			color:#5b6064;			
		}

				
		input.search::-webkit-input-placeholder {
			color:#5b6064;		
		}
		input.search:-moz-placeholder {
			color:#5b6064;		
		}
		input.search::-moz-placeholder {
			color:#5b6064;		
		}
		input.search:-ms-input-placeholder {
			color:#5b6064;
		}
		
		#sideNav li.searchForm:hover input.search:focus,
		#sideNav li.searchForm:hover input.search::-webkit-input-placeholder {
			color:#fff;		
		}

Responsive

Pues eso, el código para que el menú se vea correctamente en cualquier dispositivo.

@media only screen and (min-width: 200px) and (max-width: 480px) {

	nav a,
	#sideNav a {
		padding:10px 15px;
	}	
	
	nav a#btn-menu {
		padding:10px 30px;	
	}

	#sideNav.showFullMenu,
	#sideNav.showFullMenu li,
	#sideNav.showFullMenu	a	 {
		width: 100%;
	}    

}

Javascript

Y... la última sección. Animar es sólo cuestión de aplicar las clases CSS adecuadas en el lugar correcto. Hemos creado algunas funciones tales como showHalfMenu(), showFullMenu(), HideMenu() y toggleMenu para que podamos volver a utilizarlas.

También he añadido un script de detección de dispositivos móviles para asegurarnos que los dispositivos de pantalla táctil no tienen que cargar los eventos del ratón.

$(function () {
	
	var GNmenu = {
		isMenuOpened: false,
		init : function () {

			var menuBtn = $('#btn-menu');
			
			// make it work on touch screen device and mouse click
			menuBtn.on('touchstart click', function (e) {
				e.stopPropagation();
				e.preventDefault();
				
				/* if the menu is half show, show the whole menu instead of hide it */
				if ($('#sideNav').hasClass('showHalfMenu') && !$('#sideNav').hasClass('showFullMenu')) {
					GNmenu.showFullMenu();
				} else {
					GNmenu.toggleMenu();												
				}
				
			});

			// only do all this if it's desktop
			if (!GNmenu.isMobile()) {

				menuBtn.bind('mouseover', function () {
						GNmenu.showHalfMenu();
				});
				
				menuBtn.bind('mouseout', function () {
						GNmenu.hideMenu();
				});	
			
				$('#sideNav').bind('mouseover', function () {
						GNmenu.showFullMenu();
				});											

				// this allow user to hide the menu by clicking on web page body
				GNmenu.bodyClick();
			}
						
			// search form
			// unbind the bodyClick event 
			$('.searchForm input[type=text]').focus(function () {
				$('html').unbind('click');	
			}).blur(function() {
				GNmenu.bodyClick();												
			});
													
		},
		
		bodyClick: function () {
		
			$('html').bind('click',function () {
				if (GNmenu.isMenuOpened) {
					GNmenu.hideMenu();														
				}
			});					
		
		},
		
		toggleMenu: function () {
			if (!GNmenu.isMenuOpened) {
				GNmenu.showFullMenu();
			} else {
				GNmenu.hideMenu();					
			}
		},
		
		showHalfMenu: function () {
			$('#sideNav').addClass('showHalfMenu');
			GNmenu.isMenuOpened = true;					
		},
		
		showFullMenu: function () {
			$('#btn-menu').addClass('icon-menu-active');
			$('#sideNav').addClass('showFullMenu');
			$('html').addClass('cursor');				
			GNmenu.isMenuOpened = true;							
		},
		
		hideMenu: function () {
			$('#btn-menu').removeClass('icon-menu-active');
			$('#sideNav').removeClass('showFullMenu showHalfMenu');		
			$('html').removeClass('cursor');						
			GNmenu.isMenuOpened = false;					
		},
		
		// Mobile Detection: http://stackoverflow.com/a/11381730				
		isMobile: function() {
			var check = false;
			(function(a){if(/(android|bbd+|meego).+mobile|avantgo|bada/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)/|plucker|pocket|psp|series(4|6)0|symbian|treo|up.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
			
			return check; 					
		}
		
	}
	
	// Run the Google Nexus Inspired Menu
	GNmenu.init();
	
});	

Fuente: queness.com

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP