引言
我们都知道在 CSS
中我们能够通过 --
来自定义 CSS
属性
css
:root {
--width: 20px;
}
定义了自定义属性后, 可以通过 var
进行引用(调用)
css
:root {
--width: 200px;
}
.box {
width: var(--width);
}
上面 👆🏻 就是我们常用的 CSS
变量, 很强大, 通过它我们可以实现很多原先需要 JS
参与才能实现的功能!!!
那么本文要讲的 @property
功能其实和上文一样, 同样是用于自定 CSS
属性!!! 但是它又和直接使用 --
来自定义 CSS
属性又有所区别, 而这也正是本文的重点....
一、@property
1.1 简介
@property
是 CSS 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: 大部分文章里都提到如下两条规则, 但经过测试发现
syntax
、inherits
、initial-value
三个都需要必填, 所以保守点在实际开发中还是能填则填吧
- 规则中
syntax
和inherits
是必需的;initial-value
仅在syntax
为通用("*"
) 时是可选的; 否则initial-value
也是必需的, 如果缺失, 整条规则都将失效且被忽略
1.2 syntax
@property
声明中除了 syntax
比较复杂, 其他两个字段(inherits
、initial-value
)则比较简单, 所以这里重点介绍下 syntax
!!
syntax
中支持的类型很多, 大部分CSS
类型都是支持的, 下表是常用的一些类型介绍; 更多信息可查阅 @property/syntax
| 类型 | 介绍 | 有效值 | | --- | --- | | "<length>"
| 长度, 用于表示距离尺寸的值, 由一组数字和长度单位组成 | 1px
、1em
、1vw
| | "<number>"
| 数字, 可为整数或小数 | 1
、2.1
| | "<percentage>"
| 百分比 | 10%
、50%
| | "<length-percentage>"
| <length>
或 <percentage>
, 即长度或百分比 | 1px
、1em
、1vw
、10%
、50%
| | "<color>"
| 颜色, 支持所有 CSS
颜色格式 | red
、#000
、#ff0099
、rgb(255 0 153)
、hsl(150 30% 60%)
| | "<angle>"
| 角度值, 由数字加单位组成, 常见单位有: 度(deg
)、百分度(grad
)、弧度(rad
)、圈数(turn
) | 90deg
、38.8grad
、6.2832rad
、0.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.5s
、150.25ms
| | "<resolution>"
| 分辨率, 一般用于描述媒体查询中的分辨率值, 由数字加单位组成, 常见的单位有: dpi
(每英寸的点数)、dpcm
(每厘米的点数)、dppx
(每一 px
的点数) | 1dpcm
、10dpcm
| | "<custom-ident>"
| 自定义字符串标识符 | nono79
、ground-level
|
- 复杂组合
| 符号 | 介绍 | 例 | 有效值 | | --- | --- | --- | | +
| 定义空格分隔的列表 | <length>+
: 由空格分隔的一组长度(初始值 initial-value
单位需要统一) | 10px 30px 40px
| | #
| 定义逗号分隔的列表 | <length>#
: 由逗号分隔的一组长度(初始值 initial-value
单位需要统一) | 10px, 30px, 40px
| | |
| 或, 定义多种情况 | <length>+ | <number>#
: 由空格分隔的一组长度 或者
由逗号分隔的一组长度 | 10px 30em 40vw
、 10px, 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
属性, 是不支持设置补间动画的!! 也就是该属性在 transition
和 animation
中就没办法实现自然的过渡动画
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
属性, 就支持自动添加补间动画
这样的话在 transition
和 animation
中就可以实现丝滑的过渡动画
下面我们使用 @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
中有很大一部分属性它是不支持补间动画的, 所以很多时候我们无法直接通过 transition
或 animation
实现一些比较自然的动画效果, 这时往往需要结合 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',
})