• 6 heures
  • Moyenne

Mis à jour le 14/06/2019

Les transformations 3D

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Bon, récapitulons :

  • Vous savez modifier l'apparence des objets de la page en 2D en CSS : ce sont les transformations

  • Vous savez faire bouger un objet pour qu'il passe d'un état à un autre : ce sont les transitions

  • Vous savez combiner plusieurs transitions à la suite : ce sont les animations

Qu'est-ce qu'il vous manque pour que le tableau soit complet... Hmm voyons voir... Ah je sais, les transformations, mais cette fois en 3D !

Croyez-le ou non, on peut aujourd'hui donner un effet 3D à nos objets dans la page, le tout simplement en CSS (et même pas besoin de payer un supplément lunettes 3D pour en profiter :-° ).

En m'amusant à préparer ce chapitre, j'ai eu l'idée de créer un carousel en 3D. Vous savez, ces blocs qui tournent pour afficher des messages différents ? Regardez ce que ça peut donner !

Un carousel en 3D animé en pur CSS !
Un carousel en 3D animé uniquement en CSS !

Dégradés, transparence, transformations 3D, animations, tout y est ! Et vous savez tout faire !

... ou presque, il vous reste à apprendre les transformations 3D justement. Allons-y ! :D

Définir la perspective

Avant de commencer à nous amuser avec la 3D, il faut que l'on parle vous et moi. D'un sujet très sérieux : la perspective. Sans perspective, pas de 3D sur votre écran.

Oui parce que voyez-vous, la 3D, ce n'est pas de la vraie 3D. On n'en est pas encore à faire des projections holographiques en 3D comme dans Star Wars. Pour le moment, votre écran reste irrémédiablement plat, en 2D.

Mais alors, comment afficher de la 3D sur un écran qui est en 2D ?

Oh, je suis sûr que vous avez déjà joué à des jeux en 3D sur votre ordinateur. Pour donner l'impression que c'est en 3D, on a justement recours à la perspective. La perspective est ce qui donne l'impression de profondeur à votre scène 3D.

La perspective indique la distance de votre oeil avec la scène. Plus la perspective est faible, plus on est près. Plus la perspective est élevée, plus on est loin.

La perspective se définit avec la propriétéperspective dans un élément conteneur : une<div> globale, ou même carrément<body> si toutes les scènes 3D de la page doivent avoir la même perspective (ce qui sera probablement souvent le cas).

body
{
    perspective: 800px;
}

800 pixels de perspective, ok, mais ça donne quoi ?

Je crois que le mieux est de visualiser la même scène en images avec différentes perspectives. :D

Une perspective de 3000 pixels : on est loin de la scène
Une perspective de 3000 pixels : peu de profondeur
Une perspective de 800 pixels
Une perspective de 800 pixels
Une perspective de 400 pixels : on est près de la scène
Une perspective de 400 pixels : beaucoup de profondeur

Définir l'origine du point de fuite

Dans toute scène 3D avec perspective, il y a ce qu'on appelle un point de fuite. C'est en quelque sorte le point d'ancrage de la scène, ce qui va donner l'impression qu'on regarde d'un certain côté la scène.

L'origine du point de fuite se définit avecperspective-origin  avec normalement deux valeurs : l'axe des X et l'axe des Y.

body
{
    perspective: 800px;
    perspective-origin: 20px 70px; /* Point de fuite placé aux coordonnées (20, 70)
}

Mais vous pouvez aussi employer les termes anglais top (haut), bottom (bas), center (centre), left (gauche), right (droite).

Ainsi, pour avoir une perspective depuis en haut à gauche :

body
{
    perspective: 800px;
    perspective-origin: top left;
}

Evidemment le mieux est encore de comparer quelques points de fuite différents pour bien se rendre compte du changement :

perspective-origin: top left;
perspective-origin: top left;
perspective-origin: center;
perspective-origin: center;
perspective-origin: right;
perspective-origin: right;

Notions de base des transformations 3D

Quand vous travaillez en 2D, vous êtes sur un plan avec 2 axes : X et Y.

Un plan en 2D a 2 axes : X et Y
Un plan en 2D a 2 axes : X et Y

Maintenant, en 3D, vous avez un 3ème axe : Z. C'est lui qui indique la profondeur :

Un plan en 3D a 3 axes : X, Y et Z
Un plan en 3D a 3 axes : X, Y et Z

Vous allez donc faire bouger vos objets selon ces 3 axes.

Vous n'allez pas être dépaysés vous allez voir : la 3D utilise comme la 2D la propriété CSStransform.

En 2D, nous avions à notre disposition je vous le rappelle les valeurs suivantes :

  • translate()

  • rotate()

  • scale()

  • skew()

  • matrix()

Eh bien mis à partskew(), toutes les autres fonctions existent dans leur version 3D :

  • translate3d() : déplacement de l'objet en 3D

  • rotate3d() : rotation de l'objet en 3D

  • scale3d() : mise à l'échelle de l'objet (agrandissement, rétrécissement) en 3D

  • matrix3d() : configuration personnalisée de la transformation en 3D. Nécessite de prendre la pilule rouge et d'aller rejoindre Neo dans son combat. Traduction : pas pour les débutants.

Les translations 3D

Bon allez, trève de configuration, il est temps de passer à l'action ! :pirate:

Il y a deux versions des translations 3D :

  • translate3d(x, y, z) : positionne l'objet dans l'espace en 3D aux coordonnées (X, Y, Z).

  • translateZ(z) : positionne l'objet dans l'espace en 3D aux coordonnées (0, 0, Z). En clair, vous ne jouerez ici que sur l'impression de profondeur, l'objet restant positionné au centre de la scène.

Si vous voulez juste jouer sur la profondeur donc,translateZ() suffit. Le code est aussi simple que ça :

div
{
    transform: translateZ(200px);
}

Testons quelques valeurs différentes sur un même bloc pour voir ce que ça change :

Quelques translations dans l'espace
Quelques translations dans l'espace

Ici, on voit bien l'impression de profondeur quand on déplace l'objet sur l'axe Z.

J'ai testé avec un seul bloc sur ma machine. Au final, ça ne fait que grossir le bloc et le décaler un peu ?!

Oui, c'est ça. La 3D fonctionne comme ça. Evidemment, quand vous avez un seul objet, la 3D n'est pas évidente. Mais quand vous avez plusieurs objets dans votre scène (comme sur ma capture précédente), vous commencez à percevoir l'impression de 3D.

L'agrandissement 3D

De la même façon, l'agrandissement 3D se gère avec l'une de ces fonctions :

  • scale3d(sx, sy, sz) : agrandit l'objet selon un facteur sx, sy, sz sur les différents axes. Avec un facteur de 2, la taille de l'objet est doublée. Avec un facteur de 0.5, la taille est divisée par deux.

  • scaleZ(sz) : agrandit l'objet selon un facteur de sz sur l'axe des Z (profondeur).

Par exemple :

div
{
    transform: scale3d(1, 2, 2);
}

Vous utiliserez plus probablement cette mécanique d'agrandissement en combinaison avec d'autres transformations. Seule, elle présente assez peu d'intérêt.

Les rotations 3D

Vous avez ici à votre disposition les fonctions suivantes :

  • rotateX(x) : une rotation sur l'axe X

  • rotateY(y) : une rotation sur l'axe Y

  • rotateZ(z) : une rotation sur l'axe Z. Cela correspond en fait à une rotation en 2D, donc c'est la même chose que la fonctionrotate().

  • rotate3d(x, y, z) : une rotation sur plusieurs axes en même temps

Par exemple :

div
{
    transform: rotateX(20deg);
}

Testons les 3 types de rotations sur chacun des axes :

Des rotations sur chacun des axes en action
Des rotations sur chacun des axes en action

Sur l'axe des X, on a l'impression que le bloc "tombe" vers le bas. Et il sera effectivement à plat si on fait une rotation de 90 degrés.

Sur l'axe des Y, il tourne, comme un drapeau autour d'un piquet.

Sur l'axe des Z, il s'agit comme je vous le disais d'une simple rotation en 2D qu'on connaît déjà.

La face cachée des blocs

Que se passe-t-il quand vous faites tourner suffisamment un bloc ? On voit sa face arrière.

Par défaut, la face arrière est visible, ce qui nous donne :

Par défaut, la face arrière du bloc est visible
Par défaut, la face arrière "retournée" du bloc est visible

Si vous ne voulez pas que la face arrière soit visible, vous pouvez utiliserbackface-visibility  et passer de la valeur "visible" par défaut à la valeur "hidden" :

div
{
    backface-visibility: hidden;
}

Lorsque le  bloc sera retourné, il ne sera cette fois plus visible :

La face arrière du bloc n'est plus visible
La face arrière du bloc n'est plus visible

Je ne vois pas trop l'intérêt de faire ça ?

L'intérêt est plus évident quand vous "collez" deux blocs l'un sur l'autre : l'un est retourné (à -180°) et l'autre est face avant (à 0°) par défaut. Si ensuite vous animez ces deux blocs pour les retourner, vous pouvez créer les deux faces d'une carte. Dans ce cas de figure,backface-visibility  permet d'éviter tout scintillement qui révélerait la supercherie.

Allez essayons justement de construire cet effet, vous allez comprendre. Prenons une page avec deux blocs :

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Transformations 3D</title>
        <link rel="stylesheet" href="style.css" />
    </head>

    <body>
        <div id="devant">Devant</div>
        <div id="derriere">Derrière !</div>
    </body>
</html>

En CSS, on va superposer ces blocs l'un sur l'autre (avec une simple position absolue) et "retourner" le bloc #derriere en lui appliquant une rotation à 180°. Au survol, les deux blocs vont tourner ensemble et grâce àbackface-visibility  toute l'animation sera propre !

body
{
    perspective: 800px;
}

div
{
    width: 200px;
    height: 150px;
    position: absolute;
    top: 200px;
    left: 500px;
    opacity: 0.5;
    border: 2px solid #444;
    text-align: center;
    font-size: 1.3em;
    font-family: arial, sans-serif;
    transition: 2s;
    backface-visibility: hidden; /* Permet d'éviter de voir le bloc de dos */
}

#devant
{
    z-index: 100;
    background-image: linear-gradient(white, green);
}

#derriere
{
    z-index: 50;
    background-image: linear-gradient(white, red);
    transform: rotateY(-180deg); /* Celui-ci est retourné par défaut */
}

#devant:hover
{
    transform: rotateY(360deg); /* On passe de 0 à 360° : tour complet */
}

/*
Astuce :
Lorsqu'on survole le bloc devant, on applique une rotation au bloc derriere
*/
#devant:hover + #derriere
{
    transform: rotateY(180deg);  /* On passe de -180° à 180° : tour complet */
}

Résultat :

Deux blocs juxtaposés forment les deux faces d'une carte !
Deux blocs juxtaposés forment les deux faces d'une carte !

Allez maintenant, à vous de jouer ! ^^

Si vous voulez du challenge (du vrai !), vous pouvez essayer d'imiter ce développeur qui a réussi à créer un environnement complet en 3D... juste en CSS avec des animations et transformations (et un peu de JavaScript d'accord !) :

Croyez-le ou non, cette scène a été générée seulement avec du CSS !
Cette scène a été générée seulement avec du CSS !

Pour admirer son travail, c'est ici. Et pour comprendre à quel point il a galéré (surtout pour les effets de lumière), c'est par là. :p

Exemple de certificat de réussite
Exemple de certificat de réussite