Aujourd’hui nous allons construire un accordéon horizontal avec jQuery, afin de faire défiler des photographies.
Balisage HTML simple, style CSS très simple, dégradation gracieuse, et la possibilité d’avoir plusieurs instances sur une page.
Le script original
En fouillant sur le net à la recherche d’un script déjà existant, je suis d’abord tombé sur Horizontal Accordion 2.0 for jQuery qui paraissait parfait pour ce que je voulais faire. Je me suis vite retrouvé confronté à un problème de taille: malgré tous mes essais, le script ne semble pas fonctionner avec jQuery 1.4. Finalement, j’ai trouvé un script d’accordéon horizontal très simple et facilement modifiable selon mes besoins.
01020304050607080910111213
$(document).ready(function(){
lastBlock = $("#a1");
maxWidth = 210;
minWidth = 75;
$("ul li a").hover(
function(){
$(lastBlock).animate({width: minWidth+"px"}, { queue:false, duration:400 });
$(this).animate({width: maxWidth+"px"}, { queue:false, duration:400});
lastBlock = this;
}
);
});
Durant mes recherches, j’ai remarqué que ce script est très utilisé, me laissant deviner que ne trouverais probablement pas plus évolué, alors c’est décidé, on va se baser sur celui-là.
Le balisage HTML
Déjà, je voulais quelque chose de simple à mettre en place (par exemple dans un post WordPress ;) ), laissant le script gérer tout dans son coin.
123456
<div class="slide-gallery">
<img src="..." alt="" />
<img src="..." alt="" />
<img src="..." alt="" />
<img src="..." alt="" />
</div>
Une div, à qui on a attribué une class, et nos images à l’intérieur, voilà ce que je cherche à faire. Ainsi, dans l’administration de WordPress je n’ai qu’à créer la div dans le panel « HTML » de mon post et insérer les images via la fenêtre « Media ». Donc out la liste et les liens du script original.
La mise en forme avec CSS
Pour faire simple, il va me falloir une bordure autour de mes photos, et si l’utilisateur a désactivé javascript dans son navigateur, qu’il puisse quand même voir toutes mes photos (car prévoyant de les mettre dans un post et non dans une bannière de header par exemple, cela ne gênera aucunement).
12345678
.slide-gallery {
border-width: 6px 0;
border-color: #000;
border-style: solid;
background-color: #000;
margin-bottom: 20px;
overflow: hidden;
}
Vous remarquerez que je met une bordure seulement en haut et en bas du conteneur. Ceci pour plusieurs raisons: premièrement il nous faudra également une bordure entre chaque image, avec un border-left par exemple, donc si on met une bordure tout autour du conteneur, on aura alors une bordure gauche 2 fois plus épaisse pour la première photo. Seconde raison, si on met une bordure à droite du conteneur, le script n’aura alors plus aucune marge de manœuvre lors des animations sur la largeur des photos, résultat: si la photo en train de s’élargir s’agrandit plus vite que celle qui est en train de « maigrir », la dernière photo du slider va passer à la ligne pendant une fraction de seconde, on aura l’impression qu’elle clignote (oui, il m’a fallut un bon moment pour trouver d’où venait ce problème :D ). Donc pas de bordure à droite, le calcul des largeurs fera le travail. On met également un fond de la même couleur que les bordures, une marge en dessous du conteneur pour ne pas que le texte situé sous le slider se retrouve collé au conteneur, et un overflow pour éviter tout débordement de « population » ;) qui, en théorie, de devrait pas arriver.
0910111213141516
.slide-gallery .item {
float: left;
border-left: solid 6px #000;
-moz-box-shadow:inset 0 0 10px #000;
-webkit-box-shadow:inset 0 0 10px #000;
-khtml-box-shadow:inset 0 0 10px #000;
box-shadow:inset 0 0 10px #000;
}
Nos photos se verront affublées de la class « item » via le script, d’où le sélecteur.slide-gallery .item. Comme prévu, on trouve la bordure gauche, et on rajoute un effet CSS 3 bien sympa: box-shadow, mais avec la valeur inset afin de créer une ombre « à l’intérieur » des photos au lieu de l’extérieur.
Le script jQuery
Première étape, modifier le script original. On part sur les valeurs suivantes: l’accordéon va faire 560px de large, on a 4 photos qui font 410px de large et les onglets feront 40px, le tout en tenant compte des bordures de 6px. Tant qu’on y est, on rentre la classe de l’accordéon dans une variable.
010203040506070809101112
jQuery(document).ready(function($){
slideClass = 'slide-gallery'; // Classe du slider
minWidth = 40; // Largeur mini d'1 image en px
maxWidth = 410; // Largeur max d'1 image par slider
lastBlock = $('.'+slideClass+' img:first');
$('.'+slideClass+' img').hover( function(){
$(lastBlock).stop(true,false).animate({'width': minWidth+"px"}, { queue:false, duration:300 });
$(this).stop(true,false).animate({'width': maxWidth+"px"}, { queue:false, duration:300});
lastBlock = this;
});
});
Au passage, on a rajouté un stop() devant chaque animation.
Bien, c’est un bon début, mais on est loin du résultat final. A ce stade, on n’a même pas l’état de départ de l’accordéon. Voyons donc cela, en même temps on va s’occuper de l’opacité des items et rajouter un « petit truc en plus ». Regardez donc les commentaires pour comprendre ce qui se passe :
01020304050607080910111213141516171819202122232425262728293031
jQuery(document).ready(function($){
slideClass = 'slide-gallery';
itemClass = 'item'; // Classe des items
minWidth = 40;
maxWidth = 410;
minOp = .3; // Opacité des onglets
var bg = new Array; // Tableau des backgrounds de nos items
var ht = new Array; // Tableau des hauteurs de nos items
$('.'+slideClass+' img').each(function(index){ // Pour chaque image
bg[index] = $(this).attr('src'); // On enregistre l'adresse de l'image
ht[index] = $(this).height(); // On enregistre sa hauteur
$(this).wrap('<div class="'+itemClass+'" />') // On l'englobe dans une div.item
.parent('.'+itemClass)
.css({'background-image': 'url('+bg[index]+')', 'height': ''+ht[index]+'px', 'opacity': minOp})
.html('') // On met la photo en background de la div, on fixe sa hauteur et son opacité, puis on supprime la photo
.width(minWidth) // On met l'item à la largeur mini
.parent('.'+slideClass)
.height(ht[0]); // On fixe la hauteur de l'accordéon à la hauteur de la 1ère photo
});
lastBlock = $('.'+slideClass+' .'+itemClass+':first'); // On remplace le sélecteur par nos div
$(lastBlock).css({'width': maxWidth,'opacity': 1}); // On met l'opacité à 1 sur l'item affiché
$('.'+slideClass+' .'+itemClass).hover( function(){ // On remplace le sélecteur par nos div
$(lastBlock).stop(true,false).animate({'width': minWidth+"px", 'opacity': minOp}, { queue:false, duration:300 });
$(this).stop(true,false).animate({'width': maxWidth+"px", 'opacity': 1}, { queue:false, duration:300});
lastBlock = this; // On gère l'opacité de nos items sur les 2 lignes ci-dessus
});
});
Qu’est ce qui s’est passé là-dedans? Pourquoi avoir remplacé les images par des div?
Pour deux raisons, la première étant d’ordre esthétique, et la seconde d’ordre pratique.
Pour l’esthétique:
Déjà, si on avait laissé les images sans même les englober dans une div, et en animant leur largeur, nos onglets auraient été tout simplement horribles. Une image dont la largeur passe à 10% ne va pas donner un résultat génial (et c’est pas propre). Donc on est obligé de « wraper » chaque image dans une div, et d’animer la largeur de la div. En fixant en CSS un overflow: hidden; sur la div ça fonctionnerait très bien. Mais j’ai voulu aller plus loin. Normalement, la partie la plus intéressante d’une photo est au centre, pas sur les côtés. Or, quand l’onglet est rétrécit à 40px de large, on ne verra que le bord gauche de la photo, ce qui est quand même bien léger pour se faire une idée de son contenu. Donc en mettant l’image en background de la div et en fixant sa position à 50% de sa largeur on verra son centre.
Pour le côté pratique:
Nous ne sommes plus obligés d’avoir des photos de 410px de large. Si elles font 40px de plus, il nous manquera juste 20px de chaque côté. Et par la même occasion, nous ne sommes plus bloqués sur le nombre de photos, et donc d’onglets, la largeur des onglets, etc.
Maintenant qu’on y pense, ça serait pas mal de ne pas avoir à calculer la largeur max des images en fonction du nombre d’onglets, de leur largeur, et des bordures :)
Mais aussi, on a oublié un truc important: là où on en est on ne peut avoir qu’un seul accordéon par page.
Bien, réglons tout ça d’un coup!
010203040506070809101112131415161718192021222324252627282930313233343536373839
jQuery(document).ready(function($){
slideClass = 'slide-gallery'; // Classe des sliders
itemClass = 'item'; // Classe des items
slideWidth = 560; // Largeur totale des sliders en px, bordures incluses
brd = 6; // Largeur border en px
minWidth = 40; // Largeur mini d'1 image en px
minOp = .3; // Opacité des onglets
var maxWidth = new Array; // Largeur max d'1 image par slider
var nbrimg = new Array; // Nombre d'images par slider
var lastBlock = new Array; // Block actif par slider
$('.'+slideClass).each(function(i){
nbrimg[i] = 0;
$(this).addClass('slide-'+i)
.children('img')
.each(function(){nbrimg[i]++;}); // Nombre d'images
maxWidth[i] = slideWidth-(2*brd)-((minWidth+brd)*(nbrimg[i]-1));// Largeur max d'1 image
var bg = new Array;
var ht = new Array;
$(this).children('img').each(function(index){
bg[index] = $(this).attr('src');
ht[index] = $(this).height();
$(this).wrap('<div class="'+itemClass+'" />')
.parent('.'+itemClass)
.css({'background-image': 'url('+bg[index]+')', 'height': ''+ht[index]+'px', 'opacity': minOp})
.html('')
.width(minWidth)
.parent('.'+slideClass)
.height(ht[0]);
});
lastBlock[i] = $(this).children('.'+itemClass+':first');
$(lastBlock[i]).css({'width': maxWidth[i],'opacity': 1});
$(this).children('.'+itemClass).hover( function(){
$(lastBlock[i]).stop(true,false).animate({'width': minWidth+"px", 'opacity': minOp}, { queue:false, duration:300 });
$(this).stop(true,false).animate({'width': maxWidth[i]+"px", 'opacity': 1}, { queue:false, duration:300});
lastBlock[i] = this;
});
});
});
Voilà, maintenant nous pouvons mettre plusieurs accordéons par page, et tout ce que nous devrons indiquer au script ce sont la largeur totale de l’accordéon (comme ça il pourra être plus petit que notre zone de texte si on le désire), la taille des bordures (selon ce que l’on a déclaré dans le style CSS), la largeur des onglets et leur opacité. C’est tout! Le script se chargera de calculer le nombre d’images dans l’accordéon, puis il en tiendra compte pour calculer la largeur de la « grande div ».
Un dernier coup de pinceau
Dernière chose, il nous faut rajouter le positionnement des backgrounds dans notre déclaration CSS. Ce qui nous donne au final:
01020304050607080910111213141516171819
.slide-gallery {
border-width: 6px 0;
border-color: #000;
border-style: solid;
margin-bottom: 20px;
background-color: #000;
overflow: hidden;
}
.slide-gallery .item {
float: left;
border-left: solid 6px #000;
background-color: transparent;
background-repeat: no-repeat;
background-position: 50% 0;
-moz-box-shadow:inset 0 0 10px #000;
-webkit-box-shadow:inset 0 0 10px #000;
-khtml-box-shadow:inset 0 0 10px #000;
box-shadow:inset 0 0 10px #000;
}
J’espère que ce tutoriel vous a plu. N’hésitez pas à laisser un commentaire pour me dire ce que vous en pensez, ou si vous avez des améliorations à apporter :)
Commentaires
Commentaire de herbaciarnia.
Thank You for sharing! Really great:)
Commentaire de MAT.
MERCI MEC ET RESPECT
Commentaire de krakot.
Bonjour,
Déjà merci beaucoup pour ce tutoriel , j’ai éssayer en vain de mettre l’accordeon de photo sur une page de mon blog mais la forme change complètement , comment régler la hauteur de chaque photo? Sur ma page les photos ne sont pas alignés mais en forme d’escaliers. Pourriez vous m’aidez? Merci.
url de la page:http://www.mana-isanandro.fr/p/gallerie-de-photos.html pour le fond et la mise en page je peux me débrouiller mais la taille du texte je n’arrive pas à la réduire. Derniére faire en sorte qu’en cliquant sur une photo ça ouvre un lien externe? Merci
Commentaire de Greg.
Bonjour.
Désolé de répondre si tardivement, un petit problème de commentaires mangés par le site.
Pour un meilleur rendu, il vaut mieux que les images aient la même taille. La hauteur du slider est choisie par le javascript, il prend la hauteur de la première image et l’applique au slider.
A priori c’est possible de mettre un lien sur les photos du slider mais il faudrait revoir le code du slider en profondeur pour ça.
A bientôt.
PS : j’arrive trop tard, mon slider a disparu de votre page ;)
Commentaire de powange.
remplacer:
13141516
Par:
1314
Commentaire de Greg.
En effet, il faut croire que j’aimais bien me compliquer la vie à l’époque :) (ça doit être pour ça que j’évite de me replonger dans du vieux code, je trouve toujours des trucs idiots).
Merci :)