什么不支持 transition ? 那是你不知道 @property 好不好?

引言

我们都知道在 CSS 中我们能够通过 -- 来自定义 CSS 属性

css 复制代码
:root {
  --width: 20px;
}

定义了自定义属性后, 可以通过 var 进行引用(调用)

css 复制代码
:root {
  --width: 200px;
}

.box {
  width: var(--width);
}

上面 👆🏻 就是我们常用的 CSS 变量, 很强大, 通过它我们可以实现很多原先需要 JS 参与才能实现的功能!!!

那么本文要讲的 @property 功能其实和上文一样, 同样是用于自定 CSS 属性!!! 但是它又和直接使用 -- 来自定义 CSS 属性又有所区别, 而这也正是本文的重点....

一、@property

1.1 简介

@propertyCSS Houdini API 中的一个功能, 通过它我们可以显式地自定义 CSS 属性!! 同时我们还可以为定义的 CSS 属性设置类型检查、默认值以及该属性是否可以被继承!!

@property 语法比较简单, 如下代码所示:

  • @property 自然不用解释, 它就是个关键词用于表示要定义一个 CSS 自定义属性
  • --property-name 则是自定义的 CSS 属性名称
  • 花括号 {} 内则是声明字段(declaration-list), 其实就是对自定义属性的描述(规定)
  • syntax 则是定义属性值的类型, 在本例子中 <color> 表示定义的属性, 它的值是一个颜色
  • inherits 则是表示该属性值是否允许被继承
  • initial-value 则是用于设置默认值
css 复制代码
@property --property-name {
  syntax: "<color>";
  inherits: false;
  initial-value: #c0ffee;
}

用法和之前的保持一致, 都是配合 var() 来使用

html 复制代码
<style>
  @property --color {
    syntax: "<color>";
    inherits: false;
    initial-value: #c0ffee;
  }
 
  .box {
    --color: #000;
    color: var(--color);
  }
</style>
<div class="box">111</div>

补充 1: 如下代码 :root--color: #000; 是无效的, 会被 @property --color 覆盖

html 复制代码
<style>
  @property --color {
    syntax: "<color>";
    inherits: false;
    initial-value: #c0ffee;
  }

  :root {
    --color: #000;
  }
 
  .box {
    color: var(--color);
  }
</style>
<div class="box">111</div>

补充 2: 大部分文章里都提到如下两条规则, 但经过测试发现 syntaxinheritsinitial-value 三个都需要必填, 所以保守点在实际开发中还是能填则填吧

  • 规则中 syntaxinherits 是必需的;
  • initial-value 仅在 syntax 为通用("*") 时是可选的; 否则 initial-value 也是必需的, 如果缺失, 整条规则都将失效且被忽略

1.2 syntax

@property 声明中除了 syntax 比较复杂, 其他两个字段(inheritsinitial-value)则比较简单, 所以这里重点介绍下 syntax!!

  1. syntax 中支持的类型很多, 大部分 CSS 类型都是支持的, 下表是常用的一些类型介绍; 更多信息可查阅 @property/syntax

| 类型 | 介绍 | 有效值 | | --- | --- | | "<length>" | 长度, 用于表示距离尺寸的值, 由一组数字和长度单位组成 | 1px1em1vw | | "<number>" | 数字, 可为整数或小数 | 12.1 | | "<percentage>" | 百分比 | 10%50% | | "<length-percentage>" | <length><percentage>, 即长度或百分比 | 1px1em1vw10%50% | | "<color>" | 颜色, 支持所有 CSS 颜色格式 | red#000#ff0099rgb(255 0 153)hsl(150 30% 60%) | | "<angle>" | 角度值, 由数字加单位组成, 常见单位有: 度(deg)、百分度(grad)、弧度(rad)、圈数(turn) | 90deg38.8grad6.2832rad0.25turn | | "<transform-function>" | transform 变换函数 | rotate(45deg)scale(1.5)translate(10px, 10px) | | "<transform-list>" | 有效的 <transform-function> 列表 | rotate(45deg) translate(10px 10px) | | "<url>" | 任何有效的 url() 值 | url(./1.png) | | "<image>" | 图片类型, 包括 url()、渐变、element() | url(./1.png)linear-gradient(blue, red)element(#colonne3) | | "<integer>" | 整型, 包括正整形、负整形 | 12+12-12 | | "<time>" | 时间, 以秒(s)或毫秒(ms)为单位的时间值 | 1.5s150.25ms | | "<resolution>" | 分辨率, 一般用于描述媒体查询中的分辨率值, 由数字加单位组成, 常见的单位有: dpi(每英寸的点数)、dpcm(每厘米的点数)、dppx(每一 px 的点数) | 1dpcm10dpcm | | "<custom-ident>" | 自定义字符串标识符 | nono79ground-level |

  1. 复杂组合

| 符号 | 介绍 | 例 | 有效值 | | --- | --- | --- | | + | 定义空格分隔的列表 | <length>+: 由空格分隔的一组长度(初始值 initial-value 单位需要统一) | 10px 30px 40px | | # | 定义逗号分隔的列表 | <length>#: 由逗号分隔的一组长度(初始值 initial-value 单位需要统一) | 10px, 30px, 40px | | | | 或, 定义多种情况 | <length>+ | <number>#: 由空格分隔的一组长度 或者 由逗号分隔的一组长度 | 10px 30em 40vw10px, 30em, 40vw |

二、特别之处

既然都是自定义 CSS 属性, 那么使用 @property 和直接进行自定义又有啥不同呢?

2.1 类型校验

是的和常规的 CSS 自定义属性相比, 多了类型校验!! 更加规范!!!

下面使用常规方法自定义了一个 CSS 属性 --color; 初始值为 #000, 后面修改为 20px, 你会发现这个改动是会生效的

css 复制代码
:root {
  --color: #000;
}

.box {
  --color: 20px; /* 生效 */
}

同样的情况放在 @property 就会失效, 因为这里在设置值时会进行类型校验

css 复制代码
@property --color {
  syntax: "<color>";
  inherits: false;
  initial-value: #000;
}

.box {
  --color: 20px; /* 失效 */
}

2.2 继承

常规定义的 CSS 属性, 被作为 var() 变量使用时, 如果该值是无效的, 那么整个属性将失效

html 复制代码
<style>
  :root {
    --color: #ff4d4f;
  }

  .parent {
    --color: #ff9c6e;
  }

  .child {
    --color: 20px;
    color: var(--color); /* 整个属性失效 */
  }
</style>
<div class="parent">
  <div class="child">
    child
  </div>
</div>

同样的情况放在 @property 中, 如果 CSS 自定义属性 --color 允许被继承, 那么在为自定义属性赋值时会先进行类型校验, 如果是值是无效的, 就会 继承 自外部元素

html 复制代码
<style>
  @property --color {
    syntax: "<color>";
    inherits: true;  /* 允许继承 */
    initial-value: #ff4d4f;
  }

  .parent {
    --color: #ff9c6e;
  }

  .child {
    --color: 20px; /* 无效值, --color 将继承自外部元素 */
    color: var(--color); 
  }
</style>
<div class="parent">
  <div class="child">
    child
  </div>
</div>

上文是开启继承的情况, 如果没有启用继承, 那么在值校验时若发现是个无效值, 将直接取 初始值

html 复制代码
<style>
  @property --color {
    syntax: "<color>";
    inherits: false; /* 不允许继承 */
    initial-value: #ff4d4f;
  }

  .parent {
    --color: #ff9c6e;
  }

  .child {
    --color: 20px;
    color: var(--color); /* 整个属性失效 */
  }
</style>
<div class="parent">
  <div class="child">
    child
  </div>
</div>

2.3 自动添加补间动画 (支持「transition」)

直接定义出来的 CSS 属性, 是不支持设置补间动画的!! 也就是该属性在 transitionanimation 中就没办法实现自然的过渡动画

html 复制代码
<style>
  .box {
    padding: 20px;
    background-color: #ffccc7;

    --width: 200px;      /* 自定义属性 */
    width: var(--width);
    transition: --width 0.4s; /* 为 --width 设置过渡动画(不生效) */
  }

  .box:hover {
    --width: 400px;
  }
</style>
<div class="box">box</div>

如图, 当鼠标 hover 到容器 .box 上, 容器宽度变大, 但是宽度的变化是没有过渡动画的, 显得很生硬

这里我们就可以使用 @property 来自定义 CSS 属性, 用它定义出来的 CSS 属性, 就支持自动添加补间动画

这样的话在 transitionanimation 中就可以实现丝滑的过渡动画

下面我们使用 @property 来改造代码:

html 复制代码
<style>
  /* 自定义属性 */
  @property --width {
    syntax: "<length>";
    inherits: false; 
    initial-value: 0px;
  }

  .box {
    padding: 20px;
    background-color: #ffccc7;

    --width: 200px; 
    width: var(--width);
    transition: --width 0.4s; /* 为 --width 设置过渡动画(生效) */
  }

  .box:hover {
    --width: 400px;
  }
</style>
<div class="box">box</div>

如图, 当鼠标 hover 到容器 .box 上, 容器宽度变大, 同时宽度的变化是有过渡动画的, 显得很是丝滑

重点: 需要特别注意的是, 上文中我们并不是为 width 设置了过渡动画, 而是给自定义属性 --width 设置了过渡动画, 然后通过 CSS 变量 var()--width 实时作用于 width

这一点特别关键, 在 CSS 中有很大一部分属性它是不支持补间动画的, 所以很多时候我们无法直接通过 transitionanimation 实现一些比较自然的动画效果, 这时往往需要结合 JS 来实现

现在 @property 的出现将彻底改变这一现状, 对于不支持自动补间动画的属性, 我们完全可以将要 过渡的值 抽离出一个 CSS 自定义属性, 然后针对该属性设置动画, 然后使用 CSS 变量的方式, 将自定义属性实时应用到指定属性上!!

下面来看个例子, 如下代码:

html 复制代码
<style>
  .box {
    width: 200px;
    height: 200px;
    padding: 10px;
    background-image: linear-gradient(#ff4d4f, #597ef7);
    transition: all 1s;
  }

  .box:hover {
    background-image: linear-gradient(#ffa940, #f759ab);
  }
</style>
<div class="box">box</div>

如下, 整个过渡是很生硬的, 即便我们设置了 transition

那么这里我们就可以使用 @property 进行一个改造:

  • 这里我们需要针对背景色进行一个过渡, 所以我们可以抽离出两个自定义属性 --gradient-color-first--gradient-color-second
  • 然后针对 --gradient-color-first--gradient-color-second 设置过渡属性
  • 最后 --gradient-color-first--gradient-color-second 实现平滑的颜色过渡, 间接实现 background-image: linear-gradient() 的平滑过渡
html 复制代码
<style>
  @property --gradient-color-first {
    syntax: "<color>";
    inherits: false; 
    initial-value: #000;
  }

  @property --gradient-color-second {
    syntax: "<color>";
    inherits: false; 
    initial-value: #000;
  }

  .box {
    --gradient-color-first:#ff4d4f;
    --gradient-color-second:#597ef7;

    width: 200px;
    height: 200px;
    padding: 10px;
    background-image: linear-gradient(var(--gradient-color-first), var(--gradient-color-second));
    transition: 1s --gradient-color-first, 1s --gradient-color-second;
  }

  .box:hover {
    --gradient-color-first:#ffa940;
    --gradient-color-second:#f759ab;
  }
</style>
<div class="box">box</div>

最后效果如下:

三、registerProperty()

同样, 在 JS 中我们也可以通过原生提供的 CSS.registerProperty 方法来注册 CSS 自定义属性

如下代码演示, name 则是自定义属性名, 其他字段和 @property 中的描述字段是一样的!!!

js 复制代码
window.CSS.registerProperty({
  name: '--primary-color',
  syntax: '<color>',
  inherits: false,
  initialValue: 'green',
})

四、参考

相关推荐
passerby606110 分钟前
完成前端时间处理的另一块版图
前端·github·web components
掘了17 分钟前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅20 分钟前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅42 分钟前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅1 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊1 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte2 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc