How to create a sphere in css?

9.3k views Asked by At

I have trying to create a 3D sphere using just pure css, but I've been unable to generate the shape required. I've seen a cylinder but I can't find any reference to creating an actual sphere.

My current code looks like:

.red {
  background-color: red;
}
.green {
  background-color: green;
}
.blue {
  background-color: blue;
}
.yellow {
  background-color: yellow;
}
.sphere {
  height: 200px;
  width: 200px;
  border-radius: 50%;
  text-align: center;
  vertical-align: middle;
  font-size: 500%;
  position: relative;
  box-shadow: inset -10px -10px 100px #000, 10px 10px 20px black, inset 0px 0px 10px black;
  display: inline-block;
  margin: 5%;
}
.sphere::after {
  background-color: rgba(255, 255, 255, 0.3);
  content: '';
  height: 45%;
  width: 12%;
  position: absolute;
  top: 4%;
  left: 15%;
  border-radius: 50%;
  transform: rotate(40deg);
}
<div class="sphere red"></div>
<div class="sphere green"></div>
<div class="sphere blue"></div>
<div class="sphere yellow"></div>
<div class="sphere"></div>

however,

  • A: these are just 2D circles, not 3D shapes
  • B: I can't rotate these in 3d (I want to have a spinning image) similar to that of a globe.

Sorry if i missed anything, but I'm not too sure where I should go to ask this.

5

There are 5 answers

2
jbutler483 On BEST ANSWER

The below answer is not an actual 3D shape. It only gives a slight illusion of being 3D, however, depending on your use case, you may be able to 'fake' it:

html,body{margin:0;padding:0;background:#222;}
div{
    height:300px;
    width:300px;
    background:url(http://lorempixel.com/300/300);
    border-radius:50%;
    animation:spin 3s linear infinite;
    transform:rotate(-15deg);
    position:relative;
}
div:before{
    content:"";
    position:absolute;
    bottom:-50px;
    border-radius:50%;
    left:0;
    height:10%;
    width:100%;
    transform:rotate(15deg);
    background:rgba(0,0,0,0.6);
    box-shadow: 0 0 10px 2px rgba(0,0,0,0.6);
    
}
div:after{
    content:"";
    position:absolute;z-index:12;
    top:0;left:0;height:100%;width:100%;border-radius:50%;
box-shadow:inset -20px -20px 20px 2px #222, inset 20px 20px 20px 5px rgba(200,200,200,0.4);    
}
@keyframes spin{
    to{background-position:-300px 0;}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div></div>

It's animating the background-position of the div, and by using box shadows, you could 'mimic' the shadowing of a 3D shape.

1
Persijn On

Sphere like shape

There is no actual 3D shapes in html5
But you can stack 2D shapes on top of each other.
With this in mind you can create pretty a close representation of a sphere.

.container {
  perspective: 1000px;
  //transform-style: preserve-3d;
  width: 300px;
  height: 300px;
  border: 5px solid pink;
}
.circ {
  transform-style: preserve-3d;
  border: 5px solid firebrick;
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform-origin: center;
  transform: translateX(-50%) translateY(-50%);
  transition: transform 2s linear;
}
.circ:nth-of-type(1) {
  height: 10%;
  width: 10%;
  transform: translateX(-50%) translateY(-50%) rotateX(40deg) rotateY(40deg) translateZ(-55px);
}
.circ:nth-of-type(2) {
  height: 20%;
  width: 20%;
  transform: translatex(-50%) translateY(-50%) rotateX(40deg) rotateY(40deg) translateZ(-45px);
}
.circ:nth-of-type(3) {
  height: 30%;
  width: 30%;
  transform: translatex(-50%) translateY(-50%) rotateX(40deg) rotateY(40deg) translateZ(-25px);
}
.circ:nth-of-type(4) {
  height: 40%;
  width: 40%;
  transform: translatex(-50%) translateY(-50%) rotateX(40deg) rotateY(40deg) translateZ(-10px);
}
.circ:nth-of-type(5) {
  height: 40%;
  width: 40%;
  transform: translatex(-50%) translateY(-50%) rotateX(40deg) rotateY(40deg) translateZ(10px);
}
.circ:nth-of-type(6) {
  height: 30%;
  width: 30%;
  transform: translatex(-50%) translateY(-50%) rotateX(40deg) rotateY(40deg) translateZ(25px);
}
.circ:nth-of-type(7) {
  height: 20%;
  width: 20%;
  transform: translatex(-50%) translateY(-50%) rotateX(40deg) rotateY(40deg) translateZ(45px);
}
.circ:nth-of-type(8) {
  height: 10%;
  width: 10%;
  transform: translatex(calc(-50%)) translateY(-50%) rotateX(40deg) rotateY(40deg) translateZ(55px);
}
/*ANIMATION*/

.container:hover .circ:nth-of-type(1) {
  transform: translateX(-50%) translateY(-50%) rotateX(180deg) rotateY(40deg) translateZ(-60px);
}
.container:hover .circ:nth-of-type(2) {
  transform: translatex(-50%) translateY(-50%) rotateX(180deg) rotateY(40deg) translateZ(-40px);
}
.container:hover .circ:nth-of-type(3) {
  transform: translatex(-50%) translateY(-50%) rotateX(180deg) rotateY(40deg) translateZ(-20px);
}
.container:hover .circ:nth-of-type(4) {
  transform: translatex(-50%) translateY(-50%) rotateX(180deg) rotateY(40deg) translateZ(0px);
}
.container:hover .circ:nth-of-type(5) {
  transform: translatex(-50%) translateY(-50%) rotateX(180deg) rotateY(40deg) translateZ(20px);
}
.container:hover .circ:nth-of-type(6) {
  transform: translatex(-50%) translateY(-50%) rotateX(180deg) rotateY(40deg) translateZ(40px);
}
.container:hover .circ:nth-of-type(7) {
  transform: translatex(-50%) translateY(-50%) rotateX(180deg) rotateY(40deg) translateZ(60px);
}
.container:hover .circ:nth-of-type(8) {
  transform: translatex(-50%) translateY(-50%) rotateX(180deg) rotateY(40deg) translateZ(70px);
}
.container:hover {
  background-color: #f2f2f2;
}
<div class="container">
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
</div>

0
Max Payne On

You might want to use 3D rotated circles:

This uses rotated circles to look like a spherical grid. the lesser no. of elements, the better performance.

Some elements have been rotated in X axis, and others in Y axis. I have filled different colours to show this:

#cont {
  perspective: 10000px;
  transform-style: preserve-3d;
  -webkit-animation: rotat 1s linear infinite;
  animation: rotat 10s linear infinite;
  transform-origin: 50% 50% 50%;
}
.circ {
  height: 200px;
  width: 200px;
  border: 2px solid black;
  border-radius: 50%;
  position: absolute;
  top: 50px;
  left: 50%;
  margin-left: -100px;
  transform-origin: 50%;
  transform-style: preserve-3d;
  background: orange;
}
.circ:nth-child(1) {
  transform: rotateX(0deg);
}
.circ:nth-child(2) {
  transform: rotateX(30deg);
}
.circ:nth-child(3) {
  transform: rotateX(60deg);
}
.circ:nth-child(4) {
  transform: rotateX(90deg);
}
.circ:nth-child(5) {
  transform: rotateX(120deg);
}
.circ:nth-child(6) {
  transform: rotateX(150deg);
}
.circ:nth-child(7) {
  transform: rotateX(180deg);
}
/*other side rotated*/

.circ:nth-child(8) {
  transform: rotateY(30deg);
}
.circ:nth-child(9) {
  transform: rotateY(60deg);
}
.circ:nth-child(10) {
  transform: rotateY(90deg);
}
.circ:nth-child(11) {
  transform: rotateY(120deg);
}
.circ:nth-child(12) {
  transform: rotateY(150deg);
}
.circ:nth-child(13) {
  transform: rotateY(180deg);
}
.oth {
  background: crimson;
}
@-webkit-keyframes rotat {
  0% {
    -webkit-transform: rotateY(0deg) translateX(0);
  }
  100% {
    -webkit-transform: rotateY(360deg);
  }
}
@keyframes rotat {
  0% {
    transform: rotateY(0deg);
  }
  100% {
    transform: rotateY(360deg);
  }
}
<div id="cont">
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <!--rotated other side-->
  <div class="circ oth"></div>
  <div class="circ oth"></div>
  <div class="circ oth"></div>
  <div class="circ oth"></div>
  <div class="circ oth"></div>
  <div class="circ oth"></div>
</div>

You can also rotate some elements in Z direction, but that will make it even more buggy. Now if you fill the same colours in circles, it almost looks like a sphere:

#cont {
  perspective: 10000px;
  transform-style: preserve-3d;
  -webkit-animation: rotat 1s linear infinite;
  animation: rotat 10s linear infinite;
  transform-origin: 50% 50% 50%;
}
.circ {
  height: 200px;
  width: 200px;
  border: 2px solid black;
  border-radius: 50%;
  position: absolute;
  top: 50px;
  left: 50%;
  margin-left: -100px;
  transform-origin: 50%;
  transform-style: preserve-3d;
  background: crimson;
}
.circ:nth-child(1) {
  transform: rotateX(0deg);
}
.circ:nth-child(2) {
  transform: rotateX(30deg);
}
.circ:nth-child(3) {
  transform: rotateX(60deg);
}
.circ:nth-child(4) {
  transform: rotateX(90deg);
}
.circ:nth-child(5) {
  transform: rotateX(120deg);
}
.circ:nth-child(6) {
  transform: rotateX(150deg);
}
.circ:nth-child(7) {
  transform: rotateX(180deg);
}
/*other side rotated*/

.circ:nth-child(8) {
  transform: rotateY(30deg);
}
.circ:nth-child(9) {
  transform: rotateY(60deg);
}
.circ:nth-child(10) {
  transform: rotateY(90deg);
}
.circ:nth-child(11) {
  transform: rotateY(120deg);
}
.circ:nth-child(12) {
  transform: rotateY(150deg);
}
.circ:nth-child(13) {
  transform: rotateY(180deg);
}
.o {
  border: none;
}
@-webkit-keyframes rotat {
  0% {
    -webkit-transform: rotateY(0deg);
  }
  100% {
    -webkit-transform: rotateY(360deg);
  }
}
@keyframes rotat {
  0% {
    transform: rotateY(0deg);
  }
  100% {
    transform: rotateY(360deg);
  }
}
<div id="cont">
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <!--rotated other side-->
  <div class="circ o"></div>
  <div class="circ o"></div>
  <div class="circ o"></div>
  <div class="circ o"></div>
  <div class="circ o"></div>
  <div class="circ o"></div>
</div>

0
EasterMedia On

#cont {
  perspective: 10000px;
  transform-style: preserve-3d;
  -webkit-animation: rotat 1s linear infinite;
  animation: rotat 10s linear infinite;
  transform-origin: 50% 50% 50%;
}
.circ {
  height: 200px;
  width: 200px;
  border: 2px solid black;
  border-radius: 50%;
  position: absolute;
  top: 50px;
  left: 50%;
  margin-left: -100px;
  transform-origin: 50%;
  transform-style: preserve-3d;
  background: orange;
}
.circ:nth-child(1) {
  transform: rotateX(0deg);
}
.circ:nth-child(2) {
  transform: rotateX(30deg);
}
.circ:nth-child(3) {
  transform: rotateX(60deg);
}
.circ:nth-child(4) {
  transform: rotateX(90deg);
}
.circ:nth-child(5) {
  transform: rotateX(120deg);
}
.circ:nth-child(6) {
  transform: rotateX(150deg);
}
.circ:nth-child(7) {
  transform: rotateX(180deg);
}
/*other side rotated*/

.circ:nth-child(8) {
  transform: rotateY(30deg);
}
.circ:nth-child(9) {
  transform: rotateY(60deg);
}
.circ:nth-child(10) {
  transform: rotateY(90deg);
}
.circ:nth-child(11) {
  transform: rotateY(120deg);
}
.circ:nth-child(12) {
  transform: rotateY(150deg);
}
.circ:nth-child(13) {
  transform: rotateY(180deg);
}
.oth {
  background: crimson;
}
@-webkit-keyframes rotat {
  0% {
    -webkit-transform: rotateY(0deg) translateX(0);
  }
  100% {
    -webkit-transform: rotateY(360deg);
  }
}
@keyframes rotat {
  0% {
    transform: rotateY(0deg);
  }
  100% {
    transform: rotateY(360deg);
  }
}
<div id="cont">
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <div class="circ"></div>
  <!--rotated other side-->
  <div class="circ oth"></div>
  <div class="circ oth"></div>
  <div class="circ oth"></div>
  <div class="circ oth"></div>
  <div class="circ oth"></div>
  <div class="circ oth"></div>
</div>

0
ZulNs On

I use JavaScript to build the sphere which formed by many div elements. To maintain the browser performance, the div elements are made as little as possible.

var DIAMETER = 200;
var CELLS_PER_CIRCLE = 26;
var IMG_CELL = 'https://sites.google.com/site/zulnasibu/sphere/earth.png';
var NAME = 'sphere';
var WRAP = NAME + '-wrapper';
var _cssRules = '';
var _cellW;
var _cellAmount = 0;
var _imgW;
var _imgH;

function createFace(w, h, rx, ry, tz, ts, tsx, tsy, cname) {
 var face = document.createElement("div");
 var css;
 var cssText =
  'width: ' + w.toFixed(2) + 'px;' +
  'height: ' + h.toFixed(2) + 'px;' +
  'margin-left: ' + (-w / 2).toFixed(2) + 'px;' +
  'margin-top: ' + (-h / 2).toFixed(2) + 'px;' +
  'background: url("' + ts + '") ' + tsx.toFixed(2) + 'px ' + tsy.toFixed(2) + 'px;';
 css = 'transform: rotateY(' + ry.toFixed(2) + 'rad) rotateX(' + rx.toFixed(2) + 'rad) translateZ(' + tz.toFixed(2) + 'px);';
 cssText += addVendorPrefix(css);
 face.className = cname;
 face.style.cssText = cssText;
 return face;
}

function createModel() {
 var wrap = document.createElement("div");
 var model = document.createElement("div");
 wrap.className = WRAP;
 model.className = NAME;
 if (CELLS_PER_CIRCLE % 2 != 0) CELLS_PER_CIRCLE++;
 if (CELLS_PER_CIRCLE < 4) CELLS_PER_CIRCLE = 4;
 var baseAngle = Math.PI / CELLS_PER_CIRCLE;
 var cellAngle = 2 * baseAngle;
 _cellW = DIAMETER * Math.tan(baseAngle);
 _imgW = _cellW * CELLS_PER_CIRCLE;
 _imgH = CELLS_PER_CIRCLE / 2;
 if (CELLS_PER_CIRCLE % 4 == 0) _imgH++;
 _imgH *= _cellW;
 var xc = Math.ceil(CELLS_PER_CIRCLE / -4);
 var yc, rx, ry, tx, ty = -_imgH, tw, cang, cdia, cw;
 for (var x = xc; x <= -xc; x++) {
  rx = x * cellAngle;
  cw = _cellW;
  yc = CELLS_PER_CIRCLE;
  if (Math.abs(rx) == Math.PI / 2)
   yc = 1;
  else if (Math.abs(x) != 1) {
   cang = rx - Math.sign(x) * cellAngle / 2;
   cdia = DIAMETER * Math.cos(cang);
   cw = cdia * Math.tan(baseAngle);
  }
  _cellAmount += yc;
  tw = cw * yc;
  tx = (tw - _imgW) / 2;
  ty += _cellW;
  for (var y = 0; y < yc; y++) {
   ry = y * cellAngle;
   model.appendChild(createFace(cw + 1, _cellW + 1, rx, ry, DIAMETER / 2, IMG_CELL, tx, ty, 'cell' + x.toString() + y.toString()));
   tx -= cw;
  }
 }
 wrap.appendChild(model);
 var style = document.createElement('style');
 style.type = 'text/css';
 if (style.styleSheet)
  style.styleSheet.cssText = _cssRules;
 else
  style.innerHTML = _cssRules;
 document.head.appendChild(style);
 return wrap;
}

function addVendorPrefix(property) {
 return '-webkit-' + property +
   '-moz-' + property +
   '-o-' + property +
   property;
}

function showGeometry(elm) {
 if (elm.checked)
  document.querySelector('.sphere').classList.add('show-geometry');
 else
  document.querySelector('.sphere').classList.remove('show-geometry');
}

document.body.appendChild(createModel());
.sphere-wrapper {
 position: absolute;
 top: 50%;
 left: 50%;
 -webkit-perspective: 1000px;
 -moz-perspective: 1000px;
 -o-perspective: 1000px;
 perspective: 1000px;
}
.sphere {
 position: absolute;
 -webkit-transform-style: preserve-3d;
 -moz-transform-style: preserve-3d;
 -o-transform-style: preserve-3d;
 transform-style: preserve-3d;
 -webkit-transform-origin: center center -100px;
 -moz-transform-origin: center center -100px;
 -o-transform-origin: center center -100px;
 transform-origin: center center -100px;
 -webkit-animation: spin 60s infinite linear;
 -moz-animation: spin 60s infinite linear;
 -o-animation: spin 60s infinite linear;
 animation: spin 60s infinite linear;
}
.sphere div {
 position: absolute;
 -webkit-backface-visibility: hidden;
 -moz-backface-visibility: hidden;
 -o-backface-visibility: hidden;
 backface-visibility: hidden;
}
@-webkit-keyframes spin {
 010.00% {-webkit-transform: rotateX(   0deg) rotateY( 360deg) rotateZ(   0deg);}
 020.00% {-webkit-transform: rotateX( 360deg) rotateY( 360deg) rotateZ(   0deg);}
 030.00% {-webkit-transform: rotateX( 720deg) rotateY( 720deg) rotateZ(   0deg);}
 100.00% {-webkit-transform: rotateX(2880deg) rotateY(3240deg) rotateZ(2520deg);}
}
@-moz-keyframes spin {
 010.00% {-moz-transform: rotateX(   0deg) rotateY( 360deg) rotateZ(   0deg);}
 020.00% {-moz-transform: rotateX( 360deg) rotateY( 360deg) rotateZ(   0deg);}
 030.00% {-moz-transform: rotateX( 720deg) rotateY( 720deg) rotateZ(   0deg);}
 100.00% {-moz-transform: rotateX(2880deg) rotateY(3240deg) rotateZ(2520deg);}
}
@-o-keyframes spin {
 010.00% {-o-transform: rotateX(   0deg) rotateY( 360deg) rotateZ(   0deg);}
 020.00% {-o-transform: rotateX( 360deg) rotateY( 360deg) rotateZ(   0deg);}
 030.00% {-o-transform: rotateX( 720deg) rotateY( 720deg) rotateZ(   0deg);}
 100.00% {-o-transform: rotateX(2880deg) rotateY(3240deg) rotateZ(2520deg);}
}
@keyframes spin {
 010.00% {transform: rotateX(   0deg) rotateY( 360deg) rotateZ(   0deg);}
 020.00% {transform: rotateX( 360deg) rotateY( 360deg) rotateZ(   0deg);}
 030.00% {transform: rotateX( 720deg) rotateY( 720deg) rotateZ(   0deg);}
 100.00% {transform: rotateX(2880deg) rotateY(3240deg) rotateZ(2520deg);}
}
input, input~ label {
 cursor: pointer;
}
input:checked~ label {
 color: #f77;
}
.show-geometry div {
 background: rgba(160, 160, 160, 0.5) !important;
 border: 1px solid #333;
 -webkit-backface-visibility: visible;
 -moz-backface-visibility: visible;
 -o-backface-visibility: visible;
 backface-visibility: visible;
}
<input id="show-geometry" type="checkbox" onchange="showGeometry(this);">
<label for="show-geometry">Show geometry</label>