web-dev-qa-db-fra.com

Comment créer des sphères tridimensionnelles CSS pures?

tl; dr: J'aimerais créer une sphère 3D réelle avec CSS - pas seulement une illusion

Remarque: certains exemples d'extraits de code ne répondent pas. S'il vous plaît utiliser le plein écran.


Avec du CSS pur, vous pouvez créer et animer un cube en 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.

Alors ... la première idée que je reçois 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 repensé mon approche et cherché une méthode différente.

J'ai regardé:

Ensuite, j'ai réessayé ... le meilleur que j'ai obtenu était excessif 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 ça:

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 est correct jusqu'à ce que vous essayiez de les faire pivoter sur le axe x ou le axe y comme le cube de mon premier exemple ... voici ce qui se passe ensuite: (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 est un objet plat 2d - ce qui est utile étant donné que c'est ce que l'élément est


La chose la plus proche que j'ai trouvée est la forme suivante créée dans un tutoriel de 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 CSS pur? Plus spécifiquement, un élément qui est couvert - pas seulement un cadre - et qui ne comporte pas des centaines d’éléments HTML.


Notes:

  1. Les sphères tridimensionnelles ont hauteur, largeur et profondeur - exactement comme le cube de mon premier extrait
  2. Je n'ai besoin d'aucune physique et aucune interaction utilisateur n'est nécessaire. Juste une sphère en rotation animée.

Ressources supplémentaires:

  1. paulrhayes.com - Sphères
  2. 3d (Illusion 2D) Terre avec animation en rotation avec CSS
  3. sphère CSS interactive
59
I haz kode

À proprement parler, any "3D" sur un flat l'écran est plus un illusion d'un objet 3D. Tout ce que nous voyons est une 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.

Cela fonctionne bien avec des objets non symétriques et des objets faits de polygones (par exemple, des cubes), mais la sphère est un cas très spécial: sa projection sur le plan ne donne toujours qu'un cercle. La sphère statique et la sphère en rotation ont la même projection, le même cercle. Même dans la réalité, si on regarde une sphère avec une surface uniforme sans aucune marque (par exemple, une boule en métal poli), il est difficile de déterminer si elle reste immobile ou tourne. Nos yeux ont besoin de conseils, de détails qui bougent à la surface de la sphère en fonction de sa géométrie. Plus ces détails bougent, comme vous le souhaitez, des points de la surface sphérique, plus la perception (bien, 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 renforcer cette illusion, nous avons besoin de nombreuses marques se déplaçant le long de chemins qui se situent 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 distincte (élément ou pseudo-élément). Si notre sphère est constituée de seulement de marques mobiles, nous avons vraiment besoin que beaucoup d'entre elles la voient comme une sphère - donc "des centaines d'éléments" dans la plupart des démos que vous avez vues.

Donc, si vous voulez donner à une sphère une apparence réaliste avec un nombre assez petit d’éléments, vous devrez probablement combiner les effets qui créent une "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 rendre moins évident le fait qu'ils soient réellement plats), orientés le long de la surface de la sphère avec des transformations 3D et animés, ce qui est fondamentalement le même principe que les faces du cube de votre première démo.

Ci-dessous, ma propre tentative de mettre en pratique cette approche. J'ai utilisé 20 éléments circulaires orientés approximativement comme les faces de l'icosaèdre régulier (comme des hexagones blancs sur le ballon de football classique). Je les ai regroupés en deux groupes, chacun formant un hémisphère pour plus de commodité (ce n'était pas nécessaire, mais cela simplifiait le style). La scène 3D entière se compose de la sphère elle-même et du cadre d'arrière-plan (pseudo-élément) qui la traverse près de son centre (un peu plus près, pour réduire le "scintillement" des cercles qui se déplacent de l'arrière vers l'arrière ) et toujours face à l'écran. Donc, 24 éléments au total (pas littéralement "des centaines", du 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. Fonctionne mieux avec Chrome et Firefox 57+ (dans Firefox 56 et iOS Safari, des "scintillements" se produisent 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>
88
Ilya Streltsyn

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

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

Pour permettre à un œil humain de voir un objet sphérique, il lui faut des points de référence. Dans mon cas, ce sont les 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’axe des X et à 5 éléments qui se trouvent dans l’axe des Y. Seuls les ensembles X/Y se voient attribuer une bordure, car cela seul fournit suffisamment de références pour créer l'illusion d'une sphère. Les lignes supplémentaires sur l'axe des Z sont simplement encombrées et sont inutiles. Si toutes les lignes sont désactivées, le tout apparaît comme une solide sphère "parfaite" (on dirait un cercle, mais toutes ses parties bougent et sont présentes sur le plan 3D dans le navigateur!).


Ce que j'ai fait:
  1. Création de 15 éléments HTML représentant chacun un cercle divisé en 3 séries de 5

    La raison de tous ces ensembles est que, lorsque tout l'objet est pivoté sur les axes x, y et z, l'arrière-plan de chacun des éléments des ensembles x, y et z remplit les espaces vides les uns pour les autres.

  2. Chaque ensemble de 5 éléments est pivoté sur les axes X, Y et Z, respectivement, par incréments de 36 degrés. enter image description here
  3. Tous les éléments sont arrondis avec border-radius:50%;3 sets of 5 rounded elements for X Y Z axis
  4. Définissez l’arrière-plan des éléments du cercle sur une couleur unie Filled sets if 5 rounded elements
  5. Assemblez les ensembles pour qu'ils se chevauchent
    enter image description here
  6. Des lacunes mineures coupées dues à un nombre insuffisant d'éléments couvrant les espaces vides entre les cercles x, y et z à l'aide de clip-path: circle(96px at center); sur le conteneur, et un effet d'ombre/de lumière froid ont été mis en place pour sceller l'accord
    enter image description here vs enter image description here

    Plus de cercles donneront lieu à une sphère moins "énervée", mais comme la performance est soulignée dans la question, un extrait rapide du tout semble être la meilleure chose à faire.


Merci également à toutes les personnes qui ont passé le temps nécessaire pour faire pression sur cette ouverture et ont proposé des approches impressionnantes du problème.

J'espère que les fruits de mes recherches seront utiles. À votre santé!

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>
24

Comme déjà mentionné ci-dessus, CSS3 ne peut pas vous fournir de vraies formes 3D, mais juste l'illusion. Une bonne illusion de sphère qui utilise un minimum d'éléments HTML et utilise l'image en tant que texture peut être réalisée en combinant ce que vous avez fait et en masquant le tout avec des ombres CSS.

Une belle touche qui peut rendre le masque plus réaliste est l’utilisation du pseudo-élément :after Pour la création d’un élément supplémentaire spark dans un emplacement décalé et dans une taille plus petite. L’effet réussi est de rappeler que différents matériaux reflètent la lumière différemment, ce qui signifie que si vous essayez de créer une sphère métallique, l’éclairage créé par le paramètre box-shadow sera différent de celui d’une sphère de plastique.

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

Remarquez, j'ai utilisé un prisme octogonal pour que l'image derrière la scène soit plus ronde lorsque la transformation 3D applique sa perspective. Même avec 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 de formes plus complexes et de mappages de texture, mais même dans ce cas, il n'est pas nécessaire d'avoir trop d'éléments en raison de l'ajout de l'ombre et du spark au-dessus de tout.

Enfin, afin de masquer le reste du prisme octogonal et d’afficher uniquement les parties situées à 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>
17
Ori Shalom

Réaliser une sphère 3d réaliste en css, sans illusion 2D, nécessiterait de nombreux éléments pour que son périmètre soit lisse.

Cependant, j'ai réalisé une version de l'exemple de Timo Korinth avec:

  • Coupure des lignes de la grille "dos à la face"
  • Ombrage sphérique approximatif en déplaçant un dégradé radial

Peut être pivoté arbitrairement tant que l'animation d'ombrage est recalculée.

Cette page a quelques maths derrière l’implémentation de l’ombrage sphérique avec CSS qui pourrait être utilisé à cet effet.

Edit: Les autres réponses sont plus jolies, donc converties en étoile mortelle

.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>
15
Peter Wishart

Non, ce n'est pas possible selon vos critères. Tous les exemples d'objets 3D utilisant uniquement HTML et CSS ont des problèmes de performances, car ce n'est pas leur objectif.

Quand il s'agit d'effets graphiques lourds, HTML et CSS sont vraiment mauvais.

Le meilleur moyen de créer une véritable sphère 3D consiste à utiliser WebGL, une API JavaScript permettant de créer du contenu 3D.

6
VictorGodoi

Jetez un coup d'œil à ceci - cela ressemble à ce dont vous avez besoin et, avec des extraits de code, vous pouvez le modifier à votre guise. https://codepen.io/Mamboleoo/post/sphere-css

HTML

.mommy
.daddy
  - for (var x = 1; x < 300; x++)
    span

CSS

@import "compass";

body{
  margin: 0;
  display: flex;
  height: 100vh;
  overflow: hidden;
  justify-content: center;
  align-items: center;
  background:black;
}

.mommy{
  width: 500px;
  height: 500px;
  position: relative;
  perspective: 800px;
}
.daddy{
  width: 500px;
  height: 500px;
  transform-style: preserve-3d;
  animation : rotate 25s infinite linear;
}
span{
  display: inline-block;
  position: absolute;
  top:50%;
  left:50%;
  perspective: 800px;
  transform-style: preserve-3d;
  width: 0;
  height: 0;
  &:before{
    content:"";
    width: 4px;
    height: 4px;
    display: inline-block;
    position: absolute;
    top: calc(50% - 2px);
    left: calc(50% - 2px);
    background: currentColor;
    color: inherit;
    border-radius: 50%;
    animation: invertRotate 25s infinite linear, scale 2s infinite linear;
    box-shadow: 0 0 10px currentColor;
  }
}

$amount : 300;
@for $i from 1 through $amount {

  $theta : ($i / $amount)*120;
  $phi : ($i / $amount) * pi();
  $x : 250 * sin($phi) * cos($theta);
  $y : 250 * sin($phi) * sin($theta);
  $z : 250 * cos($phi);
  .mommy span:nth-child(#{$i}){
    transform: translate3d($x+px, $y+px, $z+px);
    color: hsl(($i/$amount)*360,100%,50%);
    &:before{
      animation-delay: 0s, -($i/$amount)*2+s;
    }
  }  
}

@keyframes rotate{
  to{transform:rotateY(360deg);}
}
@keyframes invertRotate{
  to{transform:rotateY(-360deg);}
}
@keyframes scale{
  0%, 45%,55%{ box-shadow: 0 0 10px 0px  currentColor;}
  50%{ box-shadow: 0 0 10px 5px currentColor;}
}
5
A Roux

Voici un exemple de sphère/bulle animée, bien que cet exemple soit davantage une illusion. Je ne sais pas si tout ce que vous demandez est possible grâce au css pur, mais je me trompe peut-être.

.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>
4
Cole Gwozdecki