Termes CSS clés employés : linear-gradient, border-radius, transform:rotate(), animation.
Compatibilité navigateurs : IE10+, Firefox16+, Chrome et Safari avec préfixe, Opera12.1+.
Obtenir dans un menu un effet de rebond d’un élément en cas de survol par la souris.
Rebond et survol, deux termes qui sous-entendent le recours à des transitions ou à des animations (ce que nous allons employer ici) et à la pseudo-classe :hover… Pour bien matérialiser l’effet de rebond, l’élément sera doté d’un reflet.
Le code HTML est composé d’un élément DIV conteneur qui renferme le contenu (quelconque, mais ici un élément IMG). Chaque élément IMG est doublé afin de créer le reflet. Un dernier élément DIV vide sera employé pour créer l’effet d’éclaircissement progressif sur les reflets.
<DIV class="menu">
<DIV class="rebond">
<IMG src="adresse_image1"><IMG src="adresse_image1">
</DIV>
…
<DIV class="rebond">
<IMG src="adresse_imagen"><IMG src="adresse_imagen">
</DIV>
<DIV class="reflet"></DIV>
</DIV>
Comme précédemment, puisqu’il s’agit d’un menu, l’image devrait être placée dans une ancre A pour pointer vers une autre page… La structure serait alors :
<DIV><A url="adresse de la page">
<IMG src="adresse_image">
</A>
</DIV>
Cela ne change toutefois rien à ce qui suit.
Comme d’habitude, le code CSS commence par définir les propriétés de l’élément conteneur :
div.menu{
position:relative;
width:360px;
height:165px;
margin:30px auto;
border: 1px solid black;
border-radius:5px;
background:linear-gradient(black, white);
perspective:500px;
transform-style:preserve-3d;
}
Il est retenu arbitrairement une hauteur de 165px. La largeur dépend de celle des éléments enfants : nous y reviendrons donc plus tard. Passons aux éléments du menu, que nous allons définir maintenant :
DIV.menu DIV.base{
float:left;
margin:7px;
width:55px;
height:55px;
}
Rien de particulier, si ce n’est la définition comme flottant à gauche et les dimensions sur lesquelles nous reviendrons. Les éléments IMG qui vont renfermer les icônes du menu sont plus intéressants :
DIV.menu DIV IMG {
position:relative;
display:inline-block;
margin-top:30px;
width :50px;
height:50px;
border: 1px solid grey;
border-radius: 5px;
transform:rotate(-22deg);
}
Les images sont définies avec une position relative, comme blocs en ligne. Elles sont ici des carrés de 50 pixels de côté et subissent une rotation de 22 degrés. Cela signifie que leur largeur et leur hauteur apparentes seront en réalité de cosinus(22 degrés) + sinus(22 degrés), soit 65,1 pixels. Cela explique les dimensions retenues pour l’élément DIV.base, soit 55 pixels de largeur plus une marge totale de 2 * 7 pixels, soit au total 69 pixels : il sera observé un écart apparent de 4 pixels entre les éléments du menu. Cela donne également la largeur totale du conteneur (le DIV.menu) : ici cinq éléments, donc 5 x 69 = 345 pixels.
La hauteur totale d’un élément image sera donc de 65 plus les 30 pixels de marge supérieure soit 95 pixels.
Passons au reflet. Il s’agit systématiquement du deuxième enfant IMG d’un DIV menu, si bien qu’il est employé le pseudo-élément nth-child(2). On lui applique une rotation de 180 degrés sur l’axe des X, de –22 degrés sur l’axe des Y, pour qu’il semble être la réflexion des images précédentes, ainsi qu’une réduction de taille et une légère translation vers la droite et vers le bas pour bien ajuster les coins correspondants de chaque image et de son reflet.
DIV.menu DIV IMG:nth-child(2) {
margin-top:0px;
transform: rotateX(180deg) rotate(-22deg) scale(.9) translate(3px, -9px);
}
Vient maintenant ce qui va finaliser l’aspect des images réfléchies : en pratique simplement un dégradé semi-transparent occupant le bas de l’élément menu. Il est placé juste en dessous des images de base, donc à 95 pixels du haut. La hauteur totale du menu étant de 165 pixel, il devra donc mesurer 70 pixels.
DIV.menu DIV.reflet{
position: absolute;
margin-top:95px;
margin-left:0px;
width:360px;
height:70px;
border-radius: 5px;
background: linear-gradient(rgba(255,255,255,1), rgba(250,250,250,.8) 20%, rgba(0,0,0,.3));
}
Voici l’aspect de notre menu, encore statique à ce stade.
Vient la partie la plus subtile. Plutôt que de déclencher l’animation sur un survol d’une icône de base, nous choisissons de le faire sur l’élément DIV.base qui contient l’icône. Pour identifier l’icône, il suffit alors d’employer le sélecteur IMG:nth-child(1).
DIV.base:hover IMG:nth-child(1) {animation: rebond 0.35s ease infinite alternate; }
Enfin, la définition de l’animation elle-même. Nous allons en pratique modifier simultanément la marge haute de l’icône et sa marge basse, de façon inverse : l’une augmente lorsque l’autre diminue. Comme toutefois l’icône doit rebondir ici plus haut que son reflet ne descend. La détermination des chiffres nécessaires peut sembler assez peu intuitive.
Tout d’abord, il semble évident que la marge supérieure doit évoluer entre 30 pixels (la valeur de départ) et 0 (avec l’icône tout en haut de son conteneur). C’est ce qui détermine la suite….
Lorsque l’icône est à sa position d’origine, la marge basse est de 0 pixels. Le premier point est déterminé. En revanche, lorsque l’icône est tout en haut, le bas de son reflet doit se situer tout en bas du conteneur. Théoriquement, sachant que la hauteur d’une image après rotation est de 65 pixels et que la hauteur totale du conteneur menu est de 165, nous devrions avoir une marge basse pour l’icône de 165 – (2* 65) = 45 pixels.
Hélas… Comme le reflet a subi des rotations supplémentaires (plus une modification de taille), ça ne fonctionne pas parfaitement. Et seuls des essais ont permis d’arriver à la valeur de 39 px…
@keyframes rebond{
from {margin-top: 0px;
margin-bottom:39px;}
to {margin-top: 30px;
margin-bottom:0px;}
}
La solution précédente marche parfaitement, mais pourquoi ne pas créer d’effet de réflexion sur un sol vu obliquement ? Rien de très compliqué, si ce n’est qu’il va falloir faire pivoter la partie inférieure du menu.
Le code HTML reste strictement identique à celui de l’exemple précédent.
La définition du DIV conteneur est analogue à la précédente, si ce n’est la hauteur est réduite à 95 pixels, l’arrondi des coins appliqué uniquement en haut et le dégradé va du noir vers le gris plutôt que vers le blanc : cet élément représente donc ici uniquement la partie supérieure (supposée verticale) du menu.
DIV.menu{
…
height:95px;
border-radius:5px 5px 0px 0px;
background:linear-gradient(black, grey);
}
Les éléments DIV de classe base, les éléments IMG et IMG:nth-child(2) sont strictement identiques à la ceux de la version précédente. Le calcul des largeurs s’effectue comme précédemment.
Le reflet va en revanche différer du précédent : nous lui attribuons une bordure, arrondie uniquement en bas, puis lui appliquons une déformation et une translation horizontale de –16 pixels (chiffre obtenu par essais) :
DIV.menu DIV.reflet{
…
border: 1px solid black;
border-radius:0px 0px 5px 5px;
transform:skew(-22deg) translateX(-16px);
}
Voici l’aspect du menu à ce stade :
Nous définissons ensuite non plus une mais deux animations : en effet, le reflet doit subir une translation progressive sur la gauche afin de respecter l’effet 3D, si bien que nous ne pouvons plus nous contenter de jouer uniquement sur la première image et ses marges :
div.base:hover IMG:nth-child(1) {animation: rebond 0.35s ease infinite alternate; }
div.base:hover IMG:nth-child(2) {animation: rebond2 0.35s ease infinite alternate; }
L’animation de l’icône elle-même est identique à la précédente :
@keyframes rebond{
from {margin-top: 0px;
margin-bottom:39px;}
to {margin-top: 30px;
margin-bottom:0px;}
}
Nous ajoutons une seconde animation pour le reflet, avec une légère translation à gauche (grâce à la marge gauche) :
@keyframes rebond2{
from {margin-left: -6px;}
to {margin-left: 0px;}
}
La valeur extrême du décalage, -6px, est simplement le sinus de l’angle (22°) multiplié par le décalage du reflet (16 pixels).
Dernière mise à jour : vendredi 19 Février 2021
|
![]() |
---|