smpa01
smpa01

Reputation: 4282

How to stack keyframes in CSS animation programmatically

I am trying to stack CSS keyframes (animation-delay) on the following and I am not sure how to do this programmatically?

        .r1 {
          animation-name: move1;
          animation-delay: 0.5s;      
          animation-duration: 1s;
          animation-iteration-count: 1;
          animation-timing-function: ease-in;
          animation-direction: normal;
          animation-fill-mode: forwards;
        }
 
        .r2 {
          animation-name: move1;
          animation-delay: 1.5s;      
          animation-duration: 1s;
          animation-iteration-count: 1;
          animation-timing-function: ease-in;
          animation-direction: normal;
          animation-fill-mode: forwards;
        }
             .r3 {
          animation-name: move1;
          animation-delay: 2.5s;      
          animation-duration: 1s;
          animation-iteration-count: 1;
          animation-timing-function: ease-in;
          animation-direction: normal;
          animation-fill-mode: forwards;
        }
 
        @keyframes move1 {
          to {
            transform: translateX(200px);
          }  
        }
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<rect class="r1" x="10" y="20" width="100" height="100" fill="red"/>
    <rect class="r2" x="10" y="130" width="100" height="100" fill="green"/>
    <rect class="r3" x="10" y="240" width="100" height="100" fill="blue"/>
</svg>

The animation-duration is hardcoded for each class and the animation-delay is hardcoded for the first class, i.e.r1.

How can I pass on the delay for r2 and r3, such as

r2 delay= r1 delay + r1 duration->0.5+1=1.5s

and

r3 delay= r2 delay + r2 duration ->1.5+1=2.5s

Is there anything in javascript that gives animation-duration by class?

I tried doing this by Element.getAnimations() but I am not sure if there is anything that gives animation duration by class.

I don't want to do this manually as I have a lot of class in the svg.

Thank you in advance.

Upvotes: 3

Views: 432

Answers (2)

gru
gru

Reputation: 3069

Animation chaining: use the event animationend

You can chain CSS animations by using a listener on this event. It will be triggered when an animation on an element has been completed (including animation delays). Alternatively, if you use transitions, you need to use the transitionend event.

Basic workflow

Extract the animation properties into a separate class, e.g. .animation.

#firstEl.animation {
  // your animation properties
}
#secondEl.animation {
  // your animation properties
}
// etc
const firstEl = document.getElementById('#firstEl');
const secondEl = document.getElementById('#secondEl');
const thirdEl = document.getElementById('#thirdEl');

firstEl.classList.add('animation');
firstEl.addEventListener('animationend', () => {
  // start the 2nd animation
  secondEl.classList.add('animation');
});
secondEl.addEventListener('animationend', () => {
  // start the 3rd animation
  thirdEl.classList.add('animation');
});
// etc

Example

Here is an example with chained animations (from the docs):

const animation = document.querySelector('p.animation');
const animationEventLog = document.querySelector('.animation-example>.event-log');
const applyAnimation = document.querySelector('.animation-example>button.activate');
let iterationCount = 0;

animation.addEventListener('animationstart', () => {
  animationEventLog.textContent = `${animationEventLog.textContent}'animation started' `;
});

animation.addEventListener('animationiteration', () => {
  iterationCount++;
  animationEventLog.textContent = `${animationEventLog.textContent}'animation iterations: ${iterationCount}' `;
});

animation.addEventListener('animationend', () => {
  animationEventLog.textContent = `${animationEventLog.textContent}'animation ended'`;
  animation.classList.remove('active');
  applyAnimation.textContent = "Activate animation";
});

animation.addEventListener('animationcancel', () => {
  animationEventLog.textContent = `${animationEventLog.textContent}'animation canceled'`;
});

applyAnimation.addEventListener('click', () => {
  animation.classList.toggle('active');
  animationEventLog.textContent = '';
  iterationCount = 0;
  let active = animation.classList.contains('active');
  if (active) {
    applyAnimation.textContent = "Cancel animation";
  } else {
    applyAnimation.textContent = "Activate animation";
  }
});
.container {
  height: 3rem;
}

.event-log {
  width: 25rem;
  height: 2rem;
  border: 1px solid black;
  margin: .2rem;
  padding: .2rem;
}

.animation.active {
  animation-duration: 2s;
  animation-name: slidein;
  animation-iteration-count: 2;
}

@keyframes slidein {
  from {
    margin-left: 100%;
    width: 300%;
  }
  to {
    margin-left: 0%;
    width: 100%;
  }
}
<div class="animation-example">
  <div class="container">
    <p class="animation">You chose a cold night to visit our planet.</p>
  </div>
  <button class="activate" type="button">Activate animation</button>
  <div class="event-log"></div>
</div>

Upvotes: 0

Yan Koshelev
Yan Koshelev

Reputation: 1129

Set r class for your rects and I think this can help you:

const blocks = document.querySelectorAll('svg rect');

for (let i = 1; i<blocks.length; i++) {
    const element = blocks[i];
  const prevElementStyles = getComputedStyle(blocks[i-1]);

  
  element.style.animationDelay = `${parseFloat(prevElementStyles.animationDelay) + parseFloat(prevElementStyles.animationDuration)}s`;
}

r class:

.r {
  animation-name: move1;
  animation-delay: 0.5s;      
  animation-duration: 1s;
  animation-iteration-count: 1;
  animation-timing-function: ease-in;
  animation-direction: normal;
  animation-fill-mode: forwards;
}

<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<rect class="r" x="10" y="20" width="100" height="100" fill="red"/>
    <rect class="r" x="10" y="130" width="100" height="100" fill="green"/>
    <rect class="r" x="10" y="240" width="100" height="100" fill="blue"/>
</svg>

Upvotes: 1

Related Questions