Récemment, nous avons eu envie d’essayer la 3D embarquée dans le navigateur. Nous avons donc commencé par un tour rapide de ce qui existe en JavaScript : Three.js, Babylon.js… mais pour être honnête, la prise en main a été un peu délicate. En effet, nous nous sommes vite rendu compte que l’on se retrouvait à coder de la vraie 3D telle qu’elle serait générée par un logiciel, ce qui n’est pas dans notre coeur de métier. Autant dire que nous étions un peu découragé, jusqu’à ce que quelque chose que nous connaissons bien pointe le bout de son nez.
De la 3D en CSS ?
Vous ne le saviez peut-être pas mais il est tout à fait possible de reproduire des illusions 3D en CSS. Attention, nous parlons bien d' »illusion » pour une bonne raison, c’est qu’il n’y aura aucun volume à proprement parler dans cette 3D. Le principe repose sur la perspective dans lequel on représente des objets en trois dimensions grâce à différentes techniques (notamment le point de fuite) alors que l’on se trouve en fait sur une surface en deux dimensions.
C’est une bonne nouvelle parce que le CSS, c’est aussi l’assurance que le code sera léger, et surtout, c’est déjà disponible dans le navigateur. Après une rapide prise en main, nous pouvons donc nous lancer d’ores et déjà dans un cas pratique !
Les propriétés CSS à retenir pour faire de la 3D
Pour activer l’effet 3D :
transform-style: preserve-3d;
Pour déplacer ou tourner son objet sur l’axe Z :
transform: translateZ(value);
transform: rotateZ(value);
Ou en utilisant la propriété raccourcie qui regroupe également les axes X et Y :
transform: rotate3d(x,y,z,angle);
transform: translate3d(x,y,z);
Pour définir la profondeur de la perspective :
perspective: value;
Autrement dit, à quelle distance l’utilisateur se trouve de l’objet, ou plutôt du point de fuite. Plus il en est proche, plus l’objet est déformé et subit l’effet de la perspective, jusqu’à occuper tout l’écran.
Pour définir la position du point de fuite :
perspective-origin: center;
La 3D en théorie
Le postulat est simple. Nous allons donc changer la propriété transform-style et passer de la valeur par défaut, flat, à preserve-3d. Dès à présent, les blocs enfants vont se comporter différemment car ils ne seront plus aplati sur le plan de leur parent.
Mais tout dépendra des propriétés que l’on utilise ensuite. Par exemple, par un simple déplacement sur l’axe Z pour ces 3 blocs côte à côte :
<div class="container">
<div class="square one"></div>
<div class="square two"></div>
<div class="square three"></div>
</div>
.container {
position: relative;
perspective: 1000px;
transform-style: preserve-3d;
}
.square {
width: 100px;
height: 100px;
transform: rotateY(-30deg);
position: absolute;
&.three {
transform: rotateY(-30deg) translateZ(100px);
}
&.one {
transform: rotateY(-30deg) translateZ(-100px);
}
}
Nous pourrons générer des blocs dont l’emplacement ne sera plus défini par leur place dans le DOM ni par un z-index, mais fera apparaître en premier plan celui qui est au plus proche de l’espace 3D, c’est-à-dire celui dont la valeur sera la plus grande. Dans cet exemple, le bloc numéro 3 sera désormais en première position.
Cas pratique : faire orbiter un avion en papier autour d’une planète
Nous allons revisiter un visuel déjà existant sur le site, sur la page des prestations. Actuellement, notre graphiste a créé un visuel avec une planète et un avion qui explore une nouvelle théorie des chemtrails : les traînées de paillettes. Mais celui-ci est un peu trop statique, et nous aurions bien imaginé que l’avion tournoie autour de la planète.
- Nous créons l’espace, avec les propriétés suivantes :
.space {
transform-style: preserve-3d;
position: relative;
width: 300px;
margin-left: auto;
}
- Puis une planète :
.planet {
width: 300px;
height: 300px;
margin: auto;
background: transparent linear-gradient(145deg, #5c6af1 0%, #093aff 100%) 0% 0% no-repeat;
border-radius: 50%;
}
- Puis un avion :
.plane {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 100px;
height: 100px;
transform: perspective(1000px) rotateY(-360deg) translateX(-15vh) translateY(50%) translateZ(24vw);
transform-style: preserve-3d;
transition: 0.5s;
z-index: -1;
animation: fly 8s ease-out;
}
Les différents plans de l’avion sont dans des divs enfants de cet élément mais ne sont pas modifiés manuellement, c’est uniquement l’avion entier qui sera déformé.
Ça donne une propriété transform à rallonge mais pas de panique : tout ce que ça fait, c’est définir le point d’arrivée du trajet de notre avion à -360 degrés. Toutes les autres propriétés sont obligées d’être répétées en raison de la propriété raccourcie transform, mais elles ne vont pas évoluer.
- Notre animation :
@keyframes fly {
0% {
transform: perspective(1000px) rotateY(0deg) translateX(-270px) translateY(-50%)
translateZ(150px);
}
100% {
transform: perspective(1000px) rotateY(-360deg) translateX(-135px) translateY(50%) translateZ(150px);
}
}
Le point de départ de l’animation étant 0 degré, l’avion en papier aura l’occasion d’effectuer plusieurs tours sur lui-même dans le sens anti-horaire avant de terminer son trajet.
Le résultat :
Les blocages que l’on rencontre en faisant de la 3D en CSS
Nous avons cependant rencontré plusieurs problèmes qui peuvent être évités.
Une image vs un « objet » CSS
Nous avions déjà un avion, mais il s’agissait d’une image .svg en 2D et même avec tout le CSS du monde, il aurait été impossible de le représenter en 3D. Nous sommes donc obligé de nous en séparer et de construire un objet entièrement en CSS, avec une multitude de plans qui auront également la propriété preserve-3d pour donner l’effet de perspective sur lui-même. Pour faire court, nous nous sommes exonéré de cette partie et avons plutôt repris un objet existant, simplifié au maximum. Nous remercions donc l’auteur de ce CodePen pour son avion en papier.
Les propriétés CSS incompatibles
Nous avions beau déplacer et tourner dans l’axe Z tout en ayant activé la perspective, l’avion ne passait pas derrière la planète. Il nous a fallu un peu de temps pour réaliser que c’est la propriété overflow en hidden sur le parent (sur lequel il y a également preserve-3d) qui cassait l’effet de perspective. En creusant, nous avons compris que la propriété overflow, lorsqu’elle a une valeur différente de visible, force transform-style à prendre la valeur flat. C’est-à-dire qu’il n’y a plus de preserve-3d et donc plus de 3D tout court. Et le pire, c’est que ce n’est pas la seule ! Les propriétés suivantes ont le même effet et sont donc à éviter :
- Opacity : lorsque sa valeur est différente de 1
- Filter
Attention donc à ces exceptions qui peuvent faire perdre beaucoup de temps !
Cet article (en anglais) entre dans le détail des things that break 3D : https://css-tricks.com/things-watch-working-css-3d/
En conclusion
Globalement, la prise en main est facile car il y a peu de nouvelles propriétés CSS à apprivoiser, notamment les transformations sur l’axe Z qui sont déjà familières car utilisées régulièrement dans des contextes 2D. Elle est fondamentalement différente une fois la 3D activée puisque l’axe Z n’existe pas autrement. Il est néanmoins facile de se bloquer très vite sur des exceptions impossibles à deviner. Il faut surtout accepter que l’on va devoir jouer sur l’illusion pour donner l’effet voulu, et avancer à tâtons pour trouver les valeurs parfaites.