74 votes

Comment créer des sphères tridimensionnelles en CSS pur ?

tl;dr : J'aimerais créer une véritable sphère en 3D avec CSS, et pas seulement une illusion.

Note : certains des exemples de snippets ne sont pas responsive. Veuillez utiliser le plein écran.


Avec le CSS pur, vous pouvez créer et animer un cube 3d comme ceci :

#cube-wrapper {
  position: absolute;
  left: 50%;
  top: 50%;
  perspective: 1500px;
}

.cube {
  position: relative;
  transform-style: preserve-3d;
  animation-name: rotate;
  animation-duration: 30s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

@keyframes rotate {
  0% {
    transform: rotate3d(0, 0, 0, 0);
  }
  100% {
    transform: rotate3d(0, 1, 0, 360deg);
    ;
  }
}

.face {
  position: absolute;
  width: 200px;
  height: 200px;
  border: solid green 3px;
}

#front_face {
  transform: translateX(-100px) translateY(-100px) translateZ(100px);
  background: rgba(255, 0, 0, 0.5);
}

#back_face {
  transform: translateX(-100px) translateY(-100px) translateZ(-100px);
  background: rgba(255, 0, 255, 0.5);
}

#right_face {
  transform: translateY(-100px) rotateY(90deg);
  background: rgba(255, 255, 0, 0.5);
}

#left_face {
  transform: translateY(-100px) translateX(-200px) rotateY(90deg);
  background: rgba(0, 255, 0, 0.5);
}

#top_face {
  transform: translateX(-100px) translateY(-200px) rotateX(90deg);
  background: rgba(0, 255, 255, 0.5);
}

#bottom_face {
  transform: translateX(-100px) rotateX(90deg);
  background: rgba(255, 255, 255, 0.5);
}

.cube {
  transform: rotateX(90deg) rotateY(90deg);
}

<div id="cube-wrapper">
  <div class="cube">
    <div id="front_face" class="face"></div>
    <div id="right_face" class="face"></div>
    <div id="back_face" class="face"></div>
    <div id="left_face" class="face"></div>
    <div id="top_face" class="face"></div>
    <div id="bottom_face" class="face"></div>
  </div>
</div>

Je veux créer et animer une sphère 3d de la même manière.

Donc... la première idée qui me vient est d'utiliser border-radius et... eh bien... ça ne marche pas.

#cube-wrapper {
  position: absolute;
  left: 50%;
  top: 50%;
  perspective: 1500px;
}

.cube {
  position: relative;
  transform-style: preserve-3d;
  animation-name: rotate;
  animation-duration: 30s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

@keyframes rotate {
  0% {
    transform: rotate3d(0, 0, 0, 0);
  }
  100% {
    transform: rotate3d(0, 1, 0, 360deg);

    ;
  }
}

.face {
  position: absolute;
  width: 200px;
  height: 200px;
  border: solid green 3px;
  border-radius: 100vw
}

#front_face {
  transform: translateX(-100px) translateY(-100px) translateZ(100px);
  background: rgba(255, 0, 0, 0.5);
}

#back_face {
  transform: translateX(-100px) translateY(-100px) translateZ(-100px);
  background: rgba(255, 0, 255, 0.5);
}

#right_face {
  transform: translateY(-100px) rotateY(90deg);
  background: rgba(255, 255, 0, 0.5);
}

#left_face {
  transform: translateY(-100px) translateX(-200px) rotateY(90deg);
  background: rgba(0, 255, 0, 0.5);
}

#top_face {
  transform: translateX(-100px) translateY(-200px) rotateX(90deg);
  background: rgba(0, 255, 255, 0.5);
}

#bottom_face {
  transform: translateX(-100px) rotateX(90deg);
  background: rgba(255, 255, 255, 0.5);
}

.cube {
  transform: rotateX(90deg) rotateY(90deg);
}

<div id="cube-wrapper">
  <div class="cube">
    <div id="front_face" class="face"></div>
    <div id="right_face" class="face"></div>
    <div id="back_face" class="face"></div>
    <div id="left_face" class="face"></div>
    <div id="top_face" class="face"></div>
    <div id="bottom_face" class="face"></div>
  </div>
</div>

J'ai donc reconsidéré mon approche et cherché une autre méthode.

J'ai regardé :

Puis j'ai réessayé... les meilleurs résultats que j'ai obtenus étaient trop... compliqué Objet 3d illusions .

Comme ça :

body {
  overflow: hidden;
  background: #333;
}

.wrapper {
  margin: 1em;
  animation-duration: 20s;
}

.planet,
.planet:before,
.planet:after {
  height: 300px;
  width: 300px;
  border-radius: 100vw;
  will-change: transform;
  margin: 0 auto;
}

.planet {
  box-shadow: inset 0px 0px 10px 10px rgba(0, 0, 0, 0.4);
  position: relative;
}

.wrapper,
.planet,
.planet:before {
  animation-name: myrotate;
  animation-duration: 20s;
}

.wrapper,
.planet,
.planet:before,
.planet:after {
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

.planet:before,
.planet:after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
}

.planet:before {
  box-shadow: inset 20px 20px 100px 00px rgba(0, 0, 0, .5), 0px 0px 5px 3px rgba(0, 0, 0, .1);
}

.planet:after {
  filter: saturate(2.5);
  background: linear-gradient(rgba(0, 0, 0, 1), transparent), url("https://i.stack.imgur.com/eDYPN.jpg");
  opacity: 0.3;
  box-shadow: inset -20px -20px 14px 2px rgba(0, 0, 0, .2);
  animation-name: myopacity;
  animation-duration: 5000000s;
}

@keyframes myrotate {
  0% {
    transform: rotatez(0deg);
  }
  100% {
    transform: rotatez(360deg);
  }
}

@keyframes myopacity {
  0% {
    background-position: 0px;
    transform: rotatez(0deg);
  }
  50% {
    background-position: 100000000px;
  }
  100% {
    background-position: 0;
    transform: rotatez(-360deg);
  }
}

<div class="wrapper">
  <div class="planet"></div>
</div>

Et ceci :

body {
  background: #131418;
}

.wrapper {
  margin: 1em;
  max-width: 100%;
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

.planet,
.planet:before,
.planet:after {
  height: 500px;
  width: 500px;
  max-height: 30vw;
  max-width: 30vw;
  border-radius: 100vw;
  will-change: transform;
}

.planet {
  box-shadow: inset 0px 0px 100px 10px rgba(0, 0, 0, .5);
  position: relative;
  float: left;
  margin: 0 2em;
}

.planet,
.planet:before,
.planet:after {
  animation-name: myrotate;
  animation-duration: 10s;
}

.wrapper,
.planet,
.planet:before,
.planet:after {
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

.planet:before,
.planet:after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
}

.planet:before {
  box-shadow: inset 50px 100px 50px 0 rgba(0, 0, 0, .5), 0 0 50px 3px rgba(0, 0, 0, .25);
  background-image: -webkit-radial-gradient( top, circle cover, #ffffff 0%, #000000 80%);
  opacity: .5;
}

.planet:after {
  opacity: .3;
  background-image: -webkit-radial-gradient( bottom, circle, #ffffff 0%, #000000 -200%);
  box-shadow: inset 0px 0px 100px 50px rgba(0, 0, 0, .5);
}

@keyframes myrotate {
  0% {
    transform: rotatez(0deg);
  }
  100% {
    transform: rotatez(-360deg);
  }
}

.bg {
  background: wheat;
}

<div class="wrapper">
  <div class="planet bg"></div>
</div>

Ce qui ne pose aucun problème jusqu'à ce que vous essayiez de les faire tourner sur l'un ou l'autre des systèmes suivants Axe x ou le axe y comme le cube de mon premier exemple... voici ce qui se passe alors : (exemple simplifié)

.sphere {
  background: black;
  width: 300px;
  height: 300px;
  border-radius: 100vw;
  animation: myrotate 10s linear infinite
}

@keyframes myrotate {
  0% {
    transform: rotate3d(0, 0, 0, 0);
  }
  100% {
    transform: rotate3d(0, 1, 0, 360deg);
  }
}

<div class="sphere"></div>

Tout ce que vous obtenez, c'est un objet plat en 2D, ce qui est normal puisque c'est ce qu'est l'élément.


La chose la plus proche que j'ai trouvée est la forme suivante créée dans une tutoriel par Timo Korinth

@-webkit-keyframes animateWorld {
  0% {
    -webkit-transform: rotateY(0deg) rotateX(0deg) rotateZ(0deg);
  }
  50% {
    -webkit-transform: rotateY(360deg) rotateX(180deg) rotateZ(180deg);
  }
  100% {
    -webkit-transform: rotateY(720deg) rotateX(360deg) rotateZ(360deg);
  }
}

html {
  background: #FFFFFF;
}

. world {
  -webkit-perspective: 1000px;
}

.cube {
  margin-left: auto;
  margin-right: auto;
  position: relative;
  width: 200px;
  height: 200px;
  -webkit-transform-style: preserve-3d;
  -webkit-animation-name: animateWorld;
  -webkit-animation-duration: 10s;
  -webkit-animation-iteration-count: infinite;
  -webkit-animation-timing-function: linear;
}

.circle {
  position: absolute;
  width: 100%;
  height: 100%;
  border: 2px dashed #009BC2;
  border-radius: 50%;
  opacity: 0.8;
  background: rgba(255, 255, 255, 0);
}

.zero {
  -webkit-transform: rotateX(90deg);
}

.two {
  -webkit-transform: rotateY(45deg);
}

.three {
  -webkit-transform: rotateY(90deg);
}

.four {
  -webkit-transform: rotateY(135deg);
}

.five {
  width: 173px;
  height: 173px;
  margin: 14px;
  -webkit-transform: rotateX(90deg) translateZ(50px);
}

.six {
  width: 173px;
  height: 173px;
  margin: 14px;
  -webkit-transform: rotateX(90deg) translateZ(-50px);
}

<div class="world">
  <div class="cube">
    <div class="circle zero"></div>
    <div class="circle one"></div>
    <div class="circle two"></div>
    <div class="circle three"></div>
    <div class="circle four"></div>
    <div class="circle five"></div>
    <div class="circle six"></div>
  </div>
</div>

Alors voici mon

Question :

Comment créer une sphère tridimensionnelle réelle avec du pur CSS ? Plus précisément, une sphère qui soit couverte - pas seulement un cadre - et qui n'implique pas des centaines d'éléments html.


Notes :

  1. Les sphères tridimensionnelles ont hauteur, largeur et profondeur - juste comme le cube de mon premier exemple.
  2. Je n'ai pas besoin de physique et il n'y a pas besoin d'une quelconque d'interaction avec l'utilisateur. Juste une sphère animée qui tourne.

Ressources supplémentaires :

  1. paulrhayes.com - Sphères
  2. 3d (2d illusion) Terre avec animation tournante avec CSS
  3. Sphère CSS interactive

126voto

Ilya Streltsyn Points 3857

A proprement parler, tout forme "3D" sur un plat L'écran est plutôt un illusion d'un objet en 3D. Tout ce que nous voyons est un objet 2D projection de cette forme sur le plan de l'écran, et notre cerveau fait de son mieux pour deviner quelle forme pourrait donner la projection que nous voyons. Si la projection change, notre cerveau l'interprète comme un objet 3D qui change d'orientation, ce qui l'aide à mieux déterminer la forme de cet objet.

Elle fonctionne bien avec les objets non symétriques et les objets constitués de polygones (par exemple, les cubes), mais la sphère est un cas très particulier : sa projection sur le plan donne toujours un simple cercle. La sphère statique et la sphère en rotation ont la même projection, le même cercle. Même dans la vie réelle, si nous regardons une sphère dont la surface est uniforme et sans aucune marque (par exemple, une boule de métal polie), il est difficile de déterminer si elle est statique ou en rotation. Nos yeux ont besoin de quelques indices, de quelques détails qui se déplacent le long de la surface de la sphère en fonction de sa géométrie. Plus ces détails se déplacent de la manière dont on s'attend à ce que les points de la surface sphérique se déplacent, plus la perception (ou plutôt l'illusion) de la sphère en rotation est claire.

Et voici la clé du problème pour créer une scène CSS qui donnerait une telle perception : pour que cette illusion soit suffisamment forte, nous avons besoin de nombreuses marques se déplaçant le long des chemins qui se trouvent dans des plans différents. Et la seule façon d'obtenir cela en CSS est d'avoir chaque marque comme une boîte CSS séparée (élément ou pseudo-élément). Si notre sphère est composée de sólo de repères mobiles, nous avons vraiment besoin de beaucoup d'entre eux pour la voir comme une sphère - d'où les "centaines d'éléments" dans la plupart des démos que vous avez vues.

Donc, si vous voulez donner à une sphère un aspect réaliste avec un nombre raisonnablement faible d'éléments, vous devrez probablement combiner les effets qui donnent l'"illusion" d'une forme sphérique de base statique (un cercle avec des gradients radiaux, des ombres intérieures, etc.) avec des éléments relativement petits (pour qu'il soit moins évident qu'ils sont en réalité plats), orientés le long de la surface de la sphère avec des transformations 3D, et animés - essentiellement de la même manière que les faces du cube dans votre première démo.

Vous trouverez ci-dessous ma propre tentative de mise en pratique de cette approche. J'ai utilisé 20 éléments circulaires orientés à peu près comme les faces de l'objet icosaèdre régulier (comme les hexagones blancs sur le ballon de football classique). Je les ai regroupés en deux groupes faisant chacun un hémisphère par commodité (ce n'était pas nécessaire, mais cela a rendu le style un peu plus simple). L'ensemble de la scène 3D se compose de la sphère elle-même et du cadre d'arrière-plan (pseudo-élément) qui traverse la sphère près de son centre (un peu plus près, pour réduire le "scintillement" des cercles lorsqu'ils passent du côté proche au côté éloigné et vice-versa) et fait toujours face à l'écran. Donc 24 éléments au total (pas littéralement "des centaines", au moins :). Pour que les cercles aient l'air plus "bombés" (comme des segments sphériques), j'ai ajouté deux pseudo-éléments à chacun d'eux et les ai légèrement surélevés l'un par rapport à l'autre. Fonctionne mieux dans Chrome et Firefox 57+ (dans Firefox 56- et iOS Safari il y a encore un certain "scintillement" près des bords). Si vous survolez le cercle, vous pouvez voir la scène sans le cadre de fond (et aussi sans "scintillement"). Une version légèrement modifiée est également disponible sur Codepen .

.scene {
  perspective: 400vmin;
  transform-style: preserve-3d;
  position: absolute;
  width: 80vmin;
  height: 80vmin;
  top: 10vmin;
  left: 10vmin;
}

.sphere {
  transform-style: preserve-3d;
  position: absolute;
  animation: rotate 20s infinite linear;
  width: 100%;
  height: 100%;
  transform-origin: 50% 50%;
  top: 0;
  left: 0;
}

.scene::before {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0%;
  left: 0%;
  background: radial-gradient(circle farthest-corner at 33% 33%, rgba(240, 240, 220, 0.85) 0%, rgba(30, 30, 40, 0.85) 80%), radial-gradient(circle farthest-corner at 45% 45%, rgba(0, 0, 0, 0) 50%, #000000 80%);
  border-radius: 50%;
  transform: translateZ(2vmin);
}

.scene:hover::before {
  display: none;
}

.hemisphere {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transform-origin: 50% 50%;
  transform: rotateX(90deg);
}

.hemisphere:nth-child(2) {
  transform: rotateX(-90deg);
}

.face {
  position: absolute;
  width: 40vmin;
  height: 40vmin;
  background: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1) 48%, #ff0000 49%, #ff0000 50%, rgba(0, 0, 0, 0) 51%);
  transform-style: preserve-3d;
  transform-origin: 50% 0;
  top: 50%;
  left: 20vmin;
}

.face::before, .face::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  box-sizing: border-box;
}

.face::before {
  width: 50%;
  height: 50%;
  top: 25%;
  left: 25%;
  border: 2px solid #333;
  background: rgba(255, 255, 255, 0.3);
  transform: translateZ(1.6vmin);
}

.face::after {
  width: 20%;
  height: 20%;
  top: 40%;
  left: 40%;
  background: rgba(0, 0, 0, 0.2);
  transform: translateZ(2.8vmin);
}

.face:nth-child(1) {
  transform: translateZ(-41.6vmin) rotateZ(36deg) translateY(-6.8vmin) rotateX(143deg);
}

.face:nth-child(2) {
  transform: translateZ(-41.6vmin) rotateZ(108deg) translateY(-6.8vmin) rotateX(143deg);
}

.face:nth-child(3) {
  transform: translateZ(-41.6vmin) rotateZ(180deg) translateY(-6.8vmin) rotateX(143deg);
}

.face:nth-child(4) {
  transform: translateZ(-41.6vmin) rotateZ(252deg) translateY(-6.8vmin) rotateX(143deg);
}

.face:nth-child(5) {
  transform: translateZ(-41.6vmin) rotateZ(-36deg) translateY(-6.8vmin) rotateX(143deg);
}

.face:nth-child(6) {
  transform: translateZ(-26.8vmin) rotateZ(36deg) translateY(-33.2vmin) rotateX(100deg);
}

.face:nth-child(7) {
  transform: translateZ(-26.8vmin) rotateZ(108deg) translateY(-33.2vmin) rotateX(100deg);
}

.face:nth-child(8) {
  transform: translateZ(-26.8vmin) rotateZ(180deg) translateY(-33.2vmin) rotateX(100deg);
}

.face:nth-child(9) {
  transform: translateZ(-26.8vmin) rotateZ(252deg) translateY(-33.2vmin) rotateX(100deg);
}

.face:nth-child(10) {
  transform: translateZ(-26.8vmin) rotateZ(-36deg) translateY(-33.2vmin) rotateX(100deg);
}

.face:nth-child(11) {
  transform: translateZ(-26.8vmin) rotateZ(36deg) translateY(-33.2vmin) rotateX(100deg);
}

@keyframes rotate {
  0% {
transform: rotateZ(25deg) rotateX(20deg) rotateY(0deg);
  }
  50% {
transform: rotateZ(-25deg) rotateX(-20deg) rotateY(180deg);
  }
  100% {
transform: rotateZ(25deg) rotateX(20deg) rotateY(360deg);
  }
}

body {
  background: #555;
  overflow: hidden;
}

<div class="scene">
  <div class="sphere">
    <div class="hemisphere">
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
    </div>
    <div class="hemisphere">
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
    </div>
  </div>
</div>

36voto

Comment créer une sphère tridimensionnelle réelle avec du pur CSS ?

Eh bien, comme beaucoup l'ont dit dans les réponses et les commentaires, il est tout simplement impossible de créer une entité 3D unique dans un navigateur avec html et css seulement à ce stade, mais il est possible de créer une illusion d'un objet 3D. Voici mon approche pour résoudre le problème.

Pour donner à l'œil humain la capacité de voir un objet sphérique, il faut des points de référence que l'œil doit suivre. Dans mon cas, ce sont des lignes qui définissent la forme de la sphère. Les lignes sont obtenues en donnant une bordure à 5 éléments qui se trouvent dans l'ensemble de l'axe X et à 5 éléments qui se trouvent dans l'ensemble de l'axe Y. Seuls les ensembles X/Y reçoivent une bordure, car cela suffit à fournir suffisamment de références pour donner l'illusion d'une sphère. Les lignes supplémentaires sur l'axe Z ne sont que du désordre et ne sont pas nécessaires. Si toutes les lignes sont désactivées, l'ensemble apparaît comme une sphère solide "parfaite" (On dirait un cercle, mais toutes ses parties sont en mouvement et sont présentes sur le plan 3D dans le navigateur !)


Ce que j'ai fait :

  1. Création de 15 éléments html qui représenteront chacun un cercle divisé en 3 séries de 5.

    La raison de tous ces ensembles est que, lorsque l'ensemble du dispositif est tourné sur les axes x, y et z, les arrière-plans de chacun des éléments des ensembles x, y et z remplissent les espaces vides les uns pour les autres.

  2. Chaque ensemble de 5 éléments est tourné sur les axes X, Y, Z respectivement par incréments de 36 degrés. enter image description here

  3. Tous les éléments sont arrondis à l'aide de border-radius:50%; 3 sets of 5 rounded elements for X Y Z axis

  4. Définir l'arrière-plan des éléments du cercle à une couleur solide Filled sets if 5 rounded elements

  5. Mettez les ensembles ensemble, de sorte qu'ils se chevauchent
    enter image description here

  6. Coupure de lacunes mineures résultant du fait qu'il n'y avait pas assez d'éléments couvrant les espaces vides entre les cercles x, y, z en utilisant clip-path: circle(96px at center); sur le conteneur, et a jeté un effet d'ombre et de lumière cool sur pour sceller l'affaire.
    enter image description here vs enter image description here

    Un plus grand nombre de cercles donnera une sphère moins "nerveuse", mais comme la question insiste sur la performance, un clip rapide de l'ensemble semblait être la chose à faire.


En guise de conclusion, je voulais exprimer mon appréciation de la question posée, elle m'a vraiment fait réfléchir et a été un excellent projet qui m'a permis d'apprendre beaucoup de choses sur les capacités 3D de html/css.

Merci également à toutes les personnes qui ont pris le temps d'ouvrir ce dossier et qui ont proposé des approches géniales pour résoudre le problème.

J'espère que le fruit de mes recherches vous sera utile. A la vôtre !

Ce stylo est également basé sur l'exemple de Timo Korinth.

* {
  margin: 0;
  padding: 0;
}

/* Rotate Sphere animation */

@-webkit-keyframes animateSphere {
  0% {
    transform: rotateY(0deg) rotateX(0deg) rotateZ(0deg);
  }
  50% {
    transform: rotateY(360deg) rotateX(360deg) rotateZ(0deg);
  }
  100% {
    transform: rotateY(720deg) rotateX(720deg) rotateZ(0deg);
  }
}

html {
  background: black;
}

.scene {
  perspective: 1000px;
}

.container {
  margin-top: 5vh;
  margin-left: auto;
  margin-right: auto;
  position: relative;
  width: 200px;
  height: 200px;
  transform-style: preserve-3d;
  animation-name: animateSphere;
  animation-duration: 30s;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

.border {
  border: 1px solid white;
}

.circle {
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: rgba(204, 0, 102, 1);
}

.circle:nth-child(1) {
  transform: rotate3d(1, 0, 0, 0deg);
}

.circle:nth-child(2) {
  transform: rotate3d(1, 0, 0, 36deg);
}

.circle:nth-child(3) {
  transform: rotate3d(1, 0, 0, 72deg);
}

.circle:nth-child(4) {
  transform: rotate3d(1, 0, 0, 108deg);
}

.circle:nth-child(5) {
  transform: rotate3d(1, 0, 0, 144deg);
}

/* 18! difference to align*/

.circle:nth-child(6) {
  transform: rotate3d(0, 1, 0, 0deg);
}

.circle:nth-child(7) {
  transform: rotate3d(0, 1, 0, 36deg);
}

/* Upper and Lower circle */

.circle:nth-child(8) {
  transform: rotate3d(0, 1, 0, 72deg);
}

.circle:nth-child(9) {
  transform: rotate3d(0, 1, 0, 108deg);
}

.circle:nth-child(10) {
  transform: rotate3d(0, 1, 0, 144deg);
}

.circle:nth-child(11) {
  transform: rotate3d(0, 1, 0, 90deg) rotate3d(1, 0, 0, 0deg);
}

.circle:nth-child(12) {
  transform: rotate3d(0, 1, 0, 90deg) rotate3d(1, 0, 0, 36deg);
}

/* Upper and Lower circle */

.circle:nth-child(13) {
  transform: rotate3d(0, 1, 0, 90deg) rotate3d(1, 0, 0, 72deg);
}

.circle:nth-child(14) {
  transform: rotate3d(0, 1, 0, 90deg) rotate3d(1, 0, 0, 108deg);
}

.circle:nth-child(15) {
  transform: rotate3d(0, 1, 0, 90deg) rotate3d(1, 0, 0, 144deg);
}

.shadow {
  margin: auto;
  border-radius: 50%;
  width: 200px;
  height: 200px;
  box-shadow: 10px 1px 30px white;
}

/* Clip the sphere a bit*/

.clip {
  clip-path: circle(96px at center);
}

<div class="scene">
  <div class="shadow">
    <div class="clip">
      <div class="container">
        <div class="circle border"></div>
        <div class="circle border"></div>
        <div class="circle border"></div>
        <div class="circle border"></div>
        <div class="circle border"></div>
        <div class="circle border"></div>
        <div class="circle border"></div>
        <div class="circle border"></div>
        <div class="circle border"></div>
        <div class="circle border"></div>
        <div class="circle"></div>
        <div class="circle"></div>
        <div class="circle"></div>
        <div class="circle"></div>
        <div class="circle"></div>
      </div>
    </div>
  </div>
</div>

23voto

Ori Shalom Points 311

Comme nous l'avons déjà mentionné plus haut, CSS3 ne peut pas vous fournir de véritables formes en 3D, mais seulement une illusion. Une bonne illusion de sphère qui utilise un minimum d'éléments HTML et une image comme texture peut être réalisée par une combinaison de ce que vous avez fait et en masquant le tout avec des ombres CSS.

Une touche sympathique qui peut rendre le masque plus réaliste est l'utilisation de la touche :after pseudo-élément pour la création d'une étincelle supplémentaire dans un emplacement décalé et de plus petite taille. Un élément clé pour un effet réussi est de se rappeler que les différents matériaux reflètent la lumière différemment. Cela signifie que si vous essayez de créer une sphère métallique, l'éclairage créé par l'élément box-shadow sera différent de l'éclairage d'une sphère en plastique.

Un autre ajout intéressant est l'utilisation de la fonction :before pseudo-élément pour la création d'un effet de réflexion. L'ajout d'une image préfabriquée du monde sur une sphère avec une certaine opacité peut créer un effet très persuasif. Notez également qu'avec le reflet, le matériau que vous essayez de créer déterminera le degré d'opacité que vous souhaitez pour le reflet.

Remarquez, j'ai utilisé un prisme octogonal pour que l'image derrière la scène ait l'air plus ronde lorsque la forme 3D applique sa perspective. Même en utilisant seulement 8 éléments, le résultat est assez réaliste. Un résultat plus réaliste peut être obtenu avec plus de polygones et des formes plus complexes et des mappages de textures, mais même dans ce cas, il n'est pas nécessaire d'utiliser trop d'éléments en raison de l'ajout de l'ombre et de l'étincelle au-dessus de tout.

Enfin, afin de cacher le reste du prisme octogonal et d'afficher uniquement les parties à l'intérieur des limites de la sphère, j'utilise clip-path: circle(99px at center); .

body {
  width: 100%;
  height: 100%;
  background-color: #000; 
}
.cube-wrapper {
  width: 0;
  height: 0;
  top: 100px;
  left: 100px;
  position: absolute;
  perspective-origin: 0 0;
  perspective: 80px;
}
.cube-2 {
  transform: translateZ(-100px) scaleX(1.8);
  transform-style: preserve-3d;
}
.cube {
  top: -100px;
  position: relative;
  transform-style: preserve-3d;
  animation-name: rotate;
  animation-duration: 10s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
@keyframes rotate {
  0% {
    transform: rotate3d(0 0, 0, 360deg);
  }
  100% {
    transform: rotate3d(0, 1, 0, 360deg);
    ;
  }
}

.face {
  position: absolute;
  background-size: 662.4px 200px;
  width: 84px;
  height: 200px;
}

#face1 {
  transform: translateX(-41.4px) translateZ(100px);
  background-position: 0 0;
}
#face2 {
  transform: translateX(29.2px) translateZ(70.8px) rotateY(45deg);
  background-position: -82.8px 0;
}
#face3 {
  transform: translateX(58.5px) rotateY(90deg);
  background-position: -165.6px 0;
}
#face4 {
  transform: translateX(29.2px) translateZ(-70.8px) rotateY(135deg);
  background-position: -248.4px 0;
}
#face5 {
  transform: translateX(-41.4px) translateZ(-100px) rotateY(180deg);
  background-position: -331.2px 0;
}
#face6 {
  transform: translateX(-111.4px) translateZ(-70.8px) rotateY(225deg);
  background-position: -414px 0;
}
#face7 {
  transform: translateX(-141.4px) rotateY(270deg);
  background-position: -496.8px 0;
}
#face8 {
  transform: translateX(-111.4px) translateZ(70px) rotateY(315deg);
  background-position: -579.6px 0;
}

.circle {
  position: absolute;
  width: 200px;
  height: 200px;
  border-radius: 50%;
}
.clip-circle {
  position: absolute;
  padding: 0;
  top: -16px;
  width: 200px;
  height: 200px;
  border-radius: 50%;
  clip-path: circle(99px at center);
}
.lighting:after {
    content: '';
    position: absolute;
    top: 50px;
    left: 67px;
}
.reflection:before {
    content: '';
    position: absolute;
    top: 0px;
    left: 0px;
    height: 200px;
    width: 200px;
    background-image:url(https://i.stack.imgur.com/ayCw7.png);
    background-size: 200px 200px;
}

.earth {
  position: absolute;
  left: 20px;
}
.earth .face{
  background-image:url(https://i.stack.imgur.com/fdtNz.jpg);
}
.earth .clip-circle {
  transform: rotateX(7deg) rotateZ(15deg);
}
.earth .lighting {
  box-shadow: -20px -30px 55px 0 rgba(0, 0, 0, 0.9) inset, -75px -100px 150px 0 rgba(0, 0, 0, 0.4) inset, 75px 100px 200px 0 rgba(255, 255, 255, 0.2) inset, -1px -2px 10px 2px rgba(200, 190, 255, 0.2);
}
.earth .lighting:after {
    box-shadow: 0 0 150px 51px rgba(255, 255, 255, 0.2), 0 0 26px 10px rgba(255, 255, 255, 0.2);
}

.wood {
  position: absolute;
  left: 240px;
}
.wood .face{
  background-image:url(https://i.stack.imgur.com/sa5P8.jpg);
}
.wood .cube-wrapper {
  transform: rotateZ(45deg);
}
.wood .lighting {
  box-shadow: -20px -30px 90px 0 rgba(0, 0, 0, 0.7) inset, -75px -100px 140px 0 rgba(0, 0, 0, 0.6) inset;
}
.wood .lighting:after {
    box-shadow: 0 0 42px 15px rgba(255, 255, 255, 0.5);
}
.wood .reflection:before {
    opacity: 0.04;
}

.metal {
  position: absolute;
  left: 460px;
}
.metal .face{
  background-image:url(https://i.stack.imgur.com/PGmVN.jpg);
}
.metal .cube-wrapper {
  transform: rotateZ(-32deg);
}
.metal .lighting {
  box-shadow: -20px -30px 100px 0 rgba(0, 0, 0, 0.9) inset, -75px -100px 107px 0 rgba(0, 0, 0, 0.3) inset, 75px 100px 127px 0 rgba(255, 255, 255, 0.23) inset;
}
.metal .lighting:after {
    box-shadow: 0 0 42px 20px rgba(255, 255, 255, 0.7), 0 0 7px 6px rgba(255, 255, 255, 0.9);
}
.metal .reflection:before {
    opacity: 0.2;
}

<body>
  <div style="position:absolute;top:20px;">
    <div class="earth">
      <dir class="clip-circle">
        <div class="cube-wrapper">
          <div class="cube-2">
            <div class="cube">
              <div id="face1" class="face"></div>
              <div id="face2" class="face"></div>
              <div id="face3" class="face"></div>
              <div id="face4" class="face"></div>
              <div id="face5" class="face"></div>
              <div id="face6" class="face"></div>
              <div id="face7" class="face"></div>
              <div id="face8" class="face"></div>
            </div>
          </div>
        </div>
      </dir>
      <div class="circle lighting"></div>
    </div>
    <div class="wood">
      <dir class="clip-circle">
        <div class="cube-wrapper">
          <div class="cube-2">
            <div class="cube">
              <div id="face1" class="face"></div>
              <div id="face2" class="face"></div>
              <div id="face3" class="face"></div>
              <div id="face4" class="face"></div>
              <div id="face5" class="face"></div>
              <div id="face6" class="face"></div>
              <div id="face7" class="face"></div>
              <div id="face8" class="face"></div>
            </div>
          </div>
        </div>
      </dir>
      <div class="circle reflection lighting"></div>
    </div>
    <div class="metal">
      <dir class="clip-circle">
        <div class="cube-wrapper">
          <div class="cube-2">
            <div class="cube">
              <div id="face1" class="face"></div>
              <div id="face2" class="face"></div>
              <div id="face3" class="face"></div>
              <div id="face4" class="face"></div>
              <div id="face5" class="face"></div>
              <div id="face6" class="face"></div>
              <div id="face7" class="face"></div>
              <div id="face8" class="face"></div>
            </div>
          </div>
        </div>
      </dir>
      <div class="circle reflection lighting"></div>
    </div>
  </div>
  <div style="position:absolute;top:240px">
    <div class="earth">
      <div class="cube-wrapper">
        <div class="cube-2">
          <div class="cube">
            <div id="face1" class="face"></div>
            <div id="face2" class="face"></div>
            <div id="face3" class="face"></div>
            <div id="face4" class="face"></div>
            <div id="face5" class="face"></div>
            <div id="face6" class="face"></div>
            <div id="face7" class="face"></div>
            <div id="face8" class="face"></div>
          </div>
        </div>
      </div>
    </div>
    <div class="wood">
        <div class="cube-wrapper">
          <div class="cube-2">
            <div class="cube">
              <div id="face1" class="face"></div>
              <div id="face2" class="face"></div>
              <div id="face3" class="face"></div>
              <div id="face4" class="face"></div>
              <div id="face5" class="face"></div>
              <div id="face6" class="face"></div>
              <div id="face7" class="face"></div>
              <div id="face8" class="face"></div>
            </div>
          </div>
        </div>
    </div>
    <div class="metal">
        <div class="cube-wrapper">
          <div class="cube-2">
            <div class="cube">
              <div id="face1" class="face"></div>
              <div id="face2" class="face"></div>
              <div id="face3" class="face"></div>
              <div id="face4" class="face"></div>
              <div id="face5" class="face"></div>
              <div id="face6" class="face"></div>
              <div id="face7" class="face"></div>
              <div id="face8" class="face"></div>
            </div>
          </div>
        </div>
    </div>
  </div>
  <div style="position:absolute;top:460px;">
    <div class="earth">
      <div class="circle lighting"></div>
    </div>
    <div class="wood">
      <div class="circle reflection lighting"></div>
    </div>
    <div class="metal">
      <div class="circle reflection lighting"></div>
    </div>
  </div>

</body>

19voto

Peter Wishart Points 2307

La réalisation d'une sphère css 3d réaliste, sans un certain niveau d'illusion 2d, nécessiterait de nombreux éléments pour que son périmètre soit lisse.

Cependant, j'ai fait une version de l'exemple de Timo Korinth avec :

  • Découpage des lignes de la grille "face arrière".
  • Approximation de l'ombrage sphérique par le déplacement d'un gradient radial

Il peut être tourné arbitrairement, à condition que l'animation de l'ombrage soit recalculée.

Cette page présente certaines des mathématiques qui sous-tendent la mise en œuvre de l'ombrage sphérique avec CSS qui pourrait être utilisé pour cela.

Edit : Les autres réponses sont plus jolies, alors je l'ai converti en étoile de la mort.

.ball {
  position: absolute;
  top:0px;
  left:0px;
  width: 98vmin;
  height: 98vmin;
  margin: 1vmin;  
  transform-style: preserve-3d;  
  transform: rotateX(-5deg);
}

@keyframes rot{
  0% { transform: rotateY(0deg) rotateX(0deg) rotateZ(0deg); }
  100% { transform: rotateY(360deg) rotateX(0deg) rotateZ(0deg); }
}

.layer {
  position: absolute;
  top: 0px;
  left: 0px;
  width: 98vmin;
  height: 98vmin;
}

.moving
{
  transform-style: preserve-3d;
  transform-origin: 49vmin 49vmin;
  animation: rot 10s linear infinite;
}

.gridplane {
  width: 97vmin;
  height: 97vmin;       
  border-radius: 50%;
  border: 0.5vmin dashed rgb(128, 128, 128);
}

.xline { transform: translateY(1%) rotateX(90deg); }
.xline2 { transform: translateY(-1%) rotateX(90deg); }
.yline { transform: rotateY(90deg); }
.zline { transform: rotateZ(90deg); }

.laser { 
  background-color: rgba(0, 0, 0, 0.05);
  transform:  translateX(-27.7128%) translateY(-27.7128%) rotateY(90deg) translateX(-27.7128%) rotateY(-135deg) rotateX(45deg) scale(0.3) translateY(-25%);
}

.laser2 { 
  background-color: rgba(0, 0, 0, 0.05);
  transform:  translateX(-27.0128%) translateY(-27.0128%) rotateY(90deg) translateX(-27.0128%) rotateY(-135deg) rotateX(45deg) scale(0.2) translateY(-35%);
}

.clip
{
  border-radius: 50%;  
  overflow:hidden;
  transform: translateZ(-0vmin);
}

@keyframes highlightanim {     
  0.00% {left: -150.00%; top: -178.00% }
  12.50% {left: -117.67%; top: -179.64% }
  25.00% {left: -97.69%; top: -195.87% }
  28.75% {left: -95.00%; top: -207.09% }
  32.50% {left: -97.69%; top: -220.70% }
  40.00% {left: -117.67%; top: -240.01% }
  47.50% {left: -150.00%; top: -247.50% }
  55.00% {left: -182.33%; top: -240.01% }
  62.50% {left: -202.31%; top: -220.70% }
  68.75% {left: -205.00%; top: -207.09% }
  75.00% {left: -202.31%; top: -195.87% }
  87.50% {left: -182.33%; top: -179.64% }
  100.00% {left: -150.00%; top: -178.00% }
}     

.shade
{
  position: relative;
  top: -150%;
  left: -150%;
  width: 400%;
  height: 400%;
  background: radial-gradient(at 50% 50%, white, black, grey, black, black);
  animation: highlightanim 10s linear infinite;
}

<div class='ball'>
  <div class='layer moving'>
    <div class='layer gridplane xline'></div>
    <div class='layer gridplane xline2'></div>
    <div class='layer gridplane yline'></div>   
    <div class='layer gridplane zline'></div>  
    <div class='layer gridplane laser'></div>  
    <div class='layer gridplane laser2'></div>  
  </div> 
  <div class='layer clip'>
    <div class='shade'> 
    </div>
  </div>
</div>

7voto

Cole Gwozdecki Points 563

Voici un exemple de sphère / bulle animée, bien que cet exemple soit plutôt une illusion. Je ne sais pas si tout ce que vous demandez est possible en utilisant uniquement du css pur, mais je peux me tromper.

.ball {
  display: inline-block;
  width: 100%;
  height: 100%;
  border-radius: 100%;
  position: relative;
  background: radial-gradient(circle at bottom, #81e8f6, #76deef 10%, #055194 80%, #062745 100%); }
  .ball:before {
    content: "";
    position: absolute;
    top: 1%;
    left: 5%;
    width: 90%;
    height: 90%;
    border-radius: 100%;
    background: radial-gradient(circle at top, white, rgba(255, 255, 255, 0) 58%);
    -webkit-filter: blur(5px);
    filter: blur(5px);
    z-index: 2; }
  .ball:after {
    content: "";
    position: absolute;
    display: none;
    top: 5%;
    left: 10%;
    width: 80%;
    height: 80%;
    border-radius: 100%;
    -webkit-filter: blur(1px);
    filter: blur(1px);
    z-index: 2;
    -webkit-transform: rotateZ(-30deg);
    transform: rotateZ(-30deg); }
  .ball .shadow {
    position: absolute;
    width: 100%;
    height: 100%;
    background: radial-gradient(circle, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.1) 40%, rgba(0, 0, 0, 0) 50%);
    -webkit-transform: rotateX(90deg) translateZ(-160px);
    transform: rotateX(90deg) translateZ(-160px);
    z-index: 1; }
  .ball.plain {
    background: black; }
    .ball.plain:before, .ball.plain:after {
      display: none; }
  .ball.bubble {
    background: radial-gradient(circle at 50% 55%, rgba(240, 245, 255, 0.9), rgba(240, 245, 255, 0.9) 40%, rgba(225, 238, 255, 0.8) 60%, rgba(43, 130, 255, 0.4));
    -webkit-animation: bubble-anim 2s ease-out infinite;
    animation: bubble-anim 2s ease-out infinite; }
    .ball.bubble:before {
      -webkit-filter: blur(0);
      filter: blur(0);
      height: 80%;
      width: 40%;
      background: radial-gradient(circle at 130% 130%, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 0) 46%, rgba(255, 255, 255, 0.8) 50%, rgba(255, 255, 255, 0.8) 58%, rgba(255, 255, 255, 0) 60%, rgba(255, 255, 255, 0) 100%);
      -webkit-transform: translateX(131%) translateY(58%) rotateZ(168deg) rotateX(10deg);
      transform: translateX(131%) translateY(58%) rotateZ(168deg) rotateX(10deg); }
    .ball.bubble:after {
      display: block;
      background: radial-gradient(circle at 50% 80%, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0) 74%, white 80%, white 84%, rgba(255, 255, 255, 0) 100%); }

.stage {
  width: 300px;
  height: 300px;
  display: inline-block;
  margin: 20px;
  -webkit-perspective: 1200px;
  -moz-perspective: 1200px;
  -ms-perspective: 1200px;
  -o-perspective: 1200px;
  perspective: 1200px;
  -webkit-perspective-origin: 50% 50%;
  -moz-perspective-origin: 50% 50%;
  -ms-perspective-origin: 50% 50%;
  -o-perspective-origin: 50% 50%;
  perspective-origin: 50% 50%;
}
body {
  width: 300px;
  margin: 20px auto;
  background: linear-gradient(to bottom, rgba(100, 100, 100, 0.2) 0%, rgba(255, 255, 255, 0.5) 40%, #ffffff 100%);
  background-repeat: no-repeat;
}

@-webkit-keyframes bubble-anim {
  0% {
    -webkit-transform: scale(1);
    transform: scale(1); }

  20% {
    -webkit-transform: scaleY(0.95) scaleX(1.05);
    transform: scaleY(0.95) scaleX(1.05); }

  48% {
    -webkit-transform: scaleY(1.1) scaleX(0.9);
    transform: scaleY(1.1) scaleX(0.9); }

  68% {
    -webkit-transform: scaleY(0.98) scaleX(1.02);
    transform: scaleY(0.98) scaleX(1.02); }

  80% {
    -webkit-transform: scaleY(1.02) scaleX(0.98);
    transform: scaleY(1.02) scaleX(0.98); }

  97%, 100% {
    -webkit-transform: scale(1);
    transform: scale(1); } }

@keyframes bubble-anim {
  0% {
    -webkit-transform: scale(1);
    transform: scale(1); }

  20% {
    -webkit-transform: scaleY(0.95) scaleX(1.05);
    transform: scaleY(0.95) scaleX(1.05); }

  48% {
    -webkit-transform: scaleY(1.1) scaleX(0.9);
    transform: scaleY(1.1) scaleX(0.9); }

  68% {
    -webkit-transform: scaleY(0.98) scaleX(1.02);
    transform: scaleY(0.98) scaleX(1.02); }

  80% {
    -webkit-transform: scaleY(1.02) scaleX(0.98);
    transform: scaleY(1.02) scaleX(0.98); }

  97%, 100% {
    -webkit-transform: scale(1);
    transform: scale(1); } }

<section class="stage">
      <figure class="ball bubble"></figure>
</section>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X