Search code examples
css-animations

How can I create this loading animation using only CSS?


I am trying to create this loading animation in HTML and CSS using only CSS to animate it but I can't get the timing down.

enter image description here

Any tips on how I can get that to work?

<div style="display: flex; flex-direction: row; gap: 50px;">
  <div style="height:100px;width:100px; background-color: #ccc">
    <div class="square"></div>
  </div>

  <div style="height:100px;width:100px;border: 1px solid white; position: relative;">
    <div id="squareToReveal-1" class="squareToReveal">1</div>
    <div id="squareToReveal-2" class="squareToReveal">2</div>
    <div id="squareToReveal-3" class="squareToReveal">3</div>
    <div id="squareToReveal-4" class="squareToReveal">4</div>
  </div>
</div>
body {
  background-color: #1E2226;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

.square {
  width: 50px;
  height: 50px;
  background-color: white;
  
  position: relative;
  animation-name: moveSquare;
  animation-duration: 2s;
  animation-iteration-count: infinite;
}

@keyframes moveSquare {
  0%    {top: 0; left: 0}
  25%   {top: 0; left: 50%;}
  50%   {top: 50%; left: 50%;}
  75%   {top: 50%; left: 0px;}
  100%  {top: 0px; left: 0px;}
}

.squareToReveal {
  width: 50px;
  height: 50px;
  background-color: #ccc;
  opacity: 0;
  
  position: absolute;
  animation-name: revealSquare;
  animation-duration: 2s;
  animation-iteration-count: infinite;
}

#squareToReveal-1 {
  top: 0;
  left: 0;
  animation-delay: 0;
}

#squareToReveal-2 {
  top: 0;
  left: 50%;
  animation-delay: 0.5s;
}

#squareToReveal-3 {
  top: 50%;
  left: 50%;
  animation-delay: 1s;
}

#squareToReveal-4 {
  top: 50%;
  left: 0;
  animation-delay: 1.5s;
}

@keyframes revealSquare {
  0%    {opacity: 0;}
  25%   {opacity: 1;}
  75%   {opacity: 1;}
  100%   {opacity: 0;}
}

Solution

  • You can apply animation-timing-function: steps(1); and break the "tiles" animation into the required frames. In fact, we have 8 frames (sorry for the photo quality):

    enter image description here

    body {
      background-color: #1E2226;
      display: grid;
      place-items: center;
      height: 100vh;
      margin: 0;
    }
    
    .loading {
      --size: 100px;
      --duration: 2s;
      position: relative;
      width: var(--size);
      height: var(--size);
      display: flex;
    }
    
    .loading:before {
      content: '';
      position: absolute;
      background-color: gray;
      inset: 0;
      animation: tile calc( var(--duration) * 2 ) steps(1) infinite;
    }
    
    .loading:after {
      content: '';
      width: calc(var(--size) / 2);
      height: calc(var(--size) / 2);
      background-color: #fff;
      animation: move var(--duration) linear infinite;
    }
    
    @keyframes move {
      25% {
        transform: translate(100%, 0);
      }
      50% {
        transform: translate(100%, 100%);
      }
      75% {
        transform: translate(0, 100%);
      }
    }
    
    @keyframes tile {
      0% {
        clip-path: polygon(0 0, 50% 0, 50% 50%, 0 50%);
      }
      12.5% {
        clip-path: polygon(0 0, 100% 0, 100% 50%, 0 50%);
      }
      25% {
        clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 100%, 50% 50%, 0 50%);
      }
      37.5% {
        clip-path: unset;
      }
      50% {
        clip-path: polygon(50% 0, 100% 0, 100% 100%, 0 100%, 0% 50%, 50% 50%);
      }
      62.5% {
        clip-path: polygon(0 50%, 100% 50%, 100% 100%, 0% 100%);
      }
      75% {
        clip-path: polygon(0 50%, 50% 50%, 50% 100%, 0% 100%);
      }
      87.5% {
        clip-path: polygon(0 0);
      }
    }
    <div class="loading"></div>