Random CSS animation time on each iteration

Trying to set a random animation time on each iteration. I set a CSS custom property --animation-time changed randomly each animation iteration with JS.

let square = document.getElementById('square');
let time;

square.addEventListener('animationiteration', duration);

function duration() {
  time = Math.random() + 1;
  square.style.setProperty('--animation-time', time + 's');
  console.log(time);
}
:root {
  --animation-time: 5s;
  --color-1: #ff0000;
  --color-2: #ffee00;
}

@keyframes fire {
  0% {
    background-color: var(--color-1);
  }
  100% {
    background-color: var(--color-2);
  }
}

#square {
  width: 100px;
  height: 100px;
  animation: fire var(--animation-time) ease alternate infinite;
}
<div id="square"></div>

The duration of animation is set to time = Math.random() + 1; so, the iterations should never have less than 1 second. However, it hapens sometimes very quickly. Why?

Here is a CodePen: https://codepen.io/aitormendez/pen/YbvvKw

Answers:

Answer

When you change the animation-duration you will not simply change how each iteration will take but you will change the whole animation. If for example you have 3s as a duration so the first iteration will take 3s and if at 3s (iterationend) you change to 5s it's like you create another animation where the iteration will last 5s and you jump to a new state of that new animation which is 3s of 5s.

Here is an example to better illustrate the issue. I will keep increasing the duration and you will notice that once you reach the end of the iteration you will get back to a previous state because you increased the duration then you will reach again the last state and you will get back and so on.

It's like someone is running to the 100% state and you keep increasing that state.

let square = document.getElementById('square');
let time = 1;

square.addEventListener('animationiteration', duration);

function duration() {
  time*=2;
  square.style.setProperty('--animation-time', time + 's');
  console.log(time);
}
:root {
  --animation-time: 1s;
}

@keyframes fire {
  0% {
    background-color: red;
  }
  50% {
    background:yellow;
  }
  100% {
    background-color: black;
  }
}

#square {
  width: 100px;
  height: 100px;
  animation: fire var(--animation-time) ease infinite alternate;
}
<div id="square"></div>

In the above example I am multiplying the duration by 2 so each time I jump back to start from the 50% state and once I reach the 100% I will repeat the same again.


The same is happening with your code but in a more complex way since you are increasing/decreasing the duration. Decreasing the duration will make you jump to the next iteration and the alternate state.

In other words, You will end with a random flickering animation.

To visually illustrate what is happening here is an animation where I will animate the timeline of your animation

.box {
  height:50px;
  border:1px solid;
  background:
    /* This will illustrate the current state */
    linear-gradient(green,green) left/2px 100% no-repeat,
    /* This is our animation with a duration of 5s (50px) intially*/
    linear-gradient(to right,yellow,red,red,yellow) left/100px 100%;
   
  animation:
    move 10s linear infinite, /* This is the current state*/
    change 2s steps(3) infinite /* this is the change of the animation duration*/
  ; /**/
}

@keyframes move {
  to {
    background-position:right,left;
  }
}
@keyframes change {
  to {
    background-size:2px 100%,40px 100%;
  }
}
<div class="box">

</div>

You can see that we aren't changing the current state (illustrated by the green line) but we are changing the whole animation making the current state jumping in different color randomly.


One idea to obtain what you want is to consider transition and class toggling:

let square = document.getElementById('square');
let time;

/* We intially add the class to trigger the transition*/
setTimeout(function(){square.classList.toggle('change')},10);

square.addEventListener('transitionend', duration);

/* When the transition is done we toggle the class to start another one*/
function duration() {
  time = Math.random() + 1;
  square.style.setProperty('--animation-time', time + 's');
  square.classList.toggle('change');
  console.log(time);
}
:root {
  --animation-time: 1s;
  --color-1: #ff0000;
  --color-2: #ffee00;
}

#square {
  width: 100px;
  height: 100px;
  transition: var(--animation-time) ease;
  background-color: var(--color-1);
}

#square.change {
  background-color: var(--color-2);
}
<div id="square"></div>

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.