skip to Main Content

I want to obtain blinking dots one after one in one direction and then blinking have to go back, and all have to happen in the loop. I have created CSS code with Javascript according to first five dots. What can I do to make shorter all reqired code in CSS? Also I want to make not visible black backgrounds of the dots during blinking.

for (let i = 0; i < 40; i++) {
  const dots = document.createElement("div");
  document.body.appendChild(dots).classList.add("dots");
}
body {
  background-color: #737373;
  display: flex;
  justify-content: space-evenly;
  align-items: center;
  min-height: 100vh;
  overflow: hidden;
}

.dots {
  background-color: rgb(0, 0, 0);
  width: 20px;
  height: 20px;
  border-radius: 50%;
}

.dots:nth-of-type(1)::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  animation: bright 0.3s linear;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 0 3px 3px rgb(255, 255, 255),
    0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
  opacity: 0;
  animation-delay: 0s;
}

.dots:nth-of-type(2)::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  animation: bright 0.3s linear;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 0 3px 3px rgb(255, 255, 255),
    0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
  opacity: 0;
  animation-delay: 0.25s;
}

.dots:nth-of-type(3)::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  animation: bright 0.3s linear;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 0 3px 3px rgb(255, 255, 255),
    0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
  opacity: 0;
  animation-delay: 0.5s;
}

.dots:nth-of-type(4)::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  animation: bright 0.3s linear;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 0 3px 3px rgb(255, 255, 255),
    0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
  opacity: 0;
  animation-delay: 0.75s;
}

.dots:nth-of-type(5)::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  animation: bright 0.3s linear;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 0 3px 3px rgb(255, 255, 255),
    0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
  opacity: 0;
  animation-delay: 1s;
}

@keyframes bright {

  0%,
  100% {
    opacity: 0;
  }

  50% {
    opacity: 1;
  }
}

2

Answers


  1. CSS can do this for you without needing to define the delays for every circle.

    In this snippet, a before pseudo element moves along the circles in steps (see definition of CSS animation steps for more info) with color the same as the background. It will therefore briefly ‘hide’ the blackness of a circle.

    The after pseudo element also moves along in the same steps, but has the blinking part as well.

    This way it looks as though each circle is being individually ‘blinked’ in turn.

    The CSS animation ‘alternate’ value is used to make the animations reverse and they are made infinite.

    for (let i = 0; i < 40; i++) {
      const dots = document.createElement("div");
      document.body.appendChild(dots).classList.add("dots");
    }
    body {
      --n: 40;
      /* number of circles */
      --w: 20px;
      /* width of a circle */
      --t: 0.3s;
      /* blink time */
      background-color: #737373;
      display: flex;
      justify-content: space-evenly;
      align-items: center;
      min-height: 100vh;
      overflow: hidden;
      position: relative;
    }
    
    .dots {
      background-color: rgb(0, 0, 0);
      width: var(--w);
      height: var(--w);
      border-radius: 50%;
    }
    
    body::before {
      content: '';
      position: absolute;
      width: calc(var(--w) + 2px);
      /* a bit wider to compensate for occasional mis-alignment as the system tries to deal with part pixels */
      height: calc(var(--w) + 2px);
      border-radius: 50%;
      animation: move calc(var(--n) * var(--t)) steps(var(--n)) infinite alternate;
      background-color: #737373;
    }
    
    body::after {
      content: "";
      position: absolute;
      width: 20px;
      height: 20px;
      border-radius: 50%;
      animation: bright var(--t) linear infinite alternate, move calc(var(--n) * var(--t)) steps(var(--n)) infinite alternate;
      background-color: rgb(255, 255, 255);
      background-image: linear-gradient(white, white);
      box-shadow: 0 0 3px 3px rgb(255, 255, 255),
        0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
      left: 0;
    }
    
    
    @keyframes bright {
    
      0%,
      100% {
        opacity: 0;
      }
    
      50% {
        opacity: 1;
      }
    }
    
    @keyframes move {
      0% {
        left: calc((100% - (var(--n) * var(--w))) / calc(var(--n) + 1) - 1px);
      }
    
      100% {
        left: calc(100% - 1px);
      }
    }

    Note: CSS variables are used so that the calculation that has to be done to get the starting position of the first circle etc can be found easily using calc. In particular, in this case the justify-content value will determine what the gap is.

    Also, I have kept the same HTML structure as in the question but would recommend using a container for the circles rather than body being the direct parent.

    Login or Signup to reply.
  2. An idea with a single element and no JS

    @property --o {
      syntax: "<number>";
      inherits: false;
      initial-value: 0; 
    }
    .box {
      --n: 10;   /* number of circles */
      --s: 30px; /* size of circle */
      --g: 15px; /* gap */
      --t: 0.3s; /* blink time */
      
      height: calc(var(--s) + var(--g));
      aspect-ratio: var(--n);
      background:
       radial-gradient(50% 50%,rgb(255 255 255/var(--o)) 50%,#0000) no-repeat,
       conic-gradient(#737373 0 0) no-repeat,
       radial-gradient(calc(var(--s)/2),#000 95%,#0000);
      background-size: calc(var(--s) + var(--g)) 100%;
      animation:
        bright var(--t) infinite,
        move calc(var(--n)*var(--t)) steps(var(--n),jump-none) infinite alternate;
    }
    
    @keyframes move {
      0% { background-position: left  }
      to { background-position: right }
    }
    
    @keyframes bright {
      50% {--o: 1}
    }
    
    
    
    body {
      margin: 0;
      min-height: 100vh;
      display: grid;
      place-content: center;
      background: #737373;
    }
    <div class="box"></div>

    And if you want an overflowing effect for the shadow, you can update like below:

    .box {
      --n: 10;   /* number of circles */
      --s: 30px; /* size of circle */
      --g: 15px; /* gap */
      --t: 0.3s; /* blink time */
      
      height: calc(var(--s) + var(--g));
      aspect-ratio: var(--n);
      background:
       conic-gradient(#737373 0 0) no-repeat,
       radial-gradient(calc(var(--s)/2),#000 95%,#0000);
      background-size: calc(var(--s) + var(--g)) 100%;
      animation:
        move calc(var(--n)*var(--t)) steps(var(--n),jump-none) infinite alternate;
      position: relative;
    }
    .box:before {
      content: "";
      position: absolute;
      height: 100%;
      aspect-ratio: 1;
      border-radius: 50%;
      opacity: 0;
      background: #fff;
      filter: blur(10px);
      scale: 1.2;
      animation:
        bright var(--t) infinite,
        move-alt calc(var(--n)*var(--t)) steps(var(--n),jump-none) infinite alternate;
    }
    
    @keyframes move {
      0% { background-position: left  }
      to { background-position: right }
    }
    
    @keyframes move-alt {
      0% { left: 0%;  translate:  0   }
      to { left: 100%;translate: -100%}
    }
    
    @keyframes bright {
      50% {opacity: 1}
    }
    
    
    
    body {
      margin: 0;
      min-height: 100vh;
      display: grid;
      place-content: center;
      background: #737373;
    }
    <div class="box"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search