为什么我不能为自定义属性(又名 CSS 变量)设置动画?
Why can't I animate custom properties (aka CSS variables)?
看这个动画:
- 金色 div 有一个自定义 属性 动画
(@keyframes roll-o-1
动画 --o
)。
这一步一步地动画。
- 银色 div 有一个动画,其中 普通 属性 是动画的
(@keyframes roll-o-2
动画 left
)。
这连续动画。
为什么金色的div动画不流畅?
是否有任何也使用变量的解决方法?
#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;
--o: 0;
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;
}
}
<div id="one"></div>
<br>
<div id="two"></div>
并非所有 CSS 属性都可以设置动画,并且您不能设置 css 变量的动画。这是您可以设置动画的属性列表 https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties
Animatable: no
然后
Notably, they can even be transitioned or animated, but since the UA has no way to interpret their contents, they always use the "flips at 50%" behavior that is used for any other pair of values that can’t be intelligently interpolated. However, any custom property used in a @keyframes rule becomes animation-tainted, which affects how it is treated when referred to via the var() function in an animation property.
所以基本上,您可以在 属性 上使用过渡和动画,其中它们的值是使用自定义 属性 定义的,但您不能为自定义 属性.[=23 执行此操作=]
请注意下面示例中的不同之处,我们可能认为两个动画是相同的,但实际上不是。浏览器知道如何动画 left
但不知道如何动画 left
使用的自定义 属性 (也可以在任何地方使用)
#one {
width: 50px;
height: 50px;
background-color: gold;
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;
--o: 1;
animation: roll-o-2 2s infinite alternate ease-in-out both;
position: relative;
}
@keyframes roll-o-2 {
0% {
left: calc(var(--o) * 1px);
}
50% {
left: calc(var(--o) * 50px);
}
100% {
left: calc(var(--o) * 100px);
}
}
<div id="one"></div>
<br>
<div id="two"></div>
另一个使用转换的例子:
.box {
--c:red;
background:var(--c);
height:200px;
transition:1s;
}
.box:hover {
--c:blue;
}
<div class="box"></div>
我们有一个过渡,但不是针对自定义 属性。它用于背景,因为在 :hover
状态下,我们正在再次评估该值,因此背景将发生变化并发生过渡。
对于动画,即使在关键帧内定义了left
属性,也不会有动画:
#one {
width: 50px;
height: 50px;
background-color: gold;
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;
left: calc(var(--o) * 1px);
}
50% {
--o: 50;
left: calc(var(--o) * 1px);
}
100% {
--o: 100;
left: calc(var(--o) * 1px);
}
}
#two {
width: 50px;
height: 50px;
background-color: silver;
--o: 1;
animation: roll-o-2 2s infinite alternate ease-in-out both;
position: relative;
}
@keyframes roll-o-2 {
0% {
left: calc(var(--o) * 1px);
}
50% {
left: calc(var(--o) * 50px);
}
100% {
left: calc(var(--o) * 100px);
}
}
<div id="one"></div>
<br>
<div id="two"></div>
当提出这个问题时,无法为自定义属性设置动画,正如@temani afif 正确指出的那样 -
since the UA has no way to interpret their contents
从那时起,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
现在您可以注册自己的自定义属性 - 包括自定义类型 属性 - 动画自定义 属性 成为可能。
要通过 CSS 注册自定义 属性 - 使用 @property
规则
@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>
要通过 javascript 注册 属性 - 使用 CSS.registerProperty()
方法:
CSS.registerProperty({
name: "--o",
syntax: "<number>",
initialValue: 0,
inherits: "false"
});
CSS.registerProperty({
name: "--o",
syntax: "<number>",
initialValue: 0,
inherits: "false"
});
#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;
}
}
<div id="one"></div>
<br>
<div id="two"></div>
注意
Browser support 目前仅限于 chrome([=19= 为 v78+,@property
为 v85+)edge 和 opera
我可以用新的 CSS Properties and Values API Level 1
(CSS Houdini 的一部分;W3C 工作草案,截至 2020 年 10 月 13 日)
我只需要使用 @property
规则
注册我的自定义 属性
@property --o {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
通过 syntax
属性 我声明此自定义 属性 为 类型 <number>
,这提示浏览器属性 的过渡或动画计算应该以何种方式进行。
列出 syntax
属性 支持的值 here
Browser compatibility is surprisingly strong, since this is an experimental feature and in draft status (See caniuse 也)。 Chrome Edge 支持,Firefox 和 Safari 不支持。
@property --o {
syntax: "<number>";
inherits: true;
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;
--o: 0;
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;
}
}
<div id="one"></div>
<br>
<div id="two"></div>
也许不是您要找的答案,但我使用 javascript 动画(fx with gsap)实现了这个
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body,html {
height: 100%;
display: flex;
}
.wrapper {
margin: auto 0;
}
.box {
--animate:0;
background-color: tomato;
height: 70px;
width: 70px;
transform: translateX(calc(var(--animate) * 1px)) rotate(calc(var(--animate) * 1deg));
}
</style>
</head>
<body>
<div class="wrapper">
<div class="box"></div>
<button onclick="play()">Play</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js" integrity="sha512-H6cPm97FAsgIKmlBA4s774vqoN24V5gSQL4yBTDOY2su2DeXZVhQPxFK4P6GPdnZqM9fg1G3cMv5wD7e6cFLZQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
const tween = gsap.to(".box",{
"--animate":900,
duration:10
})
tween.pause();
function play() {
tween.progress(0);
tween.play();
}
</script>
</body>
</html>
看这个动画:
- 金色 div 有一个自定义 属性 动画
(@keyframes roll-o-1
动画--o
)。
这一步一步地动画。 - 银色 div 有一个动画,其中 普通 属性 是动画的
(@keyframes roll-o-2
动画left
)。
这连续动画。
为什么金色的div动画不流畅?
是否有任何也使用变量的解决方法?
#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;
--o: 0;
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;
}
}
<div id="one"></div>
<br>
<div id="two"></div>
并非所有 CSS 属性都可以设置动画,并且您不能设置 css 变量的动画。这是您可以设置动画的属性列表 https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties
Animatable: no
然后
Notably, they can even be transitioned or animated, but since the UA has no way to interpret their contents, they always use the "flips at 50%" behavior that is used for any other pair of values that can’t be intelligently interpolated. However, any custom property used in a @keyframes rule becomes animation-tainted, which affects how it is treated when referred to via the var() function in an animation property.
所以基本上,您可以在 属性 上使用过渡和动画,其中它们的值是使用自定义 属性 定义的,但您不能为自定义 属性.[=23 执行此操作=]
请注意下面示例中的不同之处,我们可能认为两个动画是相同的,但实际上不是。浏览器知道如何动画 left
但不知道如何动画 left
使用的自定义 属性 (也可以在任何地方使用)
#one {
width: 50px;
height: 50px;
background-color: gold;
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;
--o: 1;
animation: roll-o-2 2s infinite alternate ease-in-out both;
position: relative;
}
@keyframes roll-o-2 {
0% {
left: calc(var(--o) * 1px);
}
50% {
left: calc(var(--o) * 50px);
}
100% {
left: calc(var(--o) * 100px);
}
}
<div id="one"></div>
<br>
<div id="two"></div>
另一个使用转换的例子:
.box {
--c:red;
background:var(--c);
height:200px;
transition:1s;
}
.box:hover {
--c:blue;
}
<div class="box"></div>
我们有一个过渡,但不是针对自定义 属性。它用于背景,因为在 :hover
状态下,我们正在再次评估该值,因此背景将发生变化并发生过渡。
对于动画,即使在关键帧内定义了left
属性,也不会有动画:
#one {
width: 50px;
height: 50px;
background-color: gold;
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;
left: calc(var(--o) * 1px);
}
50% {
--o: 50;
left: calc(var(--o) * 1px);
}
100% {
--o: 100;
left: calc(var(--o) * 1px);
}
}
#two {
width: 50px;
height: 50px;
background-color: silver;
--o: 1;
animation: roll-o-2 2s infinite alternate ease-in-out both;
position: relative;
}
@keyframes roll-o-2 {
0% {
left: calc(var(--o) * 1px);
}
50% {
left: calc(var(--o) * 50px);
}
100% {
left: calc(var(--o) * 100px);
}
}
<div id="one"></div>
<br>
<div id="two"></div>
当提出这个问题时,无法为自定义属性设置动画,正如@temani afif 正确指出的那样 -
since the UA has no way to interpret their contents
从那时起,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
现在您可以注册自己的自定义属性 - 包括自定义类型 属性 - 动画自定义 属性 成为可能。
要通过 CSS 注册自定义 属性 - 使用 @property
规则
@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>
要通过 javascript 注册 属性 - 使用 CSS.registerProperty()
方法:
CSS.registerProperty({
name: "--o",
syntax: "<number>",
initialValue: 0,
inherits: "false"
});
CSS.registerProperty({
name: "--o",
syntax: "<number>",
initialValue: 0,
inherits: "false"
});
#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;
}
}
<div id="one"></div>
<br>
<div id="two"></div>
注意
Browser support 目前仅限于 chrome([=19= 为 v78+,@property
为 v85+)edge 和 opera
我可以用新的 CSS Properties and Values API Level 1
(CSS Houdini 的一部分;W3C 工作草案,截至 2020 年 10 月 13 日)
我只需要使用 @property
规则
@property --o {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
通过 syntax
属性 我声明此自定义 属性 为 类型 <number>
,这提示浏览器属性 的过渡或动画计算应该以何种方式进行。
列出 syntax
属性 支持的值 here
Browser compatibility is surprisingly strong, since this is an experimental feature and in draft status (See caniuse 也)。 Chrome Edge 支持,Firefox 和 Safari 不支持。
@property --o {
syntax: "<number>";
inherits: true;
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;
--o: 0;
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;
}
}
<div id="one"></div>
<br>
<div id="two"></div>
也许不是您要找的答案,但我使用 javascript 动画(fx with gsap)实现了这个
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body,html {
height: 100%;
display: flex;
}
.wrapper {
margin: auto 0;
}
.box {
--animate:0;
background-color: tomato;
height: 70px;
width: 70px;
transform: translateX(calc(var(--animate) * 1px)) rotate(calc(var(--animate) * 1deg));
}
</style>
</head>
<body>
<div class="wrapper">
<div class="box"></div>
<button onclick="play()">Play</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js" integrity="sha512-H6cPm97FAsgIKmlBA4s774vqoN24V5gSQL4yBTDOY2su2DeXZVhQPxFK4P6GPdnZqM9fg1G3cMv5wD7e6cFLZQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
const tween = gsap.to(".box",{
"--animate":900,
duration:10
})
tween.pause();
function play() {
tween.progress(0);
tween.play();
}
</script>
</body>
</html>