2

I'm trying to use javascript to change the start and end degree values for keyframes animations.

In :root in my CSS file I create a start and end variable for each of six divs and assign an initial value of "0deg". Then in my javascript file I update each variable every 3 seconds. Theoretically this should mean that each div randomly turns in 90 degree increments.

But, that's not what's happening and I've hit a wall. Interestingly, when I print the values of my CSS rotation variables to the console using getComputedStyle, they are correctly reflecting the javascript-created values. You can see here the console is logging a variety of numbers that match my formula for "rotate0a" and "rotate0b"--the start and end values for the top-left div. Yet that div continuously rotates according to the variable I set in :root.

The CSS transform isn't respecting the new variable values. Any help would be hugely appreciated. Thank you!

Code below and CodePen version here: https://codepen.io/KylePalermo/pen/rNeENwd

let min = 0;
let max = 4;

let x;

let rotVar;
let rotVarStarts = [0, 0, 0, 0, 0, 0];
let rotVarEnds = [];


function getRandomInt() {
  min = Math.ceil(min);
  max = Math.floor(max);
  x = Math.floor(Math.random() * (max - min) + min); 
}

function resetStarts() {
  for (let i = 0; i < 6; i++) {
    rotVarStarts[i] = rotVarEnds[i];
  }
}


function setEnds(){
  for (let i = 0; i < 6; i++) {
    getRandomInt();
    if (x == 0) { 
      rotVar = 0; } else if 
      (x == 1) {
      rotVar = 90; } else if 
      (x == 2) {
      rotVar = 180; } else if 
      (x == 3) { 
      rotVar = 270; } else if 
      (x == 4) {
      rotVar = 360; }
    rotVarEnds[i] = rotVar;   
  }


  document.documentElement.style.setProperty('--rotate0a', rotVarStarts[0] + "deg");
  document.documentElement.style.setProperty('--rotate0b', rotVarEnds[0] + "deg");
  
  document.documentElement.style.setProperty('--rotate1a', rotVarStarts[1] +"deg");
  document.documentElement.style.setProperty('--rotate1b', rotVarEnds[1] + "deg");

  document.documentElement.style.setProperty('--rotate2a', rotVarStarts[2] + "deg");
  document.documentElement.style.setProperty('--rotate2b', rotVarEnds[2] + "deg");

  document.documentElement.style.setProperty('--rotate3a', rotVarStarts[3] + "deg");
  document.documentElement.style.setProperty('--rotate3b', rotVarEnds[3] + "deg");

  document.documentElement.style.setProperty('--rotate4a', rotVarStarts[4] + "deg");
  document.documentElement.style.setProperty('--rotate4b', rotVarEnds[4] + "deg");

  document.documentElement.style.setProperty('--rotate5a', rotVarStarts[5] + "deg");
  document.documentElement.style.setProperty('--rotate5b', rotVarEnds[5] + "deg");
  
  let test1 = getComputedStyle(document.documentElement).getPropertyValue('--rotate0a');
  let test2 = getComputedStyle(document.documentElement).getPropertyValue('--rotate0b');
  
  console.log(test1 + "//" + test2);
  
}


setInterval(function () {
     setEnds();
     resetStarts();
     }, 3000);
:root {
  --rotate0a: 0deg;
  --rotate0b: 90deg;
  --rotate1a: 0deg;
  --rotate1b: 0deg;
  --rotate2a: 0deg;
  --rotate2b: 0deg;
  --rotate3a: 0deg;
  --rotate3b: 0deg;
  --rotate4a: 0deg;
  --rotate4b: 0deg;
  --rotate5a: 0deg;
  --rotate5b: 0deg;
}

.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

.rotater {
  width: 100%;
  background-color: blue;
  padding-bottom: 100%;
  position: relative;
}

.zero {
  animation: rotation0 1s infinite linear;
}

.one {
  animation: rotation1 1s infinite linear;
}

.two {
  animation: rotation2 1s infinite linear;
}

.three {
  animation: rotation3 1s infinite linear;
}

.four {
  animation: rotation4 1s infinite linear;
}

.five {
  animation: rotation5 1s infinite linear;
}

.inner {
  position: absolute;
  top: 0;
  left: 0;
}

@keyframes rotation0 {
  from {
    transform: rotate(var(--rotate0a));
  }
  to {
    transform: rotate(var(--rotate0b));
  }
}

@keyframes rotation1 {
  from {
    transform: rotate(var(--rotate1a));
  }
  to {
    transform: rotate(var(--rotate1b));
  }
}

@keyframes rotation2 {
  from {
    transform: rotate(var(--rotate2a));
  }
  to {
    transform: rotate(var(--rotate2b));
  }
}

@keyframes rotation3 {
  from {
    transform: rotate(var(--rotate3a));
  }
  to {
    transform: rotate(var(--rotate3b));
  }
}

@keyframes rotation4 {
  from {
    transform: rotate(var(--rotate4a));
  }
  to {
    transform: rotate(var(--rotate4b));
  }
}

@keyframes rotation5 {
  from {
    transform: rotate(var(--rotate5a));
  }
  to {
    transform: rotate(var(--rotate5b));
  }
}
<body onload = "setEnds()">
  <div class = "wrapper">
    <div class = "rotater zero">
      <div class = "inner"></div>
    </div>
    <div class = "rotater one">
      <div class = "inner"></div>
    </div>
    <div class = "rotater two">
      <div class = "inner"></div>
    </div>
    <div class = "rotater three">
      <div class = "inner"></div>
    </div>
    <div class = "rotater four">
      <div class = "inner"></div>
    </div>
    <div class = "rotater five">
      <div class = "inner"></div>
    </div>
  </div>
</body>

3 Answers 3

2

In Blink and Webkit browsers, the values for the animation are parsed the first time the animation is set and not at every iteration, nor when the variables change.

You need to force a new animation for it to use the new variables:

  const rotaters = document.querySelectorAll('.rotater');
  rotaters.forEach( (elem) => {
    elem.style.animationName = "none";
  });
  //
  // update the variables here
  //
  document.body.offsetWidth; // force a single reflow
  rotaters.forEach( (elem, i) => {
    elem.style.animationName = "rotation" + i;
  });

let min = 0;
let max = 4;

let x;

let rotVar;
let rotVarStarts = [0, 0, 0, 0, 0, 0];
let rotVarEnds = [];


function getRandomInt() {
  min = Math.ceil(min);
  max = Math.floor(max);
  x = Math.floor(Math.random() * (max - min) + min); 
}

function resetStarts() {
  for (let i = 0; i < 6; i++) {
    rotVarStarts[i] = rotVarEnds[i];
  }
}


function setEnds(){
  for (let i = 0; i < 6; i++) {
    getRandomInt();
    if (x == 0) { 
      rotVar = 0; } else if 
      (x == 1) {
      rotVar = 90; } else if 
      (x == 2) {
      rotVar = 180; } else if 
      (x == 3) { 
      rotVar = 270; } else if 
      (x == 4) {
      rotVar = 360; }
    rotVarEnds[i] = rotVar;   
  }

  
  const rotaters = document.querySelectorAll('.rotater');
  rotaters.forEach( (elem) => {
    elem.style.animationName = "none";
  });

  document.documentElement.style.setProperty('--rotate0a', rotVarStarts[0] + "deg");
  document.documentElement.style.setProperty('--rotate0b', rotVarEnds[0] + "deg");
  
  document.documentElement.style.setProperty('--rotate1a', rotVarStarts[1] +"deg");
  document.documentElement.style.setProperty('--rotate1b', rotVarEnds[1] + "deg");

  document.documentElement.style.setProperty('--rotate2a', rotVarStarts[2] + "deg");
  document.documentElement.style.setProperty('--rotate2b', rotVarEnds[2] + "deg");

  document.documentElement.style.setProperty('--rotate3a', rotVarStarts[3] + "deg");
  document.documentElement.style.setProperty('--rotate3b', rotVarEnds[3] + "deg");

  document.documentElement.style.setProperty('--rotate4a', rotVarStarts[4] + "deg");
  document.documentElement.style.setProperty('--rotate4b', rotVarEnds[4] + "deg");

  document.documentElement.style.setProperty('--rotate5a', rotVarStarts[5] + "deg");
  document.documentElement.style.setProperty('--rotate5b', rotVarEnds[5] + "deg");
  
  let test1 = getComputedStyle(document.documentElement).getPropertyValue('--rotate0a');
  let test2 = getComputedStyle(document.documentElement).getPropertyValue('--rotate0b');
  console.log(test1 + "//" + test2);

  document.body.offsetWidth; // force a single reflow
  rotaters.forEach( (elem, i) => {
    elem.style.animationName = "rotation" + i;
  });
  
}


setInterval(function () {
     setEnds();
     resetStarts();
     }, 3000);
:root {
  --rotate0a: 0deg;
  --rotate0b: 90deg;
  --rotate1a: 0deg;
  --rotate1b: 0deg;
  --rotate2a: 0deg;
  --rotate2b: 0deg;
  --rotate3a: 0deg;
  --rotate3b: 0deg;
  --rotate4a: 0deg;
  --rotate4b: 0deg;
  --rotate5a: 0deg;
  --rotate5b: 0deg;
}

.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

.rotater {
  width: 100%;
  background-color: blue;
  padding-bottom: 100%;
  position: relative;
}

.zero {
  animation: rotation0 1s infinite linear;
}

.one {
  animation: rotation1 1s infinite linear;
}

.two {
  animation: rotation2 1s infinite linear;
}

.three {
  animation: rotation3 1s infinite linear;
}

.four {
  animation: rotation4 1s infinite linear;
}

.five {
  animation: rotation5 1s infinite linear;
}

.inner {
  position: absolute;
  top: 0;
  left: 0;
}

@keyframes rotation0 {
  from {
    transform: rotate(var(--rotate0a));
  }
  to {
    transform: rotate(var(--rotate0b));
  }
}

@keyframes rotation1 {
  from {
    transform: rotate(var(--rotate1a));
  }
  to {
    transform: rotate(var(--rotate1b));
  }
}

@keyframes rotation2 {
  from {
    transform: rotate(var(--rotate2a));
  }
  to {
    transform: rotate(var(--rotate2b));
  }
}

@keyframes rotation3 {
  from {
    transform: rotate(var(--rotate3a));
  }
  to {
    transform: rotate(var(--rotate3b));
  }
}

@keyframes rotation4 {
  from {
    transform: rotate(var(--rotate4a));
  }
  to {
    transform: rotate(var(--rotate4b));
  }
}

@keyframes rotation5 {
  from {
    transform: rotate(var(--rotate5a));
  }
  to {
    transform: rotate(var(--rotate5b));
  }
}
<body onload = "setEnds()">
  <div class = "wrapper">
    <div class = "rotater zero">
      <div class = "inner"></div>
    </div>
    <div class = "rotater one">
      <div class = "inner"></div>
    </div>
    <div class = "rotater two">
      <div class = "inner"></div>
    </div>
    <div class = "rotater three">
      <div class = "inner"></div>
    </div>
    <div class = "rotater four">
      <div class = "inner"></div>
    </div>
    <div class = "rotater five">
      <div class = "inner"></div>
    </div>
  </div>
</body>

Note that Firefox does update the values live, and that this kind of animation is not the only one to suffer from this behavior in Webkit+Blink (I faced it a lot with updating svg referenced elements for instance). Though I'm not sure what are the specs requirements here...


(Ran a bisect against Chromium and found they did expose FF's behavior (using updated var() values) until M54, here is the bug where this change happened, given there is nothing in there mentioning this behavior, I guess it's a side-effect, and maybe an unwanted one, opened BUG 1135443).

Sign up to request clarification or add additional context in comments.

1 Comment

That worked! Thank you very much. I'm very much a beginner when it comes to getting js to communicate properly with CSS. Thank you again and hopefully this q&a helps someone else.
1

The problem is once you initilized the animation with animation: rotation0 1s infinite linear; it doesn't have a reason to refresh it, and picking up your updated values from js, you can easily see it changing if you force the css to start it on :hover (mouse hover to see):

let x,rotVar,min=0,max=4,rotVarStarts=[0,0,0,0,0,0],rotVarEnds=[];function getRandomInt(){min=Math.ceil(min),max=Math.floor(max),x=Math.floor(Math.random()*(max-min)+min)}function resetStarts(){for(let t=0;t<6;t++)rotVarStarts[t]=rotVarEnds[t]}function setEnds(){for(let t=0;t<6;t++)getRandomInt(),0==x?rotVar=0:1==x?rotVar=90:2==x?rotVar=180:3==x?rotVar=270:4==x&&(rotVar=360),rotVarEnds[t]=rotVar;document.documentElement.style.setProperty("--rotate0a",rotVarStarts[0]+"deg"),document.documentElement.style.setProperty("--rotate0b",rotVarEnds[0]+"deg");let t=getComputedStyle(document.documentElement).getPropertyValue("--rotate0a"),e=getComputedStyle(document.documentElement).getPropertyValue("--rotate0b");console.log(t+"//"+e)}setInterval(function(){setEnds(),resetStarts()},3e3);
:root {
  --rotate0a: 0deg;
  --rotate0b: 90deg;
}

.rotater {
  width: 100px;
  height: 50px;
  background-color: blue;
  position: relative;
}

.wrapper:hover .zero {
  animation: rotation0 1s infinite linear;
}

.inner {
  position: absolute;
  top: 0;
  left: 0;
}

@keyframes rotation0 {
  from {
    transform: rotate(var(--rotate0a));
  }
  to {
    transform: rotate(var(--rotate0b));
  }
}
<body onload = "setEnds()"><div class = "wrapper"><div class = "rotater zero"><div class = "inner"></div></div></body>

What I wonder is why won't you just use transition instead of animation, like so:

let x,rotVar,min=0,max=4,rotVarStarts=[0,0,0,0,0,0],rotVarEnds=[];function getRandomInt(){min=Math.ceil(min),max=Math.floor(max),x=Math.floor(Math.random()*(max-min)+min)}function resetStarts(){for(let t=0;t<6;t++)rotVarStarts[t]=rotVarEnds[t]}function setEnds(){for(let t=0;t<6;t++)getRandomInt(),0==x?rotVar=0:1==x?rotVar=90:2==x?rotVar=180:3==x?rotVar=270:4==x&&(rotVar=360),rotVarEnds[t]=rotVar;document.documentElement.style.setProperty("--rotate0a",rotVarStarts[0]+"deg"),document.documentElement.style.setProperty("--rotate0b",rotVarEnds[0]+"deg");let t=getComputedStyle(document.documentElement).getPropertyValue("--rotate0a"),e=getComputedStyle(document.documentElement).getPropertyValue("--rotate0b");console.log(t+"//"+e)}setInterval(function(){setEnds(),resetStarts()},3e3);
:root {
  --rotate0a: 0deg;
  --rotate0b: 90deg;
}

.rotater {
  width: 100px;
  height: 50px;
  background-color: blue;
  position: relative;
  transition: all 1s ease-in-out;
}

.zero {
  transform: rotate(var(--rotate0a));
}

.inner {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
}
<body onload = "setEnds()"><div class = "wrapper"><div class = "rotater zero"><div class = "inner"></div></div></body>

Comments

0

Because you are rotating from 0deg to 0deg which means no transition. Update your variables to following ant you will see it working:

:root {
  --rotate0a: 0deg;
  --rotate0b: 90deg;
  --rotate1a: 0deg;
  --rotate1b: 90deg;
  --rotate2a: 0deg;
  --rotate2b: 90deg;
  --rotate3a: 0deg;
  --rotate3b: 90deg;
  --rotate4a: 0deg;
  --rotate4b: 90deg;
  --rotate5a: 0deg;
  --rotate5b: 90deg;
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.