Search code examples
htmlcssanimationloader

Loader timing issues


I am confused why the after selector is moving faster than the no selector div, can anyone explain why this is the case? The before and after selectors are working perfectly but I cannot seem to edit the actual main div I have the selectors off of. I am kinda new at this and apologize for poor formatting or anything of that sort, But I would greatly appreciate some help on this! I want the inner cicle to move the fastest, the middle to move slower, and the outer the slowest.

https://jsfiddle.net/wnmsfudo/1/

html,
body {
  height: 100vh;
  width: 100vw;
}

.loader {
  margin-top: -80px;
  content: "";
  display: block;
  position: absolute;
  top: 50vh;
  left: 46vw;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  border: 3px solid transparent;
  border-top-color: red;
  animation: spin 2s linear infinite;
}

.loader::before {
  content: "";
  display: block;
  position: absolute;
  top: 12.5px;
  left: 12.5px;
  width: 75px;
  height: 75px;
  border-radius: 50%;
  border: 3px solid transparent;
  border-top-color: green;
  animation: spin 1s linear infinite;
}

.loader::after {
  content: "";
  display: block;
  position: absolute;
  top: -12.5px;
  left: -12.5px;
  width: 125px;
  height: 125px;
  border-radius: 50%;
  border: 3px solid transparent;
  border-top-color: blue;
  animation: spin 3s linear infinite;
}

@keyframes spin {
  0% {
    -webkit-transform: rotate(0deg);
    /* Chrome, Opera 15+, Safari 3.1+ */
    -ms-transform: rotate(0deg);
    /* IE 9 */
    transform: rotate(0deg);
    /* Firefox 16+, IE 10+, Opera */
  }
  100% {
    -webkit-transform: rotate(360deg);
    /* Chrome, Opera 15+, Safari 3.1+ */
    -ms-transform: rotate(360deg);
    /* IE 9 */
    transform: rotate(360deg);
    /* Firefox 16+, IE 10+, Opera */
  }
}
<div class="container">
  <div class="loader"></div>
</div>


Solution

  • The issue is that the entire .loader div rotates once every 2 seconds, and that the ::before and ::after pseudo elements therefore rotate with it. Their own rotation is added to the parent's!

    So the solution is to adapt the pseudo-elements' rotations so that they move relatively to the main element. The ::after even needs to rotate in reverse if you want to make it appear slower.

    html, body {
      height: calc(100% - 16px);
    }
    
    .loader {
      margin-top: -80px;
      content: "";
      display: block;
      position: absolute;
      top: 50vh;
      left: 46vw;
      width: 100px;
      height: 100px;
      border-radius: 50%;
      border: 3px solid transparent;
      border-top-color: red;
      animation: spin 2s linear infinite;
    }
    
    .loader::before {
      content: "";
      display: block;
      position: absolute;
      top: 12.5px;
      left: 12.5px;
      width: 75px;
      height: 75px;
      border-radius: 50%;
      border: 3px solid transparent;
      border-top-color: green;
      animation: spin 5s linear infinite;
    }
    
    .loader::after {
      content: "";
      display: block;
      position: absolute;
      top: -12.5px;
      left: -12.5px;
      width: 125px;
      height: 125px;
      border-radius: 50%;
      border: 3px solid transparent;
      border-top-color: blue;
      animation: spin 5s linear reverse infinite;
    }
    
    @keyframes spin {
      0% {
        -webkit-transform: rotate(0deg);
        /* Chrome, Opera 15+, Safari 3.1+ */
        -ms-transform: rotate(0deg);
        /* IE 9 */
        transform: rotate(0deg);
        /* Firefox 16+, IE 10+, Opera */
      }
      100% {
        -webkit-transform: rotate(360deg);
        /* Chrome, Opera 15+, Safari 3.1+ */
        -ms-transform: rotate(360deg);
        /* IE 9 */
        transform: rotate(360deg);
        /* Firefox 16+, IE 10+, Opera */
      }
    }
    <div class="container">
      <div class="loader">
      </div>
    </div>