探索@property以及它在動畫上的能力
翻譯自: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)。
動畫化色相感覺像是一些我們能做的有趣的的事。什麼是色彩豐富的?一道彩虹!有很多方法能畫出一道彩虹,以下就是一例。
。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% 作為一個大致的區間。
我們可以動畫化
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;
}
}
對的,正是這樣:
為了讓起點準確,我們可以調整每個帶的延遲。這給了我們一些很酷的靈活性。比如說,我們可以提高 animation-duration 來獲得一個慢的迴圈。用下面這個例子來嘗試一下不同的速度
&__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 會在滑鼠懸浮的時候流轉一整個色輪的顏色。
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!
- 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;
}
}
這讓我們獲得這樣的效果,非常酷
。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 計數器的 影片 。
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
的時候,所有的浪層都會移動。
*
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
,我們就可以獲得一個非常好的效果。
想到其它我們操作影象所能實現的場景就令人興奮。比如說旋轉。或者說動畫化
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;
}
非常酷
。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% 電量的時候充電)
變換(Transform)也很酷
動畫化變換的一個問題就是在不同的部分間過渡。它常常最後直接不成功或者看起來不像它該有的樣子。個經典的例子就是一個被扔出去的球。我們想讓它從 A 點移動到 B 點並同時模仿重力的效果。
一個早期的嘗試可能看起來像這樣:
@keyframes throw {
0% {
transform: translate(-500%, 0);
}
50% {
transform: translate(0, -250%);
}
100% {
transform: translate(500%, 0);
}
}
但,我們馬上就會發現它看起來並不是我們想要的。
*
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;
}
}
那麼結果是?結果是我們獲得了想要的曲線路徑。並且我們可以根據使用的不同時序函式讓它看起來不同。我們可以將這個動畫拆成三個方向並且使用不同的時序函式。而這將會讓球移動的方式有不同的結果。
讓我們考慮另外一個例子,我們想駕駛一輛車環繞圓角四邊形一週。
。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 軸上的行程正確了。
然後我們在此之上新增 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;
}
}
但結果卻不太對。
讓我們新增一些額外的步驟到我們的
@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;
}
}
嗯,現在看起來好多了
剩下的就是讓這輛車旋轉了。我們選了 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
的使用場景,但將我們看到過的所有技巧組合在一起則可以將效果提升到另一個級別。比如來說,我們可以透過僅使用幾個自定義屬性驅動一整個場景。
考慮一下以下這個 404 頁面的概念。兩個註冊的自定義屬性驅動不同的移動部分。我們有一個透過
webkit-backgroun-clip
裁切的移動的漸變(模仿文字上的高光)。陰影透過讀取屬性的值移動(模仿文字形成的影子)。然後我們為了光的效果擺動另外一個元素(模仿擺動的光源)。
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 」