🤷♂️ 這是一個實驗性技術,正如你所見,它兼容性目前糟糕!但是 Chrome 的發展是在不斷支持它的,接下來了解一下它。
介紹
@ property CSS at-rule 是 CSS Houdini api 的一部分,它允許開發人員顯式定義 CSS 自定義屬性,允許屬性類型檢查,設置默認值,並定義屬性是否可以繼承值。
該規則直接在樣式表中表示自定義屬性的注冊,無需運行任何 JS。有效的規則會生成一個已注冊的自定義屬性,就好像使用等效的 parameters.@property@property 調用了 CSS.registerProperty 一樣
Houdini 是一組底層 api,它公開了 CSS 引擎的部分內容,使開發人員能夠通過鈎入瀏覽器渲染引擎的樣式和布局過程來擴展 CSS。Houdini 是一組 api,可以讓開發者直接訪問 CSS 對象模型(CSSOM) ,使開發者能夠編寫瀏覽器可以解析為 CSS 的代碼,從而創建新的 CSS 特性,而無需等待這些特性在瀏覽器中自行實現。
語法
@property --propery-name {
syntax: '<color>';
inherits: false;
initial-value: #c0ffee;
}
一個有效的規則表示自定義屬性注冊,屬性名稱是規則的 prelude.@property 中的序列化。
@property 規則需要語法(syntax)和繼承描述符(inheritsdescriptor); 如果缺少其中一個,則整個規則無效,必須忽略。只有在語法是通用語法定義的情況下,初始值描述符才是可選的,否則就需要描述符; 如果缺少初始值描述符,則整個規則無效,必須忽略它。
未知的描述符無效並被忽略,但是不要使 rule.@property 無效。
注意:應該在選擇器塊外面聲明
如果您仍然不是很明白,接下來我們創建一個自定義屬性 --my-color:
@property --my-color {
syntax: '<color>';
inherits: false;
initial-value: #c0ffee;
}
- syntax:語法
- @property --my-color:聲明一個自定義屬性 --mycolor
- inherits:是否允許繼承
- initial-value:初始值
如果使用 Javascript 實現,等價於:
window.CSS.registerProperty({
name: '--my-color',
syntax: '<color>',
inherits: false,
initialValue: '#c0ffee',
});
例子
如果您經常使用 css 的 transition 屬性,試着回想一下,是不是遇到過這種情況:在給某個屬性添加 transition 時,卻沒有生效。看下面這段 css 代碼:
.el {
background: linear-gradient(white, black);
/* this transition won't work */
transition: 1s;
}
.el:hover {
background: linear-gradient(red, black);
}
你可能認為這個漸變中的白色會隨着過渡漸變為紅色,但事實並非如此,這種過渡是不可能的。如果我們過去需要使用一些小技巧實現它,比如用新的漸變顏色在偽元素中褪色,或者用比元素漸變更寬的背景位置來偽造它。
但是現在我們可以這樣做了:
@property --gradient-start {
syntax: "<color>";
initial-value: white;
inherits: false;
}
.el {
--gradient-start: white;
background: linear-gradient(var(--gradient-start), black);
transition: --gradient-start 1s;
}
.el:hover {
--gradient-start: red;
}
因為我們告訴 CSS 這個自定義屬性是一個 <color>
,所以它可以被處理或者動畫化。之前的顏色值是無法被動畫化的。
這里有個有趣的示例:
實現思路很簡單,使用 CSS3 border-image 屬性設置邊框顏色為漸變色,然后使用 animation 改變漸變方向。代碼大概這樣子:
<div class="wrapper">
<div class="box">
<p>圓錐形漸變</p>
</div>
<div class="box">
<p>徑向漸變</p>
</div>
</div>
:root {
--angle: 90deg;
--gradX: 100%;
--gradY: 50%;
--d: 2500ms;
--c1: rgba(168, 239, 255, 1);
--c2: rgba(66, 106, 116, 0.1);
}
.box {
border-image: conic-gradient(from var(--angle), var(--c2), var(--c1) 0.1turn, var(--c1) 0.15turn, var(--c2) 0.25turn) 30;
animation: borderRotate var(--d) linear infinite forwards;
}
.box:nth-child(2) {
border-image: radial-gradient(ellipse at var(--gradX) var(--gradY), var(--c1), var(--c1) 10%, var(--c2) 40%) 30;
animation: borderRadial var(--d) linear infinite forwards;
}
@keyframes borderRotate {
100% {
--angle: 420deg;
}
}
@keyframes borderRadial {
20% {
--gradX: 100%;
--gradY: 50%;
}
40% {
--gradX: 100%;
--gradY: 100%;
}
60% {
--gradX: 50%;
--gradY: 100%;
}
80% {
--gradX: 0%;
--gradY: 50%;
}
100% {
--gradX: 50%;
--gradY: 0%;
}
}
此時,你會發現 border 上的漸變並不會因為設置了 animation 就動起來,這時候 @property 就排上用場了。
我們在 css 中先使用 @property 聲明這幾個需要變化的屬性。
@property --angle {
syntax: '<angle>';
initial-value: 90deg;
inherits: true;
}
@property --gradX {
syntax: '<percentage>';
initial-value: 50%;
inherits: true;
}
@property --gradY {
syntax: '<percentage>';
initial-value: 0%;
inherits: true;
}
此時 border 才真正的“動起來”了。
代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
box-sizing: border-box;
}
@property --angle {
syntax: '<angle>';
initial-value: 90deg;
inherits: true;
}
@property --gradX {
syntax: '<percentage>';
initial-value: 50%;
inherits: true;
}
@property --gradY {
syntax: '<percentage>';
initial-value: 0%;
inherits: true;
}
body {
font-family: Raleway, sans-serif;
text-align: center;
margin: 0;
padding: 1rem;
background-color: rgba(10, 12, 18, 1);
color: white;
min-height: 100vh;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
p {
margin: 0;
}
:root {
--angle: 90deg;
--gradX: 100%;
--gradY: 50%;
--d: 2500ms;
--c1: rgba(168, 239, 255, 1);
--c2: rgba(66, 106, 116, 0.1);
}
.wrapper {
min-width: min(40rem, 100%);
}
.box {
font-size: 3vw;
margin: max(1rem, 3vw);
border: 0.35rem solid;
padding: 3vw;
border-image: conic-gradient(from var(--angle), var(--c2), var(--c1) 0.1turn, var(--c1) 0.15turn, var(--c2) 0.25turn) 30;
animation: borderRotate var(--d) linear infinite forwards;
}
.box:nth-child(2) {
border-image: radial-gradient(ellipse at var(--gradX) var(--gradY), var(--c1), var(--c1) 10%, var(--c2) 40%) 30;
animation: borderRadial var(--d) linear infinite forwards;
}
@keyframes borderRotate {
100% {
--angle: 420deg;
}
}
@keyframes borderRadial {
20% {
--gradX: 100%;
--gradY: 50%;
}
40% {
--gradX: 100%;
--gradY: 100%;
}
60% {
--gradX: 50%;
--gradY: 100%;
}
80% {
--gradX: 0%;
--gradY: 50%;
}
100% {
--gradX: 50%;
--gradY: 0%;
}
}
</style>
</head>
<body>
<div class="wrapper">
<div class="box">
<p>圓錐形漸變</p>
</div>
<div class="box">
<p>徑向漸變</p>
</div>
</div>
</body>
</html>