
guide
Tutti i linguaggi per diventare uno sviluppatore di app per Android.
Un primo approccio alla creazione da zero di uno slideshow che possa essere sfruttato anche su dispositivi mobili.
In questo articolo mostreremo come creare una esperienza di navigazione orizzontale dei contenuti, miscelando la voglia di riprodurre una user experience simile a quella offerta dagli slideshow o dai tablet, alla necessità di caricare contenuti in modo tradizionale. In altre parole l’idea è di “sfogliare il sito” caricando una pagina nuova ad ogni transizione.
Affronteremo la questione in tre parti:
La caratteristica principale dello slideshow quella di rappresentare un contenitore che si estende su tutta la pagina.
Per non lasciare l’utente disorientato, utilizziamo un menu di navigazione e associeremo ad ogni pagina del sito una voce del menu. La voce di menu corrispondente alla slide visualizzata sarà evidenziata con un differente colore sia per il testo che per lo sfondo.
Il nostro slideshow quindi si comporrà dei seguenti elementi:
Il contenitore principale è centrato nella pagina e al suo interno le slide verranno disposte una accanto all’altra mediante l’interazione tra CSS e jQuery.
I pulsanti dello slideshow saranno posizionati in modo assoluto ai lati della pagina e centrati verticalmente. Vediamo ora la struttura HTML del nostro slideshow:
<div id="site"> <div id="nav"> <ul id="navigation"> <li><a href="#home" class="current" data-rel="0">Home</a></li> <li><a href="#articoli" data-rel="1">Articoli</a></li> <li><a href="#about" data-rel="2">About</a></li> </ul> </div> <div id="content"> <div id="content-wrapper"> <div class="page" id="home"><!-- prima slide --></div> <div class="page" id="articoli"><!-- seconda slide --></div> <div class="page" id="about"><!-- terza slide --></div> </div> <a href="#" id="previous">Prec.</a> <a href="#" id="next">Succ.</a> <img src="img/loading.gif" alt="" id="loader"/> </div> </div>
Abbiamo aggiunto anche un’immagine GIF che fungerà da immagine di preload ogni volta che viene selezionata una pagina. Le voci del menu di navigazione sono legate alle slide in due modi:
href
che corrisponde all’ID della slide.data-rel
che in questo caso corrisponde all’indice della slide.Questo legame ci semplificherà la vita quando definiremo il codice jQuery.
In un caso reale di navigazione orizzontale tra le categorie di un sito, cose come la definizione dei contenuti o la presenza di elementi come il menu di navigazione possono subire profondi cambiamenti: i contenuti potrebbero essere presi in modo dinamico dal CMS e il menu di navigazione integrato direttamente nel contenuto ad esempio. In questo momento però ci interessa mostrare un comportamento di base e semplificare tutto il resto.
Nel nostro layout ci sono sia elementi posizionati in modo statico (nel flusso del documento) che elementi posizionati in modo assoluto (fuori dal flusso del documento). A metà di questi due contesti di formattazione si pongono i contenuti (le slide), che verranno flottati all’interno del contenitore.
Se ogni slide è larga 800 pixel, tre slide richiedono 2400 pixel di spazio. Ovviamente nessuno schermo ha questa risoluzione, quindi si impiega una tecnica fondamentale in ogni slideshow: il contenitore più esterno delle slide (#content
) avrà la dichiarazione overflow:hidden
. Tale dichiarazione ci permette di mostrare solo 800 pixel per volta, sopprimendo la visualizzazione del contenuto in eccesso.
Ma il cuore di ogni slideshow sta nel contenitore più interno (#content-wrapper
), la cui larghezza totale verrà impostata sommando le larghezze di tutte le slide. Questo farà in modo che le slide si dispongano su un’unica riga, ma ha anche un altro effetto importante: impostando infatti position: relative
su questo contenitore, non solo gli permetteremo di scorrere da destra a sinistra, ma faremo anche in modo che ciascuna slide abbia un offset sinistro definito che potremo usare con jQuery per far muovere il contenitore secondo la distanza necessaria a mostrare ciascuna slide.
Partiamo dalle dichiarazioni più generali:
* { margin: 0; padding: 0; } html, body { width: 100%; height: 100%; min-height: 100%; } body { background: #fff; color: #000; font: 90% Arial, sans-serif; } h1 { font-size: 2em; font-family: Georgia, serif; font-weight: normal; color: #08c; margin-bottom: 0.3em; } p { margin-bottom: 1em; line-height: 1.4; }
Abbiamo resettato i margini e il padding di tutti gli elementi in modo da non dovere dichiararli ogni volta. Quindi abbiamo fatto in modo di avere la pagina a piena larghezza ed altezza usando le proprietà width
, height
e min-height
.
A questo punto possiamo passare a definire il contenitore più esterno:
#site { width: 100%; }
Quindi tocca al menu di navigazione superiore. Si tratta di un semplice menu orizzontale con le singole allineate tramite display: inline
:
#nav { height: 3em; background: #000 url(img/menu.gif) repeat-x 0 0; } #nav ul { width: 100%; list-style: none; height: 100%; text-align: center; line-height: 3; } #nav li { display: inline; margin-right: 1em; } #nav a { color: #f96; padding: 0.4em 0.6em; text-decoration: none; text-transform: uppercase; letter-spacing: 0.1em; } #nav a:hover { color: #fff; } #nav a.current { background: #f96; color: #fff; }
La classe .current
verrà assegnata dinamicamente da jQuery ogni volta che si seleziona la slide corrente.
Passiamo ora al contenitore più esterno delle slide:
#content { margin: 2em auto; width: 100%; max-width: 800px; overflow: hidden; }
Usando una larghezza in percentuale e una larghezza massima in pixel facciamo in modo che il contenitore si adatti alla finestra del browser. Se c’è spazio disponibile, allora la larghezza sarà di 800 pixel, altrimenti inferiore.
Quindi tocca al contenitore più interno:
#content-wrapper { position: relative; }
La larghezza e l’altezza massima di questo elemento verranno fissate dinamicamente da jQuery in base al numero di slide e all’altezza massima del contenuto presente in esse. Quindi definiamo gli stili per ciascuna slide:
div.page { margin: 0; float: left; width: 800px; position: relative; }
La larghezza di ciascuna slide deve essere uguale alla larghezza massima del contenitore più esterno. Ora non resta che posizionare in modo assoluto i due pulsanti e la GIF animata del preload:
#previous, #next { width: 40px; height: 40px; position: absolute; text-indent: -9999px; } #previous { left: 0; background: url(img/arr-left.png) no-repeat; } #next { right: 0; background: url(img/arr-right.png) no-repeat; } #loader { position: absolute; top: 50%; left: 50%; margin: -16px 0 0 -16px; display: none; }
Abbiamo detto che i due pulsanti devono essere centrati verticalmente, ma sui dispositivi mobili c’è un problema: la centratura viene persa quando si ruota il dispositivo. Deleghiamo quindi questo compito a jQuery che centrerà i due elementi intercettando il cambio di orientamento del dispositivo.
La nostra GIF è invece centrata sia orizzontalmente che verticalmente tramite una nota tecnica CSS: proprietà top
e left
su 50% e margine superiore e sinistro impostati su un valore negativo pari alla metà dell’altezza e della larghezza (la GIF è di 32×32 pixel).
L’immagine viene nascosta perchè a noi interessa che appaia per un certo periodo di tempo solo quando si seleziona una slide.
Useremo un approccio object-oriented per il nostro codice. Vogliamo infatti scrivere del codice che possa essere riutilizzato in più contesti modificando solo alcune impostazioni di base.
Il nostro oggetto dovrà avere i seguenti componenti:
Ecco quindi la struttura di base:
var Slides = { Elements: { // Elementi dello slideshow }, Utils: { // Metodi di utilità generale }, fn: { // Core }, init: function() { // Inizializzazione } }; $(function() { Slides.init(); });
Definiamo gli elementi comuni come proprietà dell’oggetto Elements
:
Elements: { links: $('a', '#navigation'), // links menu di navigazione pages: $('div.page', '#content'), // slide previous: $('#previous'), // bottone "Indietro" next: $('#next'), // bottone "Avanti" loader: $('#loader'), // spinner GIF per il preload wrapper: $('#content-wrapper') // contenitore interno delle slide }
Attenzione a quando si utilizzano oggetti letterali. Se ad esempio scrivete:
var obj = { test: $('#test'), list: $('ul', this.test) // Errore! };
this
in questo caso essendo contenuto nell’oggetto jQuery()
punta a jQuery
e non all’oggetto obj
. Infatti l’interprete JavaScript proverà ad accedere alla proprietà test
dell’oggetto jQuery
che è ovviamente undefined
.
Questo invece è corretto:
var obj = { test: $('#test'), list: $('ul', obj.test) // Funziona };
Il primo metodo di utility che definiremo è quello che controlla il movimento delle slide, il preload e l’evidenziazione del link corrente nel menu di navigazione superiore.
Il cuore dello slideshow un contatore (definito più avanti nell’oggetto core) che verrà incrementato o decrementato di 1 a seconda che venga premuto il bottone “Avanti” o “Indietro”.
Ovviamente bisogna verificare ad ogni clic che l’indice del contatore non sia inferiore a 0 o superiore al numero massimo di slide contenute nello slideshow.
Il nostro metodo di utility si occupa di:
current
rimuovendola al contempo da tutti gli
altri link.Utils: { doSlide: function(params) { params = $.extend({ container: Slides.Elements.wrapper, a: Slides.Elements.links, spinner: Slides.Elements.loader, pages: Slides.Elements.pages, button: 'next' }, params); params.pages.eq(Slides.fn.index). animate( { opacity: 0.5 }, 600, function() { params.spinner.show(); setTimeout(function() { params.spinner.hide(); if (params.button == 'next') { Slides.fn.index++; } else { Slides.fn.index--; } params.container.animate( { left: -params.pages.eq(Slides.fn.index).position().left }, 1000, 'linear', function() { params.a.eq(Slides.fn.index).addClass('current'). parents('ul').find('a').not(params.a.eq(Slides.fn.index)). removeClass('current'); } ); }, 1000); } ); } }
Lavoriamo con il metodo .eq()
a cui passiamo l’indice generato dal contatore, che ovviamente verrà incrementato o decrementato a seconda se si clicca sul bottone “Avanti” o “Indietro”.
Il secondo metodo da definire è quello che centra verticalmente i bottoni “Avanti” e “Indietro”. Si tratta di dividere per due l’altezza della finestra del browser e sottrarre al valore ottenuto la metà dell’altezza dei bottoni:
positionButtons: function() { var prev = Slides.Elements.previous; var next = Slides.Elements.next; var totalHeight = $(window).height(); prev.css({ top: (totalHeight / 2) - (prev.height() / 2) }); next.css({ top: (totalHeight / 2) - (next.height() / 2) }); }
Il metodo core contiene i componenti fondamentali dello slideshow, come l’indice incrementale e i metodi per lo sliding. Il primo metodo che andremo a definire, insieme al contatore, serve a dare le dimensioni al contenitore interno delle slide:
fn: { index: 0, setWidths: function() { var totalHeight = Math.max(Slides.Elements.pages.outerHeight()); var totalWidth = Slides.Elements.pages.eq(0).width() * Slides.Elements.pages.length; $('#content').css({ height: totalHeight }); Slides.Elements.wrapper.css({ width: totalWidth, height: totalHeight }); } }
Usiamo Math.max()
per calcolare l’altezza massima delle slide. Quindi per ottenere la larghezza complessiva moltiplichiamo la larghezza di una slide per il numero totale di slde. Quindi impostiamo l’altezza e la larghezza del contenitore interno su questi valori.
A questo punto associamo due metodi ai bottoni “Avanti” e “Indietro”:
nextBtn: function() { var btn = Slides.Elements.next; var slides = Slides.Elements.pages; btn.click(function(evt) { evt.preventDefault(); slides.css('opacity', 1); if (Slides.fn.index >= 0) { if (Slides.fn.index == (slides.length - 1)) { Slides.fn.index = slides.length - 1; Slides.Elements.previous.click(); return; } Slides.Utils.doSlide(); } }); }, prevBtn: function() { var btn = Slides.Elements.previous; var slides = Slides.Elements.pages; btn.click(function(evt) { evt.preventDefault(); slides.css('opacity', 1); if (Slides.fn.index >= 0) { if (Slides.fn.index < 0 || Slides.fn.index == 0) { Slides.fn.index = 0; Slides.Elements.next.click(); return; } Slides.Utils.doSlide({ button: 'previous' }); } }); }
Ciascun metodo prima reimposta la corretta opacità su tutte le slide, quindi verifica che l’indice non sia inferiore a 0 o superiore al numero di slide. Se ciò dovesse accadere, ciascun metodo resetta l’indice e invoca il metodo opposto. Quindi viene utilizzato il metodo doSlide()
visto in precedenza.
L’ultimo metodo dell’oggetto core gestisce il menu di navigazione superiore mostrando la slide collegata a ciascun link:
navigationMenu: function() { var links = Slides.Elements.links; var wrapper = Slides.Elements.wrapper; var slides = Slides.Elements.pages; links.each(function() { var $a = $(this); var slide = $($a.attr('href')); var $index = $a.attr('data-rel'); $a.click(function(evt) { slides.css('opacity', 1); evt.preventDefault(); Slides.fn.index = $index; wrapper.animate({ left: -slide.position().left }, 1000, 'linear', function() { $a.addClass('current').parents('ul'). find('a').not($a).removeClass('current'); }); }); }); } }
Questo metodo fa una cosa importante:
var $index = $a.attr('data-rel'); Slides.fn.index = $index;
Utilizzando l’indice di ciascun link memorizzato nell’attributo data-rel
e impostandolo sull’indice dello slideshow si fa in modo che quando l’utente clicca sul link del menu e poi torna a cliccare sui bottoni, lo scorrimento riparta dalla slide collegata al link, senza perdere la sequenza.
Caricare tutti i metodi dell’oggetto fn
è qualcosa che può essere automatizzato usando un loop for...in
e lanciando solo quei membri che sono funzioni:
init: function() { for (var property in this.fn) { if (typeof this.fn[property] === 'function') { this.fn[property](); } } }
Come abbiamo detto, la centratura verticale dei bottoni si perde sui dispositivi mobile quando l’utente ruota il dispositivo. Per questa ragione usiamo l’evento orientationchange
dell’oggetto window
associandogli il nostro metodo di utility positionButtons()
:
$(function() { Slides.init(); if (window.orientation) // Siamo su mobile? { $(window).bind('orientationchange', function() { Slides.Utils.positionButtons(); }); } });
Potete intanto visionare il nostro esempio in questa pagina.
Esaminiamo ora come aggiungere un effetto swipe al nostro slideshow per i dispositivi mobile. Lo swipe non esiste come evento DOM sui dispositivi mobile, ma è la combinazione degli eventi touchstart
, touchmove
e touchend
. Il movimento è quello che si effettua con un dito verso destra o sinistra per sfogliare o far scorrere le pagine.
Implementare questo effetto da zero è molto difficile, ma fortunatamente esistono plugin jQuery come touchSwipe che riescono nell’intento. Useremo questo plugin per il nostro slideshow.
Useremo le due action di Touchswipe chiamate swipeLeft
e swipeRight
per intercettare lo swipe sull’intero blocco contenitore (ossia #site
). Alla prima action assoceremo l’azione registrata sul bottone “Indietro” e alla seconda quella sul bottone “Avanti”.
Per farlo aggiungeremo un altro metodo di utility all’oggetto Utils
:
swiping: function() { $('#site').swipe({ swipeLeft: function(event, direction, distance, duration, fingerCount) { Slides.Elements.previous.click(); }, swipeRight: function(event, direction, distance, duration, fingerCount) { Slides.Elements.next.click(); } }); }
Quindi invochiamo questo metodo se siamo su mobile in fase di inizializzazione:
$(function() { Slides.init(); if (window.orientation) // Siamo su mobile? { $(window).bind('orientationchange', function() { Slides.Utils.positionButtons(); }); Slides.Utils.swiping(); } });
Potete vedere il nostro esempio completo in questa pagina.
L’esempio completo è allegato all’articolo.
Nelle parti precedenti abbiamo analizzato il codice jQuery necessario per implementare un effetto swipe sui dispositivi mobile. In questa parte vedremo come aggiungere AJAX al nostro slideshow e realizzare finalmente la navigazione orizzontale delle pagine.
Anzitutto dobbiamo modificare la struttura del markup HTML eliminando i contenuti di tutte le slide eccetto la prima (che potrebbe essere la nostra home page):
<div class="page" id="home">
<h1>Home</h1>
<!-- continua -->
</div>
<div class="page" id="articoli"></div>
<div class="page" id="about"></div>
I contenuti saranno infatti recuperati via AJAX con una richiesta GET. Si possono immaginare diversi modi per sfogliare l’elenco delle pagine, noi sfrutteremo una semplice query string del tipo page=numero
.
In un ambiente di produzione il nostro script PHP userebbe il numero di pagina passato tramite AJAX per reperire i contenuti dal database. Nel nostro esempio ci limiteremo ad usare un array associativo per simulare questa interazione:
<?php
header('Content-Type: text/html');
$page = $_GET['page'];
if(ctype_digit($page) && strlen($page) == 1) {
$pageNum = $page;
} else {
exit();
}
$htmlContents = array(
'...', // primo contenuto
'...', // secondo contenuto
'...' // terzo contenuto
);
echo $htmlContents[$pageNum];
Lo script imposta subito il tipo di contenuto su text/html
. Meglio impostare il content type in modo esplicito per evitare che il server lo imposti in modo arbitrario.
C’è una elementare validazione dei dati: se il parametro GET page
è un numero e la sua lunghezza è di 1 carattere, allora viene impostata la variabile $pageNum
. Altrimenti lo script si interrompe. A questo punto occorrerebbe restituire un errore.
Alla fine ritorniamo il contenuto relativo al numero di pagina scelto e associato alla posizione nell’array.
Dobbiamo modificare il metodo doSlide()
inserendo una chiamata al metodo $.get
prima che la pagina scorra:
Utils: {
doSlide: function(params) {
params = $.extend({
container: Slides.Elements.wrapper,
a: Slides.Elements.links,
spinner: Slides.Elements.loader,
pages: Slides.Elements.pages,
button: 'next'
}, params);
params.pages.eq(Slides.fn.index).
animate({
opacity: 0.5
}, 600, function() {
params.spinner.show();
setTimeout(function() {
params.spinner.hide();
if (params.button == 'next') {
Slides.fn.index++;
} else {
Slides.fn.index--;
}
$.get('ajax.php', {page: Slides.fn.index}, function(html) {
params.pages.eq(Slides.fn.index).html(html);
});
params.container.animate({
left: -params.pages.eq(Slides.fn.index).position().left
}, 1000, 'linear', function() {
params.a.eq(Slides.fn.index).addClass('current').
parents('ul').find('a').not(params.a.eq(Slides.fn.index)).
removeClass('current');
});
}, 1000);
});
},
//...
}
Ecco la nostra modifica:
$.get('ajax.php', {page: Slides.fn.index}, function(html) { params.pages.eq(Slides.fn.index).html(html); });
La query string GET viene passata come oggetto letterale che jQuery convertirà nel canonico page=numero
. In questo caso il numero di pagina corrisponde all’indice corrente della slide. Quindi la successiva funzione anonima riceve come parametro la stringa HTML restituita dal server con cui andiamo a creare il contenuto della slide.
Ora non ci resta che modificare il codice per il menu di navigazione superiore:
navigationMenu: function() { var links = Slides.Elements.links; var wrapper = Slides.Elements.wrapper; var slides = Slides.Elements.pages; links.each(function() { var $a = $(this); var slide = $($a.attr('href')); var $index = $a.attr('data-rel'); $a.click(function(evt) { slides.css('opacity', 1); evt.preventDefault(); Slides.fn.index = $index; $.get('ajax.php', {page: $index}, function(html) { slide.html(html); }); wrapper.animate({ left: -slide.position().left }, 1000, 'linear', function() { $a.addClass('current').parents('ul'). find('a').not($a).removeClass('current'); }); }); }); }
Il codice è pressoché identico a quello visto in precedenza. La differenza sta nel fatto che la slide di riferimento qui viene presa dall’attributo href
del link corrente che, come sappiamo, contiene un’ancora che punta alla slide.
Potete vedere l’esempio completo in questa pagina e il codice completo è nell’allegato.
Se vuoi aggiornamenti su Slideshow e 'navigazione orizzontale' con jQuery inserisci la tua email nel box qui sotto:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
La tua iscrizione è andata a buon fine. Se vuoi ricevere informazioni personalizzate compila anche i seguenti campi opzionali:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
A volte può capitare di non voler lasciare traccia del proprio passaggio su Facebook, in modo da nascondere ad occhi […]
Tutti i linguaggi per diventare uno sviluppatore di app per Android.
Come creare applicazioni per il Web con PHP e MySQL per il DBMS.
Tutte le principali tecnologie per diventare uno sviluppatore mobile per iOS.
I fondamentali per lo sviluppo di applicazioni multi piattaforma con Java.
Diventare degli esperti in tema di sicurezza delle applicazioni Java.
Usare Raspberry Pi e Arduino per avvicinarsi al mondo dei Maker e dell’IoT.
Le principali guide di HTML.it per diventare un esperto dei database NoSQL.
Ecco come i professionisti creano applicazioni per il Cloud con PHP.
Lo sviluppo professionale di applicazioni in PHP alla portata di tutti.
Come sviluppare applicazioni Web dinamiche con PHP e JavaScript.
Fare gli e-commerce developer con Magento, Prestashop e WooCommerce.
Realizzare applicazioni per il Web utilizzando i framework PHP.
Creare applicazioni PHP e gestire l’ambiente di sviluppo come un pro.
Percorso base per avvicinarsi al web design con un occhio al mobile.
Realizzare siti Web e Web application con WordPress a livello professionale.
Come creare database e collection, inserire, estrarre, aggiornare e rimuovere dati da una base di dati MongoDB con Python
Impariamo ad utilizzare Takamaka, una blockchain Java Full Stack, per scrivere codice Java installabile ed eseguibile su una blockchain
StandardJS è uno strumento in ambiente Node.js, per la definizione ed il controllo di uno standard di codifica in JavaScript.
Come creare un’applicazione per la gestione di un negozio di commercio elettronico con il framework PHP Laravel. Una guida completa che, partendo dalla configurazione di un ambiente di sviluppo basato su Laravel, descrive nel dettaglio tutti i passaggi necessari per pubblicare un e-commerce con cui promuovere e vendere i propri prodotti online