Dans ce tutoriel, nous allons essayer de reproduire l'effet de grossissement des icônes du Dock de Mac Os. Pour réaliser cet effet, la principale contrainte que l'on aura est que l'on ne peut utiliser que les transformations CSS car ce sont les propriétés les plus optimisées (elles évitent le recalcul de la structure par le navigateur).
Morceler le problème
Pour arriver au résultat souhaité, il est important de décomposer le problème en plusieurs problèmes plus simples à résoudre. On commencera d'abord par poser la structure de base en utilisant du CSS et les flexbox.
<div class="dock-wrapper">
<div class="dock">
<button class="dock-icon"><img src="images/finder.png" /></button>
<button class=" dock-icon"><img src="images/appstore.png" /></button>
<button class=" dock-icon"><img src="images/mail.png" /></button>
<button class=" dock-icon"><img src="images/messages.png" /></button>
<button class=" dock-icon">
<img src="images/systempreferences.png" />
</button>
<button class=" dock-icon"><img src="images/terminal.png" /></button>
<button class=" dock-icon"><img src="images/trashbin.png" /></button>
</div>
</div>
<style>
.dock-wrapper {
position: absolute;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
}
.dock {
display: flex;
}
.dock-icon {
width: 64px;
height: 64px;
display: flex;
align-items: center;
justify-content: center;
transform-origin: center bottom;
background: transparent;
border: none;
padding: 0;
margin: 0;
}
.dock-icon img {
width: 100%;
height: 100%;
object-fit: contain;
}
</style>
Ensuite, nous allons essayer de déterminer la position du curseur relativement à chaque icône. Afin de créer un système qui soit indépendant de la taille des icônes, nous allons créer un repère ayant comme unité la taille d'une icône.
Cela nous permettra ensuite de connaître la position du curseur en se basant sur la taille d'icône (en bleu sur le schéma). Cette valeur pourra être utilisée dans le calcul du ratio d'agrandissement. En regardant l'effet original, on se rend compte que la première icône est la plus grossie (sa taille ne change pas quelquesoit la position du cuseur) et les icônes situées à côté diminuent en taille progressivement. Pour se simplifier le travail on va représenter ce facteur de grossissement sous forme d'une courbe sur une échelle normalisée.
À partir de cette courbe, on peut créer une fonction mathématique qui nous donnera le facteur de grossissement à appliquer en fonction de la distance par rapport au curseur de la souris. On utilisera les fonctions Math.min()
et Math.max()
afin de limiter les valeurs entre 0 et 1.
function between(val, min, max) {
return Math.max(min, Math.min(val, max));
}
function scaling(d) {
return between(-0.2 * Math.pow(d, 2) + 1.05, 0, 1);
}
Enfin, le dernier point va être de déplacer les icônes sur les côtés pour rendre l'effet de parallaxe de l'effet original. Pour cela, on utilise un accumulateur qui permettra de calculer le déplacement appliqué en fonction du grossissement de chaque icône.
/**
* @param {number} index Index de l'icône à agrandir
* @param {number} direction Position de l'icône (0: centre, -1: gauche, 1: droite)
* @param {number} offset Décalage à appliquer à l'icône
* @return Valeur de décalage créé par l'agrandissement de l'icône
*/
scaleFromDirection(index, direction, offset) {
const center = index + 0.5;
const distanceFromPointer = this.mousePosition - center;
const scale = scaling(distanceFromPointer) * this.scale;
const icon = this.icons[index];
icon.style.setProperty(
"transform",
`translateX(${offset}px) scale(${scale + 1})`
);
icon.style.setProperty(
"transform-origin",
`${TransformOrigins[direction.toString()]} bottom`
);
return scale * this.iconSize;
}
J'ai ici mis les points les plus importants, mais je vous laisse regarder la vidéo pour voir l'algorithme dans sa totalité.