CSS @property,开启无限可能的设计探索

186 阅读7分钟

CSS @property,开启无限可能的设计探索

本文主要讲讲 CSS 非常新的一个特性,CSS @Property,它的出现,极大的增强的 CSS 的能力! image.png

CSS Property 定义

根据 MDN -- CSS Property@property css at-ruleCSS Houdini API 的一部分,它允许开发者显式地定义他们的CSS 自定义属性, 允许进行属性类型检查、设定默认值以及定义该自定义属性是否可以被继承。

那么 CSS Houdini 又是什么呢?

Houdini 是一组底层 API,它们公开了 CSS 引擎的各个部分,从而使开发人员能够通过加入浏览器渲染引擎的样式和布局过程来扩展 CSS。

Houdini 是一组 API,它们使开发人员可以直接访问 CSS 对象模型(CSSOM),使开发人员可以编写浏览器可以解析为 CSS 的代码,从而创建新的 CSS 功能,而无需等待它们在浏览器中本地实现。

CSS Property 基础用法

示例

正常而言,我们定义和使用一个 CSS 自定义属性的方法是这样的:

:root {
  --whiteColor: #fff;
}

p {
  color: (--whiteColor);
}

而有了 @property 规则之后,我们还可以像下述代码这样去定义个 CSS 自定义属性:

<style>
@property --property-name {
  syntax: '<color>';
  inherits: false;
  initial-value: #fff;
}

p {
  color: var(--property-name);
}
</style>
  • @property --property-name 中的 --property-name 就是自定义属性的名称,定义后可在 CSS 中通过 var(--property-name) 进行引用
  • syntax:该自定义属性的语法规则,也可以理解为表示定义的自定义属性的类型
  • inherits:是否允许继承
  • initial-value:初始值

其中,@property 规则中的 syntaxinherits 描述符是必需的。initial-value 描述符仅在 syntax 描述符为通用 syntax 定义时是可选的。缺参整条规则都将失效且被忽略。

在 JavaScript 内定义的写法也很简单:

<script>
CSS.registerProperty({
  name: "--property-name",
  syntax: "<color>",
  inherits: false,
  initialValue: "#c0ffee"
});
</script>

支持的 syntax 语法类型

syntax 支持的语法类型非常丰富,基本涵盖了所有你能想到的类型。

  • length
  • number
  • percentage
  • length-percentage
  • color
  • image
  • url
  • integer
  • angle
  • time
  • resolution
  • transform-list
  • transform-function
  • custom-ident (a custom identifier string)

syntax 中的 +#| 符号

定义的 CSS @property 变量的 syntax 语法接受一些特殊的类型定义。

  • syntax: '<color#>' :接受逗号分隔的颜色值列表
  • syntax: '<length+>' :接受以空格分隔的长度值列表
  • syntax: '<length | length+>':接受单个长度或者以空格分隔的长度值列表

看到这里就会有小明问了,为什么要使用这么麻烦的语法定义 CSS 自定义属性呢?CSS Houdini 定义的自定义变量的优势在哪里?

CSS Property 实际应用

使用 color syntax 语法类型作用于渐变

我们来看这样一个例子,我们有这样一个渐变的图案:

<div></div>

div {
  background: linear-gradient(45deg, #fff, #000);
}

image.png

我们改造下上述代码,改为使用 CSS 自定义属性:

:root {
  --colorA: #fff;
  --colorB: #000;
}
div {
  background: linear-gradient(45deg, var(--colorA), var(--colorB));
}

得到的还是同样的一个渐变图:

image.png

我们再加上一个过渡效果:

:root {
  --colorA: #fff;
  --colorB: #000;
}
div {
  background: linear-gradient(45deg, var(--colorA), var(--colorB));
  transition: 1s background;
    
  &:hover {
    --colorA: yellowgreen;
    --colorB: deeppink;
  }
}

看看鼠标 Hover 的时候,会发生什么:

114257638-f2877b80-99f3-11eb-9814-043acef1def0.gif

虽然我们设定了 1s 的过渡动画 transition: 1s background,但是很可惜,CSS 是不支持背景渐变色的直接过渡变化的,我们得到的只是两帧之间的直接变化。

CSS @Property 出手改造

让我们请出本文的主角,使用 Houdini API 中的 CSS 自定义属性替换原本的 CSS 自定义属性。

简单进行改造一下,使用 color syntax 语法类型:

@property --houdini-colorA {
  syntax: '<color>';
  inherits: false;
  initial-value: #fff;
}
@property --houdini-colorB {
  syntax: '<color>';
  inherits: false;
  initial-value: #000;
}
.property {
   background: linear-gradient(45deg, var(--houdini-colorA), var(--houdini-colorB));
   transition: 1s --houdini-colorA, 1s --houdini-colorB;

   &:hover {
     --houdini-colorA: yellowgreen;
     --houdini-colorB: deeppink;
   }
}

我们使用了 @property 语法,定义了两个 CSS Houdini 自定义变量 --houdini-colorA 和 --houdini-colorB,在 hover 变化的时候,改变这两个颜色。

需要关注的是,我们设定的过渡语句 transition: 1s --houdini-colorA, 1s --houdini-colorB,在这里,我们是针对 CSS Houdini 自定义变量设定过渡,而不是针对 background 设定过渡动画,再看看这次的效果:

114261426-11463c00-9a0d-11eb-9e84-8516df950d15.gif

Wow,成功了,渐变色的变化从两帧的逐帧动画变成了补间动画,实现了从一个渐变色过渡到另外一个渐变色的效果!而这,都得益于 CSS Houdini 自定义变量的强大能力!

使用 CSS @Property 实现渐变背景色过渡动画

在上述的 DEMO 中,我们利用了 CSS Houdini 自定义变量,将原本定义在 background 的过渡效果嫁接到了 color 之上,而 CSS 是支持一个颜色变换到另外一个颜色的,这样,我们巧妙的实现了渐变背景色的过渡动画。

@property --colorA {
  syntax: '<color>';
  inherits: false;
  initial-value: fuchsia;
}
@property --colorC {
  syntax: '<color>';
  inherits: false;
  initial-value: #f79188;
}
@property --colorF {
  syntax: '<color>';
  inherits: false;
  initial-value: red;
}
div {
  background: linear-gradient(45deg, var(--colorA), var(--colorC), var(--colorF));
  animation: change 10s infinite linear;
}

@keyframes change {
  20% {
    --colorA: red;
    --colorC: #a93ee0;
    --colorF: fuchsia;
  }
  40% {
    --colorA: #ff3c41;
    --colorC: #e228a0;
    --colorF: #2e4c96;
  }
  60% {
    --colorA: orange;
    --colorC: green;
    --colorF: teal;
  }
  80% {
    --colorA: #ae63e4;
    --colorC: #0ebeff;
    --colorF: #efc371;
  }
}

114261707-9ed65b80-9a0e-11eb-8ad7-f25ac0250cb9.gif

conic-gradient 配合 CSS @Property 实现饼图动画

上面我们演示了 syntax 为 color 语法类型的情况。在文章一开头,我们还列举了非常多的syntax  类型。

下面我们尝试下其他的类型,使用 percentage 百分比类型或者 angle 角度类型,实现一个饼图的 hover 动画。

如果我们还是使用传统的写法,利用角向渐变实现不同角度的饼图:

<div></div>

.normal {
  width: 200px;
  height: 200px;
  border-radius: 50%;
  background: conic-gradient(yellowgreen, yellowgreen 25%, transparent 25%, transparent 100%); 
  transition: background 300ms;

  &:hover {
    background: conic-gradient(yellowgreen, yellowgreen 60%, transparent 60.1%, transparent 100%); 
  }
}

将会得到这样一种效果,由于 conic-gradient 也是不支持过渡动画的,得到的是一帧向另外一帧的直接变化:

114262545-2a51eb80-9a13-11eb-93b8-b1a3e34608fc.gif

好,使用 CSS Houdini 自定义变量改造一下:

@property --per {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 25%;
}

div {
  background: conic-gradient(yellowgreen, yellowgreen var(--per), transparent var(--per), transparent 100%); 
  transition: --per 300ms linear;

  &:hover {
    --per: 60%;
  }
}

看看改造后的效果:

114262645-b6641300-9a13-11eb-8c6e-2b2479cb3137.gif

以往使用纯 CSS 非常复杂才能实现的效果,如今可以轻松的达成,不得不感慨 CSS @property 强大的能力!

syntax 的 | 符号

顺便演示一下定义 Houdini 自定义变量时 syntax 的一些稍微复杂点的用法。

在 conic-gradient 中,我们可以使用百分比也可以使用角度作为关键字,上述的 DEMO 也可以改造成这样:

@property --per {
  syntax: '<percentage> | <angle>';
  inherits: false;
  initial-value: 25%;
}
...

表示,我们的自定义属性即可以是一个百分比值,也可以是一个角度值。

除了 | 符号外,还有 + 和 # 号分别表示接受以空格分隔、和以逗号分隔的属性,感兴趣的可以自行尝试。

使用 length 类型作用于一些长度变化

掌握了上述的技巧,我们就可以利用 Houdini 自定义变量的这个能力,去填补修复以前无法直接过渡动画的一些效果了。

过去,我们想实现这样一个文字下划线的 Hover 效果:

p {
  text-underline-offset: 1px;
  text-decoration-line: underline;
  text-decoration-color: #000;
  transition: all .3s;

  &:hover {
    text-decoration-color: orange;
    text-underline-offset: 10px;
    color: orange;
  }
}

因为 text-underline-offset 不支持过渡动画,得到的结果如下:

114263539-b0bcfc00-9a18-11eb-82e6-7d2131e8bc25.gif

使用 Houdini 自定义变量改造,化腐朽为神奇:

@property --offset {
  syntax: '<length>';
  inherits: false;
  initial-value: 0;
}
div {
  text-underline-offset: var(--offset, 1px);
  text-decoration: underline;
  transition: --offset 400ms, text-decoration-color 400ms;
    
  &:hover {
    --offset: 10px;
    color: orange;
    text-decoration-color: orange;
  }
}

可以得到丝滑的过渡效果:

114263566-dfd36d80-9a18-11eb-9aaa-72bbb7967ab5.gif

参考文献: