Come realizzare la view di una HTML Mobile App con tab-bar animata

Finora abbiamo parlato di performance e fluidità di applicazioni mobile, in particolare di quali tecniche e consigli adottare per potere migliorare le prestazioni e limitare in modo considerevole le differenze tra un progetto realizzato in HTML5, CSS3 e Javascript e un’app sviluppata nativamente.

In questo articolo ti voglio mostrare come usare del markup HTML5 insieme a codice CSS3 performante per poter impostare una “classica” view (quindi una tipica schermata mobile) con una tab-bar interna, che richiami un’animazione al momento del passaggio da una scheda ad un’altra.

La struttura della view

L’obiettivo è quello di realizzare una struttura che presenti un’header all’interno del quale posizioneremo il titolo e due pulsanti, un footer che racchiuda la tab-bar, ed una parte centrale con le varie schede legate ai pulsanti della tab-bar.

Per rendere la user-experience più accattivante inseriremo un’animazione stile ‘slide’ nel passaggio tra una scheda e l’altra.

html-mobile-tabbar-sketch

Ovviamente utilizzeremo animazioni fluide e performanti, sfruttando anche la tecnica dell’accelerazione hardware.

Se sei interessato/a a scoprire come funziona la tecnica dell’accelerazione hardware (o hardware acceleration) e poterla impiegare per realizzare animazioni leggere e che sfruttano la memoria grafica del dispositivo (o GPU), ti consiglio di leggere questo post:

Cosa includere nel documento

Per il nostro esperimento includeremo nel documento le seguenti risorse esterne:

  • il foglio di stile ‘normalize.css’, per il reset su tutti i browser degli elementi HTML
  • la libreria Modernizr, utile per ottenere il nome corretto dell’evento di fine animazione CSS, a seconda del browser usato
  • il “solito” jQuery, di cui ci serviremo per gestire gli eventi (come il ‘click’ delle tab) e per la manipolazione del DOM (essenzialmente aggiungere o rimuovere classi di stile).

Il markup HTML

markup-html

Vediamo subito come impostare gli elementi DOM all’interno del nostro documento:

 


<!-- header della schermata -->
	<nav class="header">
		<a class="left" href="https://www.yourinspiration.com" data-js="prev"><i class="fa fa-globe"></i> YourInspiration.com</a>			
		<button href="#" class="right" data-js="settings"><i class="fa fa-cog"></i></button>
		<h1 class="title">HTML Mobile App</h1>
	</nav>

	<!-- footer della schermata, che funge da contenitore per i pulsanti della tabbar -->
	<nav class="footer">
	<!-- per ogni pulsante l'attributo 'href' si riferisce a alla tab ad esso legata-->
		<a href="#tab1"> 
			Tab 1
		</a>
		<a href="#tab2">
			Tab 2
		</a>
		<a href="#tab3">
			Tab 3
		</a>

</nav>

	<!-- wrapper per le tab della schermata -->
	<article class="content">
		
		<div id="tab1" class="tab">

			<h1>Tab 1</h1>

		</div>
		<div id="tab2" class="tab">

			<h1>Tab 2</h1>

		</div>
		<div id="tab3" class="tab">

			<h1>Tab 3</h1>

		</div>
		
	</article>

Gli stili CSS3

Per quanto riguarda gli stili CSS, come prima cosa facciamo in modo che header e footer abbiano le seguenti caratteristiche:

/* stili generali per l'header e la tabbar: facciamo in modo che siano fixed, e che si adattino alla larghezza della pagina tramite le proprietà ‘left’ e ‘right’;  */
nav {
  position: fixed;
  right: 0;
  left: 0;
  z-index: 10;
  height: 45px;
  padding-right: 10px;
  padding-left: 10px;
  background: #2759A3;
}

Posizioniamo quindo il footer in fondo al documento, specificando un tipo di visualizzazione “formato tabella” con la proprietà “display: table”.

nav.footer{
	 /* siccome tutti i pulsanti contenuti nella tab devono occupare assieme il 100% della latghezza, usiamo lo stesso 'display' del tag <table> */
	display: table;	bottom: 0;      /*posizioniamo la tabbar in fondo al documento*/
	height: 50px;   /* aumentiamo l'altezza a 50px */
	/*azzeriamo il padding per fare in modo che tutto lo spazio sia occupato dai pulsanti*/
	padding: 0;		
	table-layout: fixed; /*impostaiamo un tipo di layout fisso per il display*/
}

Vediamo ora come trattare il titolo ed i pulsanti presenti all’interno dell’header:

/* 
Il titolo viene centrato nella barra di navigazione, e largo quanto l'elemento <nav> che lo contiene
In altre parole copre interamente il suo contenitore
*/
	nav.header .title {
	  position: absolute;
	  top: 0;
	  left: 0;
	  display: block;
	  margin:0;
	  width: 100%;
	  font-size: 18px;
	  font-weight: 100;
	  line-height: 45px;
	  color: #F4F4F4;
	  text-align: center;
	  white-space: nowrap;
	}
		
	/* stili per il pulsanti dell'header: facciamo in mdo; */
	nav.header button, 
	nav.header a{	  

		/* button ed anchor sono impostati in modo assoluto all'interno della navbar */
	
	  position: relative;
	  top: 7px;
	
	  /* impostaiamo lo z-index per visualizzare i pulsanti sopra l'elemento del title */
	  z-index: 20;
	  
	 /*impostiamo uno stile flat*/
	 padding: 6px 12px 7px; /*aumentiamo l'area per il tap sull'elemento con il padding */
	 border:none;	
	 color: #FFF;
	 background: transparent;
	 text-decoration: none;	

 outline: 0;  
}
	
/* posizioniamo i pulsanti con classe 'right' e 'left' rispettivamente a destra o sinistra */
nav.header a.right,
nav.header button.right{
	float: right;;
margin-left: 10px;
	right:0;
}
nav.header a.left,
nav.header button.left {
	float: left;
	margin-right: 10px;
	left:0;
}

/*stili per l'hover dei pulsanti*/
nav.header button:hover, nav a:hover{	
	color: #FFF;
}

header-tabbar

Mentre invece i pulsanti della tab-bar avranno i seguenti stili:

/*stili per i pulsanti della tabbar*/

nav.footer > a{
	 /* visto che per il contenitore abbiamo usato il dispay del tag <table>, di conseguenza per i pulsanti usiamo quello della cella */
	display: table-cell;
	 /* piccolo hach per fare in modo che il borwser attribuisca una larghezza ai pulsanti */
	width: 1%; 			
	height: 50px;	     /*stessa altezza del contenitore*/
	vertical-align: middle;
	color: #FFFFFF;
	text-align: center;
}
nav.footer > a.active{
	background: #1A397B;
}

Posizioniamo anche il contenuto centrale (tag <article>  avente classe di stile ‘.content’) della view con il valore ‘fixed’ e facciamo in modo che abbia le stesse dimensioni del suo contenitore (nel nostro caso il <body>, quindi della finestra stessa), impostando un padding-top o padding-bottom rispettivamente della stessa altezza di header e footer, nel caso in cui questi siano presenti nel documento allo stesso livello DOM del contenuto.

.content {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  overflow: auto;
  background-color: #4895D9;

  /*abilitiamo lo scrolling nativo*/
  -webkit-overflow-scrolling: touch;
}

/* nel caso in cui nel markup sia presente l'header prima del contenuto attribuiamo un padding-bottom a quest'ultimo */
nav.header ~ .content {
  padding-top: 44px;
}

/* nel caso in cui nel markup sia presente la tabbar prima del contenuto attribuiamo un padding-bottom a quest'ultimo*/
nav.footer ~ .content {
  padding-bottom: 50px;
}

Vediamo ora gli stili da attribuire alle tab dei contenuti.

Ognuna di esse viene posizionata in modo assoluto all’interno del contenitore e, come abbiamo fatto in precedenza, nel caso in cui siano presenti all’interno del documento header o footer, modifichiamo le proprietà top o bottom di ciascuna tab per definire l’area in cui deve essere visualizzato il loro contenuto.

.content .tab{
	position: absolute;
	top: 0;
	left: 0;
	bottom:0;
	right:0;
	visibility: hidden;		/*tutte le tab sono inizialmente nasoste*/
	overflow: hidden;
}

/*anche per le tab modifichiamo la posizione, nel caso in cui siano presenti header e footer*/	
nav.header ~ .content .tab{
	top:44px;
}
nav.footer ~ .content .tab{
	bottom:50px;
}

Impostiamo ora gli stili per “preparare” ogni scheda per l’animazione di entrata e di uscita, definendo su quali proprietà applicare le trasformazioni, la classe della tab correntemente visualizzata, le classi che “scatenano” le animazioni, e dichiarando la dinamica di quest’ultime attraverso l’istruzione “keyframe”:

 css3-slide-hardware-acceleration

.content .tab{
	position: absolute;
	top: 0;
	left: 0;
	bottom:0;
	right:0;
	visibility: hidden;		/*tutte le tab sono inizialmente nasoste*/
	overflow: hidden;

	/*preveniamo il rendering per la parte posteriore della tab, in modo da alleggerire il carico di lavoro */
	-webkit-backface-visibility: hidden;
	-moz-backface-visibility: hidden;
	backface-visibility: hidden;
	/* 
	forziamo la tecnica dell'accelerazione hardware impostando come 'transition' la proprietà 'translate3d', 
	in modo da delegare le animazioni alla GPU (memoria grafica) del dispositivo ed ottenendo un effetto fluido
	*/
	-webkit-transform: translate3d(0, 0, 0);
	-moz-transform: translate3d(0, 0, 0);
	transform: translate3d(0, 0, 0);

	/* facciamo in modo che anche gli elementi che si trovano all'interno di ogni tab mantegano le trasofrmazioni 3d applicate alla tab stessa */
	-webkit-transform-style: preserve-3d;
	-moz-transform-style: preserve-3d;
	transform-style: preserve-3d;
}


/*la tab con classe 'active', ovvero quella che deve essere mostrata diventa visibile e tramite 'z-index' posizionata sopra le altre*/
.content .tab.active{ 
	visibility: visible;
	z-index: 2;
}

/*definiamo la classe per  dare il via all'animazione di uscita della tab da nascondere*/
.exit {
	-webkit-animation: exit .6s ease both;
	-moz-animation: exit .6s ease both;
	animation: exit .6s ease both;
}
/*definiamo la classe per dare il via all'animazione di entrata della tab da visualizzare*/
.enter {
	-webkit-animation: enter .6s ease both;
	-moz-animation: enter .6s ease both;
	animation: enter .6s ease both;
}

/*
dichiarazione delle animazioni di entrata e di uscita per le tab.
Si tratta di animazioni fluide e performanti, in quanto sfruttano la proprietà 'translate' e, siccome funzionano muovendo l’elemento sull’asse delle ascisse, insieme alla definizione della durata dell’animazione danno vita ad un effetto ‘slide’
*/

/* animazione di uscita (dalla posizione attuale, l'elemento viene traslato verso sinistra per una distanza pari alla sua larghezza ) */

@-webkit-keyframes exit {
	to { -webkit-transform: translateX(-100%); }
}
@-moz-keyframes exit {
	to { -moz-transform: translateX(-100%); }
}
@keyframes exit {
	to { transform: translateX(-100%); }
}


/* animazione di entrata (dalla posizione attuale, l'elemento viene traslato verso destra per una distanza pari alla sua larghezza ) */

@-webkit-keyframes enter {
	from { -webkit-transform: translateX(100%); }
}
@-moz-keyframes enter {
	from { -moz-transform: translateX(100%); }
}
@keyframes enter {
	from { transform: translateX(100%); }
}

Per approfondire il discorso di transizioni ed animazioni CSS3 impiegate nello sviluppo di interfacce mobile e fluide e performanti, ti consiglio di leggere questi due articoli:

La gestione della tab-bar attraverso il Javascript

Una volta definito il markup e gli stili necessari per il posizionamento dei vari elementi nel documento, e quelli per le animazioni delle schede della tab-bar, non ci resta che gestire il click dei pulsanti presenti nel footer  in modo da innescare le animazioni di passaggio da una scheda all’altra.

Come prima cosa grazie a  Modernizr riprendiamo il nome dell’evento di fine-animazione in base al browser usato dall’utente, che ci servirà  per richiamare una funzione quando l’effetto è giunto al termine:

 

/* Grazie a modernizr riprendo il nome dell'evento di fine animazione, che cambia a seconda del browser */

var animEndEventNames = {
	'WebkitAnimation' : 'webkitAnimationEnd',
	'OAnimation' : 'oAnimationEnd',
	'msAnimation' : 'MSAnimationEnd',
	'animation' : 'animationend'
};
var animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ];

Poi rendiamo attivo il primo pulsante del footer e visibile la prima scheda, aggiungendo ad entrambi gli elementi la classe ‘active’:

//inizialmente impostiamo come attiva la prima tab
$(".footer a:first, .content .tab:first").addClass("active");

Il passo successivo sarà quello di gestire l’evento di ‘click’ su ogni pulsante del footer:

//inizialmente impostiamo come attiva la prima tab
$(".footer a:first, .content .tab:first").addClass("active");

//variabile per determinare se vi è un'animazione in corso
var animating = false;

//init dei pulsanti della tab
$(".footer a").on("click", function(e){
	e.preventDefault();
	if(animating) //se vie è già un'animazione in corso, annulliamo l'operazione
		return

	//definizione delle variabili
	var btn = $(this),			//pulsante
		tab = btn.attr("href");		//selettore della tab da mostrare
		currentTab = $(".content .tab.active"), //elemento jquery della tab da nascondere
		targetTab = $(".content").find(tab),	//elemento jquery della tab da visualizzare
		endCurrentAnimation = false,			//variabile per determinare se l'animazione della tab di uscita è giunta al termine
		endTargetAnimation = false;				//variabile per determinare se l'animazione della tab di entrata è giunta al termine


	//aggiungiamo la classe active al pulsante appena cliccato e la togliamo agli altri
	btn.addClass("active").siblings().removeClass("active");

	//prima di dare il via all'animazione di entrata impostiamo a 'true' la variabile di controllo
	animating = true;

	//funzione da richiamare solo quando entrambe le animazioni di uscita e di entrata sono  giunte al termine
	var onEnd = function(){
		//rimuoviamo la classe per l'animazione e quella dell'elemento corrente alla div di uscita
		currentTab.removeClass("exit").removeClass("active");	    

		//rimuoviamo la classe per l'animazione alla div di entrata ed aggiungiamo quella per l'elemento corrente
		targetTab.removeClass("enter").addClass("active");	 

		//visto che le animazioni sono giunte al termine, resettiamo la variabile per il controllo dell'animazione   			
		animating = false;
	};	    		


	//inneschiamo l'animazione di uscita ed eseguiamo una funzione al termine della stessa
	currentTab.addClass( 'exit' ).one( animEndEventName, function() {

		//aggiorniamo la variabile per il controllo di fine animazione di uscita
		endCurrentAnimation = true;
		//se anche l'animazione di entrata è terminata, richiamo la funzone 'onEnd'
		if( endTargetAnimation )
			onEnd(); 					
	});

	//aggiugiamo la classe 'active' per rendere visibile la tab da mostrare, scateniamo l'animazione di entrata 
	//ed eseguiamo una funzione al termine della stessa
	targetTab.addClass("active").addClass('enter').one( animEndEventName, function() {
		//aggiorniamo la variabile per il controllo di fine animazione di entrata
		endTargetAnimation = true;
		//se anche l'animazione di uscita è terminata, richiamo la funzone 'onEnd'
		if( endCurrentAnimation ) 
			onEnd();
	});

	
});

HTML Mobile App Tabbar

In altre parole abbiamo agito secondo questa sequenza:

  1. aggiungiamo la classe ‘active’ al pulsante appena cliccato e la rumuoviamo dagli altri

  2. impostiamo a ‘true’ la variabile ‘animating’ che ci serve per evitare di innescare un’animazione mentre un’altra è già in corso

  3. aggiungiamo la classe ‘active’  anche alla scheda che deve essere mostrata

  4. aggiungiamo alla scheda di uscita e quella di entrata rispettivamente le classi ‘exit’ ed ‘enter’, in modo da innescare entrambe le animazioni

  5. attraverso il metodo jQuery ‘.one(),’ che ci permette di eseguire una funzione per un evento solo la prima volta che questo si verifica, ci assicuriamo che l’ultima delle animazioni di entrata o di uscita a giungere al termine, esegua la funzione ‘onEnd’

  6. Grazie alla funzione ‘onEnd’ rimuoviamo le classi ‘exit’ ed ‘enter’ alle schede appena animate in modo tale che esse siano disponibili per un’ulteriore animazione, rimuoviamo la classe ‘active’ dalla scheda appena nascosta, ed impostiamo il valore ‘false’ per la variabile di controllo ‘animating’.

Ecco finalmente la nostra HTML5 Mobile App di base con tanto di Tab-bar al suo interno; puoi vedere la demo o scaricare i sorgenti direttamente dai seguenti link:

livedownload-now

Conclusioni

Utilizzando questa tecnica unita a molte altre ho creato per i miei clienti oltre 10 applicazioni mobile, tra cui due progetti personali: Tint Weather App e Dieta SI o NO?.

Se anche tu sei un web designer e vuoi realizzare la tua applicazione mobile da distribuire nei vari stores, forse sarai interessato al mio e-book: HTML Mobile Accelerato.

Grazie ad esso verrai a conoscenza di tutte quelle tecniche che renderanno la tua mobile app fluida e reattiva come quelle native.

E tu cosa ne pensi? Hai mai usato queste tecniche per realizzare una applicazione mobile in HTML5, CSS3 e Javascript?

Se hai dei suggerimenti e consigli da aggiungere lascia un tuo commento!

L'autore

Web Designer Freelance e Developer (conosciuto anche come upCreative), si occupa del design e dello sviluppo di applicazioni web dal 2008; è abituato a gestire più ruoli e spaziare su più campi, ma le sue passioni principali sono realizzare interfacce in html5 e CSS3 e testare continuamente nuove tecniche di content marketing. Sta scrivendo un e-book su come realizzare mobile app (https://www.upcreative.net/html-mobile-accelerato/)

Sito web dell'autore | Altri articoli scritti da

Articoli correlati

Potresti essere interessato anche ai seguenti articoli:

1 commento

Trackback e pingback

  1. Come realizzare la view di una HTML Mobile App ...
    […] Finora abbiamo parlato di performance e fluidità di applicazioni mobile, in particolare di quali tecniche e consigli adottare per…