htmlcssanimation

How to create a simple orbit animation using 3D CSS


I'm attempting to create a simple loading animation of a moon orbiting a planet. I'm using 3d css to simulate an elliptical orbit but I can't get the animation to look how I would prefer, explained below. I feel I may have over complicated it along the way.

This is where I'm up to so far:

.outer {
  width: vw;
  height: vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.inner {
  width: 100px;
  height: 100px;
  transform: rotate(-25deg);
  display: flex;
  justify-content: center;
  align-items: center;
}

.planet-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transform: perspective(150px) rotateY(75deg);
  transform-style: preserve-3d;
}

.planet-outer {
  animation: planet 2s infinite;
  transform-origin: 50% -25%;
  transform-style: preserve-3d;
  width: 100%;
  height: 100%;
}

.planet {
  width: 15%;
  height: 15%;
  background-color: black;
  border-radius: 999px;
  margin: 75% auto 0 auto;
  transform: perspective(150px) rotateY(-75deg);
}

.back-circle {
  background-color: blue;
  border-radius: 999px;
  position: absolute;
  width: 40%;
  height: 40%;
  top: 50%;
  left: 50%;
  transform: perspective(150px) translate3d(-50%, -50%, 0px);
}

@keyframes planet {
  0% {
    transform: perspective(150px) rotate(0);
  }
  100% {
    transform: perspective(150px) rotate(360deg);
  }
}
<div class="outer">
  <div class="loading-icon">
    <div class="inner">
      <div class="back-circle"></div>
      <div class="planet-container">
        <div class="planet-outer">
          <div class="planet"></div>
        </div>
      </div>
    </div>
  </div>
</div>

And below is a quick diagram of my desired animation:

enter image description here

As you can see, the elliptical orbit & perspective is working well. However, the "moon" is scewed and flipping as it rotates, and is not obscured behind the "planet" on its return orbit. I understand why these aspects are not working correctly, but despite playing around for a while, I am unable to come up with a solid solution to these issues.

Any help would be appreciated, thanks!


Solution

  • Late to the party but here's my 2 cents:
    I would use some CSS 3D for that purpose

    Example:

    * {
      margin: 0;
      box-sizing: border-box;
    }
    
    body {
      overflow: hidden;
      min-height: 100dvh;
    }
    
    .system {
      perspective: 1000px;
      transform-style: preserve-3d;
      height: 180px;
      aspect-ratio: 1;
      rotate: 45deg;
      margin: auto;
    }
    
    .planet,
    .orbit,
    .moon {
      position: absolute;
      transform-style: preserve-3d;
      inset: 0;
      height: calc(var(--size) * 1%);
      margin: auto;
      aspect-ratio: 1;
      border-radius: 50%;
    }
    
    .planet {
      --size: 50;
      background: #0bf;
    }
    
    .orbit {
      --size: 80;
      --duration: 6s;
      box-shadow: 0 0 0 2cqmin #5554, inset 0 0 0 2cqmin #5554;
      animation: orbit calc(var(--duration)) linear infinite;
      rotate: 1 0 0 80deg;
    }
    
    .moon {
      --size: 24;
      height: calc(var(--size) * 1%);
      margin-left: calc(var(--size) * -0.5%);
      background: #000;
      animation: moon calc(var(--duration)) linear infinite;
      rotate: 1 0 0 90deg;
    }
    
    @keyframes orbit { to { transform: rotate3d(0, 0, 1,  360deg); } }
    @keyframes moon  { to { transform: rotate3d(0, 1, 0, -360deg); } }
    <div class="system">
      <div class="planet"></div>
      <div class="orbit">
        <div class="moon"></div>
      </div>
    </div>

    Not perfect (issues are not visible due to the slight 80deg orbit inclination) - but fine for this demo. Gimballing the moon would become pretty hard on more steep angles and would involve too much work to determine all the necessary X Y Z angles in order to keep the moon perfectly facing the camera during the animation. So, this seems quite fine. Obviously the perfect result would be putting the orbit at 90deg, but this slight inclination seems nicer visually.

    For a similar concept see this answer.