【问题标题】:Why can't I animate custom properties (aka CSS variables)?为什么我不能为自定义属性(又名 CSS 变量)设置动画?
【发布时间】:2019-07-02 19:11:00
【问题描述】:

看这个动画:

  • 金色 div 有一个动画,其中自定义属性是动画的
    (@keyframes roll-o-1 动画--o)。
    这一步一步地动画。
  • 银色 div 有一个动画,其中 normal 属性是动画的
    @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-transitions css-animations css-variables


【解决方案1】:

当被问到这个问题时,无法为自定义属性设置动画,正如 @temani afif 正确指出的那样 -

因为 UA 无法解释它们的内容

从那时起,CSS HoudiniCSS Properties and Values API specification 放在一起

本规范扩展了 [css-variables],允许注册 具有值类型、初始值和已定义属性的属性 继承行为,通过两种方法:

一个 JS API,registerProperty() 方法

CSS 规则,@property 规则

现在您可以注册自己的自定义属性 - 包括自定义属性的类型 - 为自定义属性设置动画成为可能。

要通过 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(registerProperty() 为 v78+,@property 为 v85+)edge 和 opera

【讨论】:

  • 感谢您的意见。在此期间,我自己想通了,并没有过多注意您的意见。真丢人。如果将来浏览器支持会更好,我会将其设置为接受的答案。
【解决方案2】:

来自the specification

动画:无

然后

值得注意的是,它们甚至可以被转换或动画化,但由于 UA 无法解释它们的内容,它们总是使用用于任何其他对的“50% 翻转”行为无法智能插值的值。但是,@keyframes 规则中使用的任何自定义属性都会被动画污染,这会影响通过动画属性中的 var() 函数引用它时的处理方式。

因此,基本上,您可以在属性上设置过渡和动画,其中它们的值是使用自定义属性定义的,但您不能为自定义属性执行此操作。

请注意以下示例中的差异,我们可能认为两个动画相同但不是。浏览器知道如何为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;
}
&lt;div class="box"&gt;&lt;/div&gt;

我们有一个过渡,但没有自定义属性。这是为了背景,因为在: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>

【讨论】:

  • 明确一点:在您的转换示例中,您应该区分transition: background 1s;(工作)和transition: --c 1s;(不工作)。
  • @yunzen 完全正确,但我不想使用最后一种语法,因为它不常见,我们可能永远不会这样写。我想着重说明我们可能认为这是自定义属性的过渡,但实际上它是用于背景的。
  • @TemaniAfif 您是否知道规范中此决定背后的原因?在我幸福的无知中,我认为计算之间没有太大区别,比如说 {from{opacity:0}to{opacity:1}}{from{--prop:0}to{--prop:1}},两者都只是属性,都是数字,所以人们会期望两者都可以同样“补间”......
  • @myf 主要有两个原因 (1) 自定义属性还可以包含字符、空格等,不仅数字和字符串不能被动画化,因此有一个只说的定义会很乏味如果它被定义为数字 (2) 自定义属性可以应用于非动画属性,并且我们不知道它将应用于哪个属性,直到我们解决 CSSOM。换句话说,在很多情况下我们无法处理过渡/动画,最简单的方法是使其不可动画化
  • @TemaniAfif 我不明白为什么仅当自定义属性是数字时才进行动画处理很乏味。这是一个简单的条件语句。在你的第二点,谁在乎?如果我们为数字设置动画,然后将其设置在另一个不适用于数字的属性上,那么我希望它不起作用(但自定义属性仍然是动画的)。
【解决方案3】:

并非所有 CSS 属性都是可动画的,您不能为 CSS 变量设置动画。这是您可以动画的属性列表https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties

【讨论】:

  • 但是你不觉得,它们应该是动画的吗?
  • @yunzen 我想这会很棒,但它们不能被动画化,CSS 文档指出,我不知道该告诉你什么。也许下一个 CSS 规范有一些提议的特性,我不知道,现在你不能(解决方法是使用 JS 为值设置动画)
  • @yunzen 不能动画是不合理的(在数值为数字的约束下)。
【解决方案4】:

我可以用新的CSS Properties and Values API Level 1做到这一点
(CSS Houdini 的一部分;W3C 工作草案,截至 2020 年 10 月 13 日)

我只需要使用@property 规则注册我的自定义属性

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

通过syntax 属性,我将这个自定义属性声明为type &lt;number&gt;,这会提示浏览器应该以何种方式计算该属性的过渡或动画效果。

syntax 属性支持的值列在here

Browser compatibility 非常强大,因为这是一个实验性功能并且处于草案状态(另见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>

【讨论】:

    猜你喜欢
    • 2018-11-12
    • 2016-07-05
    • 2013-08-14
    • 2019-09-01
    • 1970-01-01
    • 2020-06-01
    相关资源
    最近更新 更多