salia_in_red
salia_in_red

Reputation: 67

CSS Animation several div one after another

I have 7 div elements (as a 100x100) and I want each box to turn one after another - so, when the first one is finished with rotating, the next one starts. Best case scenario would be that this would go on forever. But I am not sure where to put infinite. I am asking if I could do the animation delay in for example just one statement, and not in so many lines. The animation itself works as I want, but I am not satisfied of how I have written the code. Is there a simpler way?

This is my code:

div {
  margin: 24px;
  width: 50px;
  height: 50px;
  float: left;
  background: #0f9;
  
  animation: spin 4s cubic-bezier(.84, .13, .40, .96);
}

@keyframes spin {
  0% {
    transform: rotateY(0turn);
  }
  100% {
    transform: rotateY(7turn);
  }
}

.two {
  animation-delay: 4s;
}

.three {
  animation-delay: 8s;
}

.four {
  animation-delay: 12s;
}

.five {
  animation-delay: 16s;
}

.six {
  animation-delay: 20s;
}

.seven {
  animation-delay: 24s;
}
<div></div>
<div class="two"></div>
<div class="three"></div>
<div class="four"></div>
<div class="five"></div>
<div class="six"></div>
<div class="seven"></div>

For now, I do it with delay on every box. It doesn't matter if it is written in plain CSS, JS, or React. I am thankful for every suggestion which makes the code as simple as possible.

Upvotes: 2

Views: 4305

Answers (2)

Didn't need sass in the end, since JS is fair game ;).

Here's my pitch:

  1. Have the animation start only if an element has an additional class (in my example, "run").

  2. Add "run" to the first element.

  3. Add a listener for the animationend event.

  4. If the animation that ended is the relevant one.

    a. Remove the 'run' class from the element.

    b. Get the next sibling (i.e. via the nextElementSibling property) or the first element if this is the last element.

    c. Add the 'run' class to the sibling element.

Here is an implementation of this logic

function sync(animationName) {
  function animationEnd(event) {
    console.log(event.animationName);
    if (event.animationName === animationName) {
      const el = event.target;
      el.classList.remove("run");
      if (el.nextElementSibling) {
        el.nextElementSibling.classList.add("run");
      } else if (el.parentElement && el.parentElement.children && el.parentElement.children[0]) {
        el.parentElement.children[0].classList.add("run");
      }
    }
  }
  window.addEventListener('animationend', animationEnd, true);

  const firstEl = document.querySelector(".hi");
  if (firstEl) {
    firstEl.classList.add("run");
  }
}

Full fiddle of an implementation example (with my own animation, since I based it on a solution to a different question :P + it looks cool).

Finally, here is a fiddle running your example.

Upvotes: 3

Mylio Chang
Mylio Chang

Reputation: 81

consider you have html like this:

<div id="divs">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
</div>

what I can think of is:

  1. for vanilla js:
function generateDelayDivCss() {
  let cssStrings = [1, 2, 3, 4, 5].map(
    (n, idx) =>
      `#divs div:nth-child(${idx + 1}) {
      animation-delay: ${idx * 4}s;
    }`
  );
  return cssStrings.join("");
}

// then append the css from the result of the generateDelayDivCss() to the document
// let css = generateDelayDivCss();
// create <style> element and append to document
  1. for react:
export default function App() {
  return (
    <div className="items">
      {[1, 2, 3, 4, 5].map((n, idx) => (
        <div className="item" style={{ animationDelay: `${idx * 4}s` }} />
      ))}
    </div>
  );
}
  1. for pure css, maybe you can replace the class name (.second, .three ...) on the div and just use css to identify it:
#divs div:nth-child(2) {
  animation-delay: 4s;
}

#divs div:nth-child(3) {
  animation-delay: 8s;
}

... // yes, you still need to write the following animation-delay for each div element :(

The idea of 1. and 3. ways are basically the same, but with js you can support for dynamic number of divs if you want. Both of them remove the class name from the element, so it may be cleaner and avoid some issues in the future (for example, you want to insert another div between the second and the third div, or switching the order of the div elements). The idea of 2. is that you can just use inline style to append different css delay duration on each element.

Also, there are other things like css-preprocessor (as mentioned in comment!) or css-in-js library, which can achieve this.

Upvotes: 2

Related Questions