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

tl;dr: je voudrais créer une véritable sphère 3d avec CSS - pas seulement une illusion

Note: certains exemples ne répondent pas. Veuillez utiliser le plein écran.


avec CSS pur vous pouvez créer et animer un cube 3d comme ainsi:

#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.

So... la première idée que j'obtiens est d'utiliser border-radius et...bien... il ne fonctionne 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>

donc, j'ai reconsidéré mon approche et j'ai cherché une méthode différente.

j'ai regardé:

puis j'ai essayé à nouveau...le meilleur que j'ai été exagérément compliqué objet 3d illusions .

comme ceci:

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>

Qui sont d'accord jusqu'à ce que vous essayez réellement de les faire tourner sur le axe x ou la axe y comme le cube dans 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 2D plat - qui est prévu considérant 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 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 véritable sphère tridimensionnelle avec du CSS pur? Plus précisément, une qui est couverte - pas seulement un cadre - et qui n'implique pas des centaines d'éléments html.


Notes:

  1. sphères tridimensionnelles ont hauteur, largeur et profondeur - just comme le cube dans mon premier exemple snippet
  2. Je n'ai pas besoin de physique et il n'y a pas besoin de interaction avec l'utilisateur. Juste une rotation animée de la sphère.

ressources supplémentaires:

  1. paulrhayes.com -sphères
  2. 3d (2d illusion) de la Terre avec une Rotation de l'Animation avec les CSS
  3. Interactive CSS sphere
58
demandé sur I haz kode 2017-07-21 15:52:00

7 réponses

à proprement parler, tout "3D" forme à plat é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 changeant son orientation, ce qui l'aide à déterminer la forme de cet objet. mieux.

il fonctionne bien avec les objets non symétriques et les objets faits de polygones (par exemple, cubes), mais la sphère est un cas très spécial: sa projection sur le plan donne toujours juste un cercle. La sphère statique et la sphère rotative ont la même projection, le même cercle. Même dans la vraie vie, si nous regardons une sphère avec une surface uniforme sans aucune marque sur elle (par exemple, une boule de métal polie), il est difficile de déterminer si elle reste immobile ou tourne. Nos yeux en ont besoin indices, quelques détails qui se déplacent le long de la surface de la sphère selon sa géométrie. Plus de tels détails se déplacent de la façon que vous attendez de points sur la surface sphérique pour se déplacer, la plus claire est la perception (bien, illusion) de la sphère tournante.

et voici la clé du problème en faisant une scène CSS qui donnerait une telle perception: pour rendre cette illusion assez forte, nous avons besoin de beaucoup de marques se déplaçant le long des chemins qui se trouvent dans différents plans. Et la seule la façon d'obtenir ceci dans CSS est d'avoir chaque marque comme une boîte CSS séparée (élément ou pseudo-élément). Si notre sphère se compose seulement de marques mobiles, nous avons vraiment besoin de beaucoup d'entre eux pour la voir comme une sphère - donc "des centaines d'éléments" dans la plupart des démos que vous avez vu.

donc si vous voulez faire paraître une sphère réaliste avec un nombre relativement petit d'éléments, vous devriez probablement combiner les effets qui font une "illusion" d'une forme sphérique statique de base (cercle avec gradients radiaux, ombres intérieures, etc.) avec certains éléments qui sont relativement petits (pour rendre moins évident qu'ils sont effectivement 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.

ci-dessous est ma propre tentative de mettre cette approche en pratique. J'ai utilisé 20 éléments circulaires orientés grossièrement comme les faces du icosaèdre régulier (comme les hexagones blancs sur le ballon de football classique). Je les ai groupés en deux groupes chacun faisant un hémisphère pour la commodité (il n'était pas nécessaire, mais il a rendu le style un peu plus simple). Toute 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 comme ils vont du côté proche au côté lointain et arrière) et toujours face à l'écran. 24 éléments au total (pas littéralement "des centaines", au moins:). Pour rendre les cercles plus "bombés" (comme les segments sphériques), j'ai ajouté deux pseudo-éléments à chacun d'eux et les ai légèrement surélevés l'un au-dessus de l'autre. Fonctionne le mieux dans Chrome et Firefox 57+ (dans Firefox 56 - et iOS Safari Il ya encore quelques "scintillement" près des bords). Si vous placez le cercle, vous pouvez voir la scène sans l'image d'arrière-plan (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>
84
répondu Ilya Streltsyn 2017-11-29 19:55:57

comment créer une sphère réelle en 3 dimensions avec du CSS pur?

Eh bien, comme beaucoup l'ont déclaré 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 moment-ci, mais il est possible de créer une illusion d'un objet 3D. Voici mon approche pour résoudre le problème.

pour donner à un oeil humain la capacité de voir un objet sphérique, les points de référence sont nécessaires pour l'œil à suivre. Dans mon cas c'est des lignes qui définissent la forme de la sphère. Les lignes sont obtenues en donnant une bordure à 5 éléments qui sont dans l'ensemble X-axis et 5 éléments qui sont dans l'ensemble Y-axis. Seuls les ensembles X/Y sont dotés d'une frontière, parce que cela fournit suffisamment de référence pour créer l'illusion d'une sphère. Les lignes supplémentaires sur l'axe Z sont tout simplement encombrantes et inutiles. Si toutes les lignes sont éteints, la chose entière apparaît comme un solide sphère" parfaite " (ressemble à un cercle, mais toutes les parties de celui-ci se déplacent et sont présents sur le plan 3D dans le navigateur!).


ce que j'ai fait:
  1. a créé 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'engin entier est tourné sur X, Y, Z axes de la les fonds de chacun des éléments des jeux x, y, z remplissent les espaces vides les uns pour les autres.

  2. chaque série de 5 éléments est tournée sur l'axe X,Y,Z respectivement par incréments de 36 degrés. enter image description here
  3. tous les éléments sont arrondis par border-radius:50%; 3 sets of 5 rounded elements for X Y Z axis
  4. placer le fond du cercle des éléments à un de couleur unie Filled sets if 5 rounded elements
  5. mettez les ensembles ensemble, de sorte qu'ils se chevauchent

    enter image description here
  6. Coupées lacunes mineures, qui est entré en raison de pas assez d'éléments couvrant les espaces vides entre x,y,z des cercles à l'aide de clip-path: circle(96px at center); sur le récipient, et a jeté une ombre/lumière d'effet sur pour sceller l'affaire

    enter image description here s enter image description here

    plus de cercles résulteront en une sphère moins "tendue", mais puisque la performance est soulignée dans la question, un clip rapide de l'ensemble semblait être la chose à faire


La fermeture de la pensée, je voulais exprimer mon appréciation la question étant posée, il m'a vraiment fait réfléchir et est un grand projet qui conduisent à m'apprendre beaucoup de choses sur les capacités 3D de html/css.

merci aussi à toutes les personnes qui ont passé le temps d'ouvrir cette porte, et qui ont trouvé des approches géniales au problème.

j'espère que les fruits de mes recherches sont utiles. Acclamations!

ce stylo est également basé sur Timo L'exemple de 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>
23
répondu Kanstantsin Arlouski 2017-08-01 14:23:13

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

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

une autre belle addition est l'utilisation du pseudo élément :before pour la création de l'effet de réflexion. Ajouter une image prémade du monde sur une sphère avec une certaine opacité peut créer un effet très persuasif. avis également avec le Réflexion Le matériau que vous essayez de créer déterminera la quantité d'opacité que vous voulez pour la réflexion.

Remarque, j'ai utilisé le prisme octogonal pour avoir l'image derrière la scène pour regarder plus Rond lorsque la trasform 3d appliquer sa perspective. Même en utilisant seulement 8 éléments, le résultat est tout à fait réaliste. Résultat plus réaliste peut être fait avec plus de polygones et des formes plus complexes et la texture des mappages mais même alors, pas besoin de trop d'éléments en raison de l'ajout de l'ombre et de la bougie au-dessus de tout.

enfin, pour cacher le reste du prisme octogonal et n'afficher que 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>
16
répondu Ori Shalom 2017-07-30 09:26:47

pour fabriquer une sphère CSS réaliste en 3d, sans un certain niveau d'illusion 2d, il faudrait plusieurs éléments pour qu'elle ait un périmètre lisse.

cependant, j'ai fait une version de L'exemple de Timo Korinth avec:

  • découpage des lignes de quadrillage "faisant face au dos
  • ombre sphérique approximative par déplacement d'un gradient radial

recalculer.

cette page a quelques mathématiques derrière la mise en œuvre de l'ombrage sphérique avec CSS qui pourrait être utilisé pour cela.

Edit: D'autres réponses semblent plus belles, donc converti en une é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>
14
répondu Peter Wishart 2017-07-27 13:42:50

non, ce n'est pas possible selon vos critères. Tous les exemples de choses 3D utilisant seulement HTML et CSS ont des problèmes de performance, parce que ce n'est pas son but.

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

la meilleure façon de créer une véritable sphère 3D est D'utiliser WebGL, qui est une API JavaScript pour créer du contenu 3D.

6
répondu VictorGodoi 2017-07-21 14:23:45

regardez cette - son comme ce que vous avez besoin et avec code snippes vous peut espérer modifier à votre convenance. 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;}
}
4
répondu A Roux 2017-07-24 15:25:27

voici un exemple de sphère / bulle animée, bien que cet exemple soit plus qu'une illusion. Je ne sais pas si tout ce que vous demandez est possible uniquement par 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>
4
répondu Cole Gwozdecki 2017-07-26 20:58:21