<div class="loading">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    background-color: #66c7f4;
}

@ballSize: 10px;
@containerSize: 150px;
@n: 36;
@pdeg: 360deg / @n;
@ani-duration: 2000ms;

.loading {
    width: @containerSize;
    height: @containerSize;
    margin: 50px auto;
    position: relative;
    border-radius: 50%;
    // outline: 1px solid #fff;

    .dot {
        position: absolute;
        left: 50%;
        top: 50%;
        width: @ballSize;
        height: @ballSize;
        margin-left: -@ballSize / 2;
        margin-top: -@ballSize / 2;
        // background-color: red;
        perspective: 70px;
        transform-style: preserve-3d;

        &::before,
        &::after {
            content: "";
            position: absolute;
            width: 100%;
            height: 100%;
            border-radius: 50%;
        }

        &::before {
            background-color: #000;
            top: -100%;
            animation: moveBlack @ani-duration infinite;
        }

        &::after {
            background-color: #fff;
            top: 100%;
            animation: moveWhite @ani-duration infinite;
        }
    }
}

@keyframes moveWhite {
    0% {
        animation-timing-function: ease-in;
    }

    25% {
        transform: translate3d(0, -100%, -@ballSize);
        animation-timing-function: ease-out;
    }

    50% {
        transform: translate3d(0, -200%, 0);
        animation-timing-function: ease-in;
    }

    75% {
        transform: translate3d(0, -100%, @ballSize);
        animation-timing-function: ease-out;
    }
}

@keyframes moveBlack {
    0% {
        animation-timing-function: ease-in;
    }

    25% {
        transform: translate3d(0, 100%, @ballSize);
        animation-timing-function: ease-out;
    }

    50% {
        transform: translate3d(0, 200%, 0);
        animation-timing-function: ease-in;
    }

    75% {
        transform: translate3d(0, 100%, -@ballSize);
        animation-timing-function: ease-out;
    }
}

.loop(@i) when(@i <= @n) {
    .dot:nth-child(@{i}) {
        // color: red;
        transform: rotate(@pdeg * @i) translateY(-@containerSize / 2);

        &::before,
        &::after {
            animation-delay: -@ani-duration / @n * 6 * @i;
        }
    }
    .loop(@i + 1);
}

.loop(1);
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.