CSS Animate Custom Properties/Variables

Why can't I animate custom properties (aka CSS variables)?

When this question was asked, it wasn't possible to animate custom properties, as @temani afif correctly pointed out -

since the UA has no way to interpret their contents

Since then, CSS Houdini have put together the CSS Properties and Values API specification

This specification extends [css-variables], allowing the registration
of properties that have a value type, an initial value, and a defined
inheritance behaviour, via two methods:

A JS API, the registerProperty() method

A CSS at-rule, the @property rule

So now that you can register your own custom properties - including the type of the custom property - animating the custom property becomes possible.

To register the custom property via CSS - use the @property rule

@property --o {
syntax: "<number>";
inherits: false;
initial-value: 0;
}

#one {
width: 50px;
height: 50px;
background-color: gold;
--o: 0;
animation: roll-o-1 2s infinite alternate ease-in-out both;
position: relative;
left: calc(var(--o) * 1px);
}

@keyframes roll-o-1 {
0% {
--o: 0;
}
50% {
--o: 50;
}
100% {
--o: 100;
}
}

#two {
width: 50px;
height: 50px;
background-color: silver;
animation: roll-o-2 2s infinite alternate ease-in-out both;
position: relative;
}

@keyframes roll-o-2 {
0% {
left: 0px;
}
50% {
left: 50px;
}
100% {
left: 100px;
}
}

@property --o {
syntax: "<number>";
inherits: false;
initial-value: 0;
}
<div id="one"></div>
<br>
<div id="two"></div>

CSS animate custom properties/variables

This can be achieved by defining variables using (as of writing this, not well-supported) @property, which allows declaring types and that allows the browser to "understand", for example, that a certain property (variable) is a Number and then it can gradually animate/transition that variable.

Example Code:

@property --opacity {
syntax: '<number>'; /* <- defined as type number for the transition to work */
initial-value: 0;
inherits: false;
}

@keyframes fadeIn {
50% {--opacity: 1}
}

html {
animation: 2s fadeIn infinite;
background: rgba(0 0 0 / var(--opacity));
}

Why do my CSS variables not affect keyframes animation?

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>

Use a css variable in jQuery .animate

In your case, you can do without CSS variables. Specify individual parameters (height, width, left, top) for each animated element.

You can use the general class of all animated elements, by clicking on which you get the desired parameters (height, width, left, top) of the current element through the css() method.

For example:

$(".a").on("click", function () {
let height_size = $(this).css("height");
let width_size = $(this).css("width");
let left_size = $(this).css("left");
let top_size = $(this).css("top");
...

Next, pass these values to the animate() method.

$(".a").animate(
{
height: height_size,
width: width_size,
top: top_size,
left: left_size,
pointer-events: "all",
},500);


Related Topics



Leave a reply



Submit