您當前的位置:首頁 > 繪畫

探索@property以及它在動畫上的能力

作者:由 位元組前端 發表于 繪畫時間:2021-03-14

翻譯自:Exploring @property and its Animating Powers | CSS-Tricks

作者:Jhey Tompkins 譯者:大白

在瞭解

@property

之前,一些背景知識可能會有所幫助。

CSS 自定義屬性 (Custom Property)已經受到了廣泛的支援,並已經被使用在了許多前端元件庫中,如 Material-UI。

帶有字首——的屬性名,比如 ——example—name,表示的是帶有值的自定義屬性,其可以透過 var() 函式在全文件範圍內複用 — MDN

/* Modifed from MDN example*/

:root {

/* 定義在偽根節點所以全域性可用 */

——main-bg-color: #488cff;

}

#firstParagraph {

/* var(自定義屬性值, 回退值)*/

background-color: var(——main-bg-color, blue);

}

它能夠幫助我們更好的管理複雜樣式中的值,並且提升程式碼的可讀性(畢竟 ——main-bg-color 總是比 #488cff 更能清楚的描述值的意思)。但是在當前的使用中,我們沒辦法利用自定義屬性穩定地實現動畫效果。

Notably, they can even be transitioned or animated,since the UA has no way to interpret their contents, they always use the “flips at 50%” behavior that is used for any other pair of values that can’t be intelligently interpolated。 尤其是,它們(自定義屬性)甚至可以被過渡或動畫化,但因為使用者代理無法解釋這些內容,它們永遠採用為所有它們不能智慧補值的值所採用的“拋硬幣”行為。 — CSS Custom Properties for Cascading Variables Module Level 1

而 @property 則幫助我們達成了這一目標,它允許我們明確的定義這些 CSS 自定義屬性,例如為其提供型別,預設值,以及定義這些值是否應該繼承 @property。這篇文章詳細介紹了具體的實現方法。

@property ——property-name {

syntax: ‘’;

initial-value: #c0ffee;

inherits: false;

}

需要注意的是,@property 仍然處於 Working Draft 階段,生產上的使用也許需要等待更多瀏覽器的支援 CSS Properties and Values API Level 1

正文

額,什麼是

@property

? 它是一個新的 CSS 特性!它讓你具有了超能力。我不是在開玩笑,有一些

@property

可以做到的事解鎖了我們以前從未能在 CSS 中做到的事。

雖然所有有關於

@property

的事都令人興奮,但最有意思的莫過於它提供了一種為自定義 CSS 屬性明確型別的方式。型別為瀏覽器提供了更多的上下文資訊,而這成就了一些很酷的事:我們可以為瀏覽器提供它所需要來過渡以及動畫化這些屬性所需要的資訊。

但在我們對其過於飄飄然之前,值得提到的是瀏覽器對它的支援還不完善。在這篇文章寫作的時候,

@property

只在 Chrome 以及 Edge(透過擴充套件)中支援。我們需要持續觀察 瀏覽器支援 來了解我們什麼時候才能在其他地方,比如 Firefox 以及 Safari ,中使用它。

首先,我們有了型別檢查

@property ——spinAngle {

/* 自定義屬性的初始值 */

initial-value: 0deg;

/* 是否應該從父級元素繼承 */

inherits: false;

/* 型別。對,型別。覺得 TypeScript 酷嗎 */

syntax: ‘’;

}

@keyframes spin {

to {

——spinAngle: 360deg;

}

}

沒有錯!CSS 中的型別檢查。它有點像在建立我們自己的迷你 CSS 規範。這僅僅是一個很簡單的例子。看看我們可以使用的所有型別

length

number

percentage

length-percentage

color

image

url

integer

angle

time

resolution

transform-list

transform-function

custom-ident (一個自定義辨識字串)

在有上述這些型別之前,我們需要依賴一些 “技巧” 來支援使用自定義屬性的動畫。

。jump

——size 50

——height 1

——squished calc(1 + (2 * (var(——height) / 10)))

——extended calc(1 - (2 * (var(——height) / 10)))

height calc(var(——size) * 1px)

width calc(var(——size) * 1px)

background radial-gradient(circle at 10% 10%, hsl(25, 100%, 70%), hsl(25, 100%, 40%))

transform-origin bottom center

animation jump 1s infinite ease

@keyframes jump

0%, 100%

transform translate(0, 0) scale(var(——squished), var(——extended))

25%, 75%

transform translate(0, 0) scale(1, 1)

50%

/* 注意這裡 */

transform translate(0, calc(var(——height) * -100%)) scale(var(——extended), var(——squished))

#jump:checked ~ 。ground 。jump

——height 1

#higher:checked ~ 。ground 。jump

——height 2

#highest:checked ~ 。ground 。jump

——height 3

#jump:checked ~ [for=‘jump’],

#higher:checked ~ [for=‘higher’],

#highest:checked ~ [for=‘highest’]

background hsl(24,100%, 75)

label[for]:hover

background hsl(24, 100%, 85)

https://

codepen。io/jh3y/pen/zYY

WxGZ

那我們可以用它來做哪些有趣的事?讓我們看看 (例子)來激發我們的想象力吧。

讓我們動畫化顏色

你會如何動畫化一個元素來展示一系列顏色活在它們間變換?我是一個 HSL 色彩空間(Color Space)的忠實支持者,因為它將顏色拆分為了一些容易理解的數字:分別為色相(Hue),飽和度 (Saturation)以及亮度 (Lightness)。

動畫化色相感覺像是一些我們能做的有趣的的事。什麼是色彩豐富的?一道彩虹!有很多方法能畫出一道彩虹,以下就是一例。

探索@property以及它在動畫上的能力

。rainbow

- let b = 0

while b < 7

。rainbow__band(style=`——index: ${b};`)

—— b++

*

box-sizing border-box

body

min-height 100vh

display grid

place-items center

background hsl(210, 80%, 90%)

。rainbow

height 25vmin

width 50vmin

position relative

overflow hidden

&__band

——size calc((50 - (var(——index, 0) * 4)) * 1vmin)

height 50vmin

width 50vmin

border-radius 50%

position absolute

top 100%

left 50%

border-width 2vmin

border-style solid

// 注意這裡

border-color ‘hsl(%s, 80%, 50%)’ % var(——hue, 10)

transform translate(-50%, -50%)

height var(——size)

width var(——size)

&:nth-of-type(2)

——hue 35

&:nth-of-type(3)

——hue 55

&:nth-of-type(4)

——hue 110

&:nth-of-type(5)

——hue 200

&:nth-of-type(6)

——hue 230

&:nth-of-type(7)

——hue 280

在這個例子中,CSS 自定義屬性被設定到了彩虹的每一個帶(band)上,並透過使用 :

nth-child()

來將他們的作用域設定在每一個單獨的帶上。每一個帶同時有一個

--index

來幫助它們調整尺寸。

為了動畫化這些帶,我們可以使用

--index

來為他們設一些負的動畫延遲,然後使用同樣的 keyframe 動畫來迴圈變換它們的顏色。

。rainbow__band {

border-color: hsl(var(——hue, 10), 80%, 50%);

animation: rainbow 2s calc(var(——index, 0) * -0。2s) infinite linear;

}

@keyframes rainbow {

0%, 100% {

——hue: 10;

}

14% {

——hue: 35;

}

28% {

——hue: 55;

}

42% {

——hue: 110;

}

56% {

——hue: 200;

}

70% {

——hue: 230;

}

84% {

——hue: 280;

}

}

如果你想要 ”分步“ 的效果,這可能看起來還好。但這些 keyframe 的每一步並不是特別的準確。我每一步使用了 14% 作為一個大致的區間。

探索@property以及它在動畫上的能力

我們可以動畫化

border-color

,而這可以實現我們的效果。但我們仍然有計算 keyframe 步長的問題。並且我們需要寫很多 CSS 來完成這個效果。

@keyframes rainbow {

0%, 100% {

border-color: hsl(10, 80%, 50%);

}

14% {

border-color: hsl(35, 80%, 50%);

}

28% {

border-color: hsl(55, 80%, 50%);

}

42% {

border-color: hsl(110, 80%, 50%);

}

56% {

border-color: hsl(200, 80%, 50%);

}

70% {

border-color: hsl(230, 80%, 50%);

}

84% {

border-color: hsl(280, 80%, 50%);

}

}

@property

入場了。首先,為色相定義一個自定義屬性。這告訴瀏覽器,

--hue

,將會是一個數字 (而不是一個看起來像數字的字串):

@property ——hue {

initial-value: 0;

inherits: false;

/* 這裡我們明確了型別 */

syntax: ‘’;

}

HSL 中的色相值可以從 0 增加到 360。我們從 0 作為初始值開始。這個值將不會繼承。並且在這個案例中,我們的值是一個數字。那麼動畫就會被簡化成:

@keyframes rainbow {

to {

——hue: 360;

}

}

對的,正是這樣:

探索@property以及它在動畫上的能力

為了讓起點準確,我們可以調整每個帶的延遲。這給了我們一些很酷的靈活性。比如說,我們可以提高 animation-duration 來獲得一個慢的迴圈。用下面這個例子來嘗試一下不同的速度

探索@property以及它在動畫上的能力

&__band

——size calc((50 - (var(——index, 0) * 4)) * 1vmin)

height 50vmin

width 50vmin

border-radius 50%

position absolute

top 100%

left 50%

border-width 2vmin

border-color ‘hsl(%s, 80%, 50%)’ % var(——hue)

border-style solid

transform translate(-50%, -50%)

height var(——size)

width var(——size)

// 這裡

animation rainbow calc(var(——duration, 8) * 1s) calc(var(——index, 0) * -0。2s) infinite linear

這也許不是 “最天馬行空” 的例子,但我覺得當我們使用數字有邏輯性的顏色空間的時候,動畫化顏色有一些很有趣的使用場景。在以前動畫化色輪需要一些技巧。比如說透過預處理器,例如 Stylus,來生成 keyframe。

@keyframes party

for $frame in (0。。100)

{$frame * 1%}

background ‘hsl(%s, 65%, 40%)’ % ($frame * 3。6)

我們會這樣做僅僅是因為瀏覽器不能理解我們想做什麼。瀏覽器會把從色輪上從 0 變換到 360 的過程理解為一個瞬間的過渡,因為起點和終點的 HSL 值都表示一樣的顏色。

@keyframes party {

from {

background: hsl(0, 80%, 50%);

}

to {

background: hsl(360, 80%, 50%);

}

}

這些 keyframe 都一樣,所以瀏覽器假定這個動畫將會停留在同樣的 backgound 值上,雖然我們想讓瀏覽器渲染整個光譜的顏色,從一個值開始做一些動作後結束在同樣的值。

想一下我們所有其它的使用場景。我們可以:

動畫化飽和度

使用不同的緩動函式

動畫化亮度

嘗試 rgb()

嘗試 hsl()中的角度,並宣告我們的自定義屬性型別為

最棒的是我們可以在不同的元素中有作用域地共用這個動畫化的值。考慮一下這個按鈕。它的 border 和 shadow 會在滑鼠懸浮的時候流轉一整個色輪的顏色。

探索@property以及它在動畫上的能力

button Start Party!

@import url(‘https://fonts。googleapis。com/css2?family=Inter:wght@500&display=swap’)

@property ——hue

syntax ‘

inherits true

initial-value 0

:root

——bg hsl(0, 0%, 10%)

——button-bg hsl(0, 0%, 0%)

*

box-sizing border-box

body

background hsl(0, 0%, 10%)

display flex

align-items center

justify-content center

min-height 100vh

transform-style preserve-3d

perspective 800px

button

——border ‘hsl(%s, 0%, 50%)’ % var(——hue, 0)

——shadow ‘hsl(%s, 0%, 80%)’ % var(——hue, 0)

user-select none

font-family ‘Inter’, sans-serif

font-size 2rem

padding 1。25rem 2。5rem

border-radius 0。5rem

border 0。25rem solid

background var(——button-bg)

color hsl(0, 0%, 100%)

border-color var(——border)

box-shadow 0 1rem 2rem -1。5rem var(——shadow)

transition transform 0。2s, box-shadow 0。2s

cursor pointer

outline transparent

&:hover

——border ‘hsl(%s, 80%, 50%)’ % var(——hue, 0)

——shadow ‘hsl(%s, 80%, 50%)’ % var(——hue, 0)

animation hueJump 0。75s infinite linear

transform rotateY(10deg) rotateX(10deg)

&:active

transform rotateY(10deg) rotateX(10deg) translate3d(0, 0, -15px)

box-shadow 0 0rem 0rem 0rem var(——shadow)

animation-play-state paused

@keyframes hueJump

to

——hue 360

動畫化顏色讓我想到…… wow!

探索@property以及它在動畫上的能力

- const COUNT = 20

- let t = 0

while t < COUNT

h1(style=`——index: ${COUNT - t};`) Wow!

- t++

@import url(‘https://fonts。googleapis。com/css2?family=Montserrat:ital,wght@1,900&display=swap’)

@property ——hue

inherits false

initial-value 0

syntax ‘

:root

——bg hsl(45, 80%, 50%)

——stroke hsl(0, 0%, 10%)

*

box-sizing border-box

body

min-height 100vh

background var(——bg)

font-family ‘Montserrat’, sans-serif

min-height 100vh

overflow hidden

h1

——color ‘hsl(%s, 80%, 60%)’ % var(——hue)

text-transform uppercase

font-size 150px

letter-spacing 0。25vmin

position absolute

margin 0

top 50%

left 50%

line-height 0。8

color var(——color)

transform translate(-30%, -70%) translate(calc(var(——index) * (var(——x, -4) * 1%)), calc(var(——index) * (var(——y, 20) * 1%)))

-webkit-text-stroke 1。25vmin var(——stroke)

animation party 1s calc(var(——index) * -0。1s) infinite linear

@keyframes party

0%

——hue 0

100%

——hue 360

import gsap from ‘https://cdn。skypack。dev/gsap’

document。addEventListener(‘pointermove’, ({ x, y }) => {

gsap。set(‘h1’, {

‘——x’: gsap。utils。mapRange(0, window。innerWidth, -10, 10, x),

‘——y’: gsap。utils。mapRange(0, window。innerHeight, -10, 10, y)

})

})

純粹的計數

因為我們可以為數字定義型別—例如

integer

以及

number

—這意味著我們也可以動畫化這些數字,而不是將這些數字作為其它東西的一部分。Carter Li 就在 CSS-Tricks 上寫過一篇文章。秘訣就是使用

integer

以及 CSS

counter

。這類似於我們如何在 “純 CSS” 遊戲中使用 counters。

使用

counter

以及偽元素提供了一種將數字轉成字串的方法。然後我們可以使用這些字元作為偽元素的

content

。以下是一些重點的部分:

@property ——milliseconds {

inherits: false;

initial-value: 0;

syntax: ‘’;

}

。counter {

counter-reset: ms var(——milliseconds);

animation: count 1s steps(100) infinite;

}

。counter:after {

content: counter(ms);

}

@keyframes count {

to {

——milliseconds: 100;

}

}

這讓我們獲得這樣的效果,非常酷

探索@property以及它在動畫上的能力

。counter

@property ——milliseconds {

inherits: false;

initial-value: 0;

syntax: ‘’;

}

body {

min-height: 100vh;

display: grid;

place-items: center;

background: hsl(10, 10%, 10%);

}

。counter {

position: relative;

counter-reset: ms var(——milliseconds);

animation: count 1s steps(100) infinite;

}

。counter:after {

content: counter(ms);

position: absolute;

top: 0;

left: 0;

font-size: 5rem;

transform: translate(-50%, 0);

color: hsl(0, 0%, 100%);

font-weight: bold;

font-family: sans-serif;

}

@keyframes count {

to {

——milliseconds: 100;

}

}

增強這個億點點,然後你就有了了一個能執行的秒錶,僅僅使用 CSS 和 HTML。點選這些按鈕試試!這裡最好的就是這實際上能和計時器一樣工作。它不會受到 Time Drift 的影響 。在某種程度上甚至會比一般透過

setInterval

實現的 JavaScript 方案更精確。看看這個來自 Google Chrome Developer 關於 JavaScript 計數器的 影片 。

探索@property以及它在動畫上的能力

Pure CSS Working Stopwatch (@property)

還有什麼其他你可以動畫化數字的地方?也許一個倒計時器?

動畫化的漸變

你知道這些的, linear, radial, and conic 。 有想過要過渡或者動畫化顏色斷點(Color Stop)的時候嗎?好的,

@property

可以實現這些效果。

考慮一下一個模仿沙灘上的浪的漸變。當我們將一些圖片疊在一起的時候我們可以做出類似下面的東西:

body {

background-image:

linear-gradient(transparent 0 calc(35% + (var(——wave) * 0。5)), var(——wave-four) calc(75% + var(——wave)) 100%),

linear-gradient(transparent 0 calc(35% + (var(——wave) * 0。5)), var(——wave-three) calc(50% + var(——wave)) calc(75% + var(——wave))),

linear-gradient(transparent 0 calc(20% + (var(——wave) * 0。5)), var(——wave-two) calc(35% + var(——wave)) calc(50% + var(——wave))),

linear-gradient(transparent 0 calc(15% + (var(——wave) * 0。5)), var(——wave-one) calc(25% + var(——wave)) calc(35% + var(——wave))), var(——sand);

}

這裡程式碼比較多。但拆開來看,我們使用

calc()

來建立每個顏色斷點。並且在計算中,我們添加了 ——wave 的值。這裡巧妙的技巧就是當我們動畫化

--wave

的時候,所有的浪層都會移動。

探索@property以及它在動畫上的能力

*

box-sizing border-box

:root

——sand hsl(45, 40%, 50%)

——wave-one hsl(200, 50%, 100%)

——wave-two hsl(200, 50%, 90%)

——wave-three hsl(210, 50%, 60%)

——wave-four hsl(210, 80%, 25%)

@property ——wave

inherits false

initial-value 0%

syntax ‘

body

background linear-gradient(transparent 0 calc(35% + (var(——wave) * 0。5)), var(——wave-four) calc(75% + var(——wave)) 100%),

linear-gradient(transparent 0 calc(35% + (var(——wave) * 0。5)), var(——wave-three) calc(50% + var(——wave)) calc(75% + var(——wave))),

linear-gradient(transparent 0 calc(20% + (var(——wave) * 0。5)), var(——wave-two) calc(35% + var(——wave)) calc(50% + var(——wave))),

linear-gradient(transparent 0 calc(15% + (var(——wave) * 0。5)), var(——wave-one) calc(25% + var(——wave)) calc(35% + var(——wave))),

var(——sand)

min-height 100vh

以下是所有我們需要來實現海浪動畫的程式碼:

body {

animation: waves 5s infinite ease-in-out;

}

@keyframes waves {

50% {

——wave: 25%;

}

}

如果不使用

@property

的話,我們的海浪就會在高和低間一步一步的迴圈。但有了

@property

,我們就可以獲得一個非常好的效果。

探索@property以及它在動畫上的能力

想到其它我們操作影象所能實現的場景就令人興奮。比如說旋轉。或者說動畫化

conic-gradient

…。但,在一個

border-image

內。Bramus Van Damme 非常好的解釋了這一概念。

讓我們透過實現一個充電指示器的方式一步步解釋。我們會同時動畫化角度以及色相。我們首先從定義這兩個自定義屬性開始:、

@property ——angle {

initial-value: 0deg;

inherits: false;

syntax: ‘’;

}

@property ——hue {

initial-value: 0;

inherits: false;

syntax: ‘’;

}

動畫會在每次迭代的時候會先暫停一下,然後更新角度以及色相。

@keyframes load {

0%, 10% {

——angle: 0deg;

——hue: 0;

}

100% {

——angle: 360deg;

——hue: 100;

}

}

現在讓我們將它作為 border-image 應用在元素上

。loader {

——charge: hsl(var(——hue), 80%, 50%);

border-image: conic-gradient(var(——charge) var(——angle), transparent calc(var(——angle) * 0。5deg)) 30;

animation: load 2s infinite ease-in-out;

}

非常酷

探索@property以及它在動畫上的能力

。loader Charging。。。

*

box-sizing border-box

:root

——bg hsl(0, 10%, 10%)

body

min-height 100vh

background var(——bg)

display grid

place-items center

@property ——a

initial-value 0deg

inherits false

syntax ‘

@property ——h

initial-value 0

inherits false

syntax ‘

。loader

padding 2rem 4rem

font-family monospace

font-weight bold

color hsl(0, 0%, 100%)

border-style solid

border-width 1vmin

font-size 2rem

——charge ‘hsl(%s, 80%, 50%)’ % var(——h, 0)

border-image conic-gradient(var(——charge) var(——a), transparent calc(var(——a) + 0。5deg)) 30

animation load 2s infinite ease-in-out

@keyframes load

0%, 10%

——a 0deg

——h 0

100%

——a 360deg

——h 100

不幸的是,

border-image

border-radius

並不能很好的一起使用。但是,我們可以在元素後加一個偽元素。結合之前提到的動畫化數字技巧,我們得到了一個完成的充電/載入動畫。(是的,它會在 100% 電量的時候充電)

探索@property以及它在動畫上的能力

變換(Transform)也很酷

動畫化變換的一個問題就是在不同的部分間過渡。它常常最後直接不成功或者看起來不像它該有的樣子。個經典的例子就是一個被扔出去的球。我們想讓它從 A 點移動到 B 點並同時模仿重力的效果。

一個早期的嘗試可能看起來像這樣:

@keyframes throw {

0% {

transform: translate(-500%, 0);

}

50% {

transform: translate(0, -250%);

}

100% {

transform: translate(500%, 0);

}

}

但,我們馬上就會發現它看起來並不是我們想要的。

探索@property以及它在動畫上的能力

*

box-sizing border-box

body

min-height 100vh

display grid

place-items center

background hsl(190, 80%, 90%)

。ball

height 10vmin

width 10vmin

border-radius 50%

fill hsl(35, 80%, 50%)

background hsl(35, 80%, 35%)

animation throw 1s infinite alternate

@keyframes throw

0%

transform translate(-500%, 0)

50%

transform translate(0, -250%)

100%

transform translate(500%, 0)

以前,我們可能會使用包裹元素以及分別動畫化這些元素來實現這一效果。但是,在有了 @property 之後, 我們可以同時動畫化變換中每個獨立的值。讓我們快速看看這是如何透過定義自定義屬性然後在球上設定變換實現的。

@property ——x {

inherits: false;

initial-value: 0%;

syntax: ‘’;

}

@property ——y {

inherits: false;

initial-value: 0%;

syntax: ‘’;

}

@property ——rotate {

inherits: false;

initial-value: 0deg;

syntax: ‘’;

}

。ball {

animation: throw 1s infinite alternate ease-in-out;

transform: translateX(var(——x)) translateY(var(——y)) rotate(var(——rotate));

}

現在對於我們的動畫來說,我們可以在 keyframe 組合我們想要的變換。

@keyframes throw {

0% {

——x: -500%;

——rotate: 0deg;

}

50% {

——y: -250%;

}

100% {

——x: 500%;

——rotate: 360deg;

}

}

那麼結果是?結果是我們獲得了想要的曲線路徑。並且我們可以根據使用的不同時序函式讓它看起來不同。我們可以將這個動畫拆成三個方向並且使用不同的時序函式。而這將會讓球移動的方式有不同的結果。

探索@property以及它在動畫上的能力

讓我們考慮另外一個例子,我們想駕駛一輛車環繞圓角四邊形一週。

探索@property以及它在動畫上的能力

。road

img。car(alt="探索@property以及它在動畫上的能力" data-isLoading="0" src="/static/img/blank.gif" data-src=“https://assets。codepen。io/605876/little-red-car。png” alt=“Little red car”)

*

box-sizing border-box

:root

——road hsl(220, 8%, 50%)

——grass hsl(90, 40%, 50%)

——island hsl(45, 40%, 50%)

——lines hsl(45, 80%, 90%)

@property ——x

inherits false

initial-value -22。5

syntax ‘

@property ——y

inherits false

initial-value 0

syntax ‘

@property ——r

inherits false

initial-value 0deg

syntax ‘

body

min-height 100vh

display grid

place-items center

background var(——grass)

。car

animation journey 5s infinite linear

transform translate(calc(var(——x) * 1vmin), calc(var(——y) * 1vmin)) rotate(var(——r))

width 3vmin

object-fit cover

。road

height 50vmin

width 50vmin

border-radius 12。5%

border 5vmin solid var(——road)

background var(——road)

position absolute

top 50%

left 50%

transform translate(-50%, -50%)

&:before

content ‘’

position absolute

height 44vmin

width 44vmin

border-radius 11%

border 0。5vmin dashed var(——lines)

top 50%

left 50%

transform translate(-50%, -50%)

&:after

content ‘’

position absolute

height 40vmin

width 40vmin

background var(——island)

top 50%

left 50%

transform translate(-50%, -50%)

border-radius 10%

我們可以使用類似之前在球上使用的方法

@property ——x {

inherits: false;

initial-value: -22。5;

syntax: ‘’;

}

@property ——y {

inherits: false;

initial-value: 0;

syntax: ‘’;

}

@property ——r {

inherits: false;

initial-value: 0deg;

syntax: ‘’;

}

小車的變換使用了

vmin

來計算以保持響應性。

。car {

transform: translate(calc(var(——x) * 1vmin), calc(var(——y) * 1vmin)) rotate(var(——r));

}

現在我們可以寫出這輛車精準的幀到幀行程了。我們可以從 ——x 的值開始

@keyframes journey {

0%, 100% {

——x: -22。5;

}

25% {

——x: 0;

}

50% {

——x: 22。5;

}

75% {

——x: 0;

}

}

我們可以看到車在 X 軸上的行程正確了。

探索@property以及它在動畫上的能力

然後我們在此之上新增 Y 軸的行程

@keyframes journey {

0%, 100% {

——x: -22。5;

——y: 0;

}

25% {

——x: 0;

——y: -22。5;

}

50% {

——x: 22。5;

——y: 0;

}

75% {

——x: 0;

——y: 22。5;

}

}

但結果卻不太對。

探索@property以及它在動畫上的能力

讓我們新增一些額外的步驟到我們的

@keyframes

中來修復這一問題。

@keyframes journey {

0%, 100% {

——x: -22。5;

——y: 0;

}

12。5% {

——x: -22。5;

——y: -22。5;

}

25% {

——x: 0;

——y: -22。5;

}

37。5% {

——y: -22。5;

——x: 22。5;

}

50% {

——x: 22。5;

——y: 0;

}

62。5% {

——x: 22。5;

——y: 22。5;

}

75% {

——x: 0;

——y: 22。5;

}

87。5% {

——x: -22。5;

——y: 22。5;

}

}

嗯,現在看起來好多了

探索@property以及它在動畫上的能力

剩下的就是讓這輛車旋轉了。我們選了 5% 在角落的時間去旋轉。這並不準確,但它展示了這種方法可以做到的潛力。

@keyframes journey {

0% {

——x: -22。5;

——y: 0;

——r: 0deg;

}

10% {

——r: 0deg;

}

12。5% {

——x: -22。5;

——y: -22。5;

}

15% {

——r: 90deg;

}

25% {

——x: 0;

——y: -22。5;

}

35% {

——r: 90deg;

}

37。5% {

——y: -22。5;

——x: 22。5;

}

40% {

——r: 180deg;

}

50% {

——x: 22。5;

——y: 0;

}

60% {

——r: 180deg;

}

62。5% {

——x: 22。5;

——y: 22。5;

}

65% {

——r: 270deg;

}

75% {

——x: 0;

——y: 22。5;

}

85% {

——r: 270deg;

}

87。5% {

——x: -22。5;

——y: 22。5;

}

90% {

——r: 360deg;

}

100% {

——x: -22。5;

——y: 0;

——r: 360deg;

}

}

然後我們有了想要的結果,一輛環繞圓角四邊形的車。不用額外的包裹元素,也不需要複雜的數學。並且我們透過自定義屬性實現了這一切。

探索@property以及它在動畫上的能力

透過變數驅動整個場景

我們到現在已經看了一些非常好的

@property

的使用場景,但將我們看到過的所有技巧組合在一起則可以將效果提升到另一個級別。比如來說,我們可以透過僅使用幾個自定義屬性驅動一整個場景。

考慮一下以下這個 404 頁面的概念。兩個註冊的自定義屬性驅動不同的移動部分。我們有一個透過

webkit-backgroun-clip

裁切的移動的漸變(模仿文字上的高光)。陰影透過讀取屬性的值移動(模仿文字形成的影子)。然後我們為了光的效果擺動另外一個元素(模仿擺動的光源)。

探索@property以及它在動畫上的能力

h1 404

。cloak__wrapper

。cloak__container

。cloak

。info

h2 We can‘t find that page

p。

We’re fairly sure that page used to be here, but seems to have gone missing。 We do apologise on it‘s behalf。

a(href=“https://jhey。dev” target=“_blank” rel=“noreferrer noopener”) Home

@import url(’https://fonts。googleapis。com/css2?family=Open+Sans:wght@800&family=Roboto:wght@100;300&display=swap‘)

:root

——button hsl(44, 0%, 70%)

——button-color hsl(0, 0%, 4%)

——shadow hsl(0, 0%, 0%)

——bg hsl(53, 0%, 45%)

——header hsl(53, 0%, 48%)

——color hsl(0, 0%, 98%)

——lit-header hsl(53, 0%, 90%)

——speed 2s

*

box-sizing border-box

transform-style preserve-3d

// 這裡定義了兩個自定義屬性

@property ——swing-x

initial-value 0

inherits false

syntax ’

@property ——swing-y

initial-value 0

inherits false

syntax ’

body

min-height 100vh

display flex

font-family ’Roboto‘, sans-serif

flex-direction column

align-items center

justify-content center

background var(——bg)

color var(——color)

perspective 1200px

a

text-transform uppercase

text-decoration none

background var(——button)

color var(——button-color)

padding 1rem 4rem

border-radius 4rem

font-size 0。875rem

letter-spacing 0。05rem

p

font-weight 100

h1

animation swing var(——speed) infinite alternate ease-in-out

font-size clamp(5rem, 40vmin, 20rem)

font-family ’Open Sans‘, sans-serif

margin 0

margin-bottom 1rem

letter-spacing 1rem

// 透過背景錐形漸變模仿燈光打在文字上的高光

transform translate3d(0, 0, 0vmin)

——x calc(50% + (var(——swing-x) * 0。5) * 1%)

background radial-gradient(var(——lit-header), var(——header) 45%) var(——x) 100% / 200% 200%

-webkit-background-clip text

color transparent

&:after

animation swing var(——speed) infinite alternate ease-in-out

content “404”

position absolute

top 0

left 0

color var(——shadow)

filter blur(1。5vmin)

// 模仿文字的陰影

transform scale(1。05) translate3d(0, 12%, -10vmin) translate(calc((var(——swing-x, 0) * 0。05) * 1%), calc((var(——swing-y) * 0。05) * 1%))

。cloak

// 前側的黑色遮罩

animation swing var(——speed) infinite alternate-reverse ease-in-out

height 100%

width 100%

transform-origin 50% 30%

transform rotate(calc(var(——swing-x) * -0。25deg))

background radial-gradient(40% 40% at 50% 42%, transparent, black 35%)

&__wrapper

position fixed

top 0

left 0

bottom 0

right 0

overflow hidden

&__container

height 250vmax

width 250vmax

position absolute

top 50%

left 50%

transform translate(-50%, -50%)

。info

text-align center

line-height 1。5

max-width clamp(16rem, 90vmin, 25rem)

& > p

margin-bottom 3rem

@keyframes swing

0%

——swing-x -100

——swing-y -100

50%

——swing-y 0

100%

——swing-y -100

——swing-x 100

到此結束!

想到我們能用

@property

定義型別的能力所能做到的事真令人激動。透過提供瀏覽器一些關於自定義屬性的額外上下文,我們可以達成一些以前使用基本字串所不能實現的奇思妙想。

你對其它的型別有什麼想法嗎?時間和解析度感覺能實現很有意思的過渡,雖然我承認我沒能讓他們像我想的那樣工作。

url

感覺也不錯,比如說像走馬燈(carousel) 一樣在一系列來源 source 中變換。僅僅是在這頭腦風暴(就給了我這些點子)。

我希望這篇對

@property

的快速展望能夠激發你學習然後製作你自己的超讚例子的興趣!我非常期待看到你的創意。

原文連結:

https://

css-tricks。com/explorin

g-property-and-its-animating-powers/

歡迎關注「 位元組前端 ByteFE 」簡歷投遞聯絡郵箱「 tech@bytedance。com 」

標簽: var  50%  HSL  Calc  property