CSS 動畫進度控制 animation-timeline、view-timeline、animation-range
雖然實作 CSS 動畫通常都需要手動設定「進度百分比」,但目前已有部分瀏覽器支援「視窗可視位置」以及「瀏覽器卷軸」控制動畫進度的功能,這篇教學會介紹運用 CSS 的 animation-timeline、view-timeline 和 animation-range 樣式屬性,實現進階的動畫進度控制功能。
- 注意,目前部分瀏覽器不支援這些樣式屬性 ( Firefox、Safari、IE )!
- 建議先閱讀:CSS 動畫 animation
快速導覽:
animation-timeline 動畫進度控制 ( 動畫時間軸 )
animation-timeline
是一個可以「根據畫面或卷軸控制動畫進度」的樣式屬性,會將元素在「可視範圍」裡的位置,或元素所在階層的「視窗捲軸」,當作「時間軸 timeline」,時間軸對應到動畫所設定的進度百分比,就夠單純透過 CSS 讀取這些元素之外數值,在不需要 JavaScript 的輔助下,控制動畫進度,這個樣式沒有繼承特性,具有下列屬性值:
注意,
animation-timeline
在 CSS 中的順序必須擺在animation-name
之後。
屬性值 | 說明 |
---|---|
none | 預動畫進度與時間軸無關 ( 預設值 )。 |
auto | 動畫進度自動套用文件時間軸 ( Document.timeline )。 |
view() | 動畫進度使用元素在「可視範圍」中的位置作為時間軸。 |
scroll() | 動畫進度使用畫面卷軸作為時間軸。 |
animation-timeline: view() 畫面高度
animation-timeline
屬性值裡的 view()
屬於 CSS 函式,支援下列參數數值:
參數 | 說明 |
---|---|
block | 邏輯參數,表示垂直方向 ( 預設值,等同 0% 0% )。 |
inline | 邏輯參數,表示水平方向。 |
y | 垂直方向 |
x | 水平方向 |
start | 垂直方向上方 ( 等同 start 0% ) |
start end | 垂直方向上方、垂直方向下方 |
block start | 垂直方向上方、垂直方向下方 ( 等同 start 0% ) |
block start end | 垂直方向上方、垂直方向下方 |
inline start | 左向右書寫模式下,水平方向左側、水平方向右側 ( 等同 inline start 0% ) |
inline start end | 左向右書寫模式下,水平方向左側、水平方向右側 |
下方的範例會展示使用 view()
屬性值,搭配各種不同參數所呈現的效果,當使用者捲動捲軸時,會看見內容方塊出現變色、尺寸改變的效果,可以注意設定了 start
和 end
的元素,會在指定的位置才發生改變,位置外的樣貌會呈現原本元素的樣式屬性。
<!-- HTML 程式碼 -->
垂直方向改變動畫進度
<div class="out a">
<div>往下拉,觀察變化狀況</div><div class="in"></div><div></div>
</div>
垂直方向改變動畫進度 ( 限制上 50px 下 100px )
<div class="out b">
<div>往下拉,觀察變化狀況</div><div class="in"></div><div></div>
</div>
水平方向改變動畫進度
<div class="out c">
<div>往右拉,觀察變化狀況</div><div class="in"></div><div></div>
</div>
水平方向改變動畫進度 ( 限制左 50px 右 100px )
<div class="out d">
<div>往右拉,觀察變化狀況</div><div class="in"></div><div></div>
</div>
<!-- CSS 程式碼 -->
<style>
.out {
width: 300px;
height: 200px;
overflow: scroll; /* 隱藏超出範圍的子元素並出現捲軸 */
margin: 5px 0 20px 0 ;
border: 1px solid #000;
}
/* 使用一般的 div 撐高畫面,並用漸層背景讓效果更明顯 */
div:not([class]) {
width: 400px;
height: 300px;
background: repeating-linear-gradient(45deg,#fff 0%, #fff 5%,#eee 5%, #eee 10%);
}
/* 水平捲動的一般 div 高度較小,讓變化的 div 一開始就出現 */
.c div:not([class]), .d div:not([class]) {height: 100px;}
/* 動畫進度會改變的 div,注意只使用了動畫名稱而已 */
.in {
width: 100px;
height: 100px;
background: red;
animation-name: oxxo; /* 只使用動畫名稱 */
}
/* 動畫設定 */
@keyframes oxxo {
0% {background: red;}
25% {background: yellow;}
50% {background: green;}
75% {background: blue;}
100% {
background: black;
width: 20px;
height: 20px;
}
}
.a .in {animation-timeline: view();} /* 元素在顯示畫面中垂直方向的位置,等同 view(0% 0%) */
.b .in {animation-timeline: view(50px 100px);} /* 元素在顯示畫面中垂直方向的位置 */
.c .in {
margin: 0 300px; /* 讓畫面出現水平捲軸 */
animation-timeline: view(inline); /* 元素在顯示畫面中水平方向的位置,等同 view(inline 0% 0%) */
}
.d .in {
margin: 0 300px; /* 讓畫面出現水平捲軸 */
animation-timeline: view(inline 50px 100px ); /* 元素在顯示畫面中水平方向的位置 */
}
</style>
animation-timeline: scroll() 捲軸位置
animation-timeline
屬性值裡的 scroll()
屬於 CSS 函式,支援下列參數數值:
注意,
animation-timeline
在 CSS 中的順序必須擺在animation-name
之後。
參數 | 說明 |
---|---|
block | 邏輯參數,表示「最靠近元素且具有捲軸的父層元素」的垂直方向捲軸 ( 預設值,等同 block nearest )。 |
inline | 邏輯參數,表示「最靠近元素且具有捲軸的父層元素」的水平方向捲軸 ( 等同 inline nearest )。 |
nearest | 搭配參數,表示「最靠近元素且具有捲軸的父層元素」的捲軸。 |
root | 搭配參數,「頁面本身的捲軸。 |
self | 搭配參數,元素本身的捲軸。 |
下方的範例會展示使用 scroll()
屬性值,搭配各種不同參數所呈現的效果,第一組元素會在捲動最靠近元素的父層元素捲軸時發生變化,第二組則是會在捲動頁面捲軸時發生變化,第三組會在捲動元素本身捲軸時發生變化。
<!-- HTML 程式碼 -->
最靠近元素的父層元素捲軸
<div class="out a">
<div>往下拉,觀察變化狀況</div>
<div class="in">apple<br>banana<br>oxxo<br>coconut<br>papaya</div>
<div></div>
</div>
網頁文件捲軸
<div class="out b">
<div>往下拉,觀察變化狀況</div>
<div class="in">apple<br>banana<br>oxxo<br>coconut<br>papaya</div>
<div></div>
</div>
元素本身捲軸
<div class="out c">
<div>往右拉,觀察變化狀況</div>
<div class="in">apple<br>banana<br>oxxo<br>coconut<br>papaya</div>
<div></div>
</div>
<br><br><br><br><br><br><br><br><br><br><br><br>
<!-- CSS 程式碼 -->
<style>
.out {
width: 300px;
height: 150px;
overflow: scroll; /* 隱藏超出範圍的子元素並出現捲軸 */
margin: 5px 0 20px 0 ;
border: 1px solid #000;
}
/* 使用一般的 div 撐高畫面,並用漸層背景讓效果更明顯 */
div:not([class]) {
background: repeating-linear-gradient(45deg,#fff 0%, #fff 5%,#eee 5%, #eee 10%);
width: 400px;
height: 50px;
}
/* 撐高產生捲軸 */
div:nth-child(3){height: 150px;}
/* 動畫進度會改變的 div,注意只使用了動畫名稱而已 */
.in {
width: 100px;
height: 100px;
animation-name: oxxo; /* 動畫名稱 */
overflow: scroll; /* 元素本身產生捲軸 */
}
/* 動畫設定 */
@keyframes oxxo {
0% {background: red;}
25% {background: yellow;}
50% {background: green;}
75% {background: blue;}
100% {
background: black;
width: 20px;
height: 20px;
}
}
.a .in {animation-timeline: scroll();} /* 捲動最靠近元素的父層元素捲軸 */
.b .in {animation-timeline: scroll(root);} /* 捲動頁面捲軸 */
.c .in {animation-timeline: scroll(self);} /* 捲動元素本身捲軸 */
</style>
view-timeline 可視範圍時間軸
view-timeline
是針對 animation-timeline
屬性值為 view()
的延伸樣式屬性,可以設定一組類似 CSS 變數的名稱與數值,接著只要呼叫這個名稱,就能執行和 view()
完全相同的效果,這個樣式是 view-timeline-name
、view-timeline-axis
和 view-inset
的縮寫格式,這些樣式屬性都沒有繼承特性,通常只使用 view-timeline
或單純透過 view()
來實現效果。
下方範例會將 animation-timeline: view()
的寫法換成 view-timeline
,會呈現完全相同的效果,當元素離底部 100px 時就會開始變色變小,值到離頂部 50px 為止。
<!-- HTML 程式碼 -->
animation-timeline: view(50px 100px);
<div class="out a">
<div>往下拉,觀察變化狀況</div><div class="in"></div><div></div>
</div>
view-timeline: --a 50px 100px;<br>
animation-timeline: --a;
<div class="out b">
<div>往下拉,觀察變化狀況</div><div class="in"></div><div></div>
</div>
<!-- CSS 程式碼 -->
<style>
.out {
width: 300px;
height: 200px;
overflow: scroll; /* 隱藏超出範圍的子元素並出現捲軸 */
margin: 5px 0 20px 0 ;
border: 1px solid #000;
}
/* 使用一般的 div 撐高畫面,並用漸層背景讓效果更明顯 */
div:not([class]) {
width: 400px;
height: 300px;
background: repeating-linear-gradient(45deg,#fff 0%, #fff 5%,#eee 5%, #eee 10%);
}
/* 動畫進度會改變的 div,注意只使用了動畫名稱而已 */
.in {
width: 100px;
height: 100px;
background: red;
animation-name: oxxo; /* 只使用動畫名稱 */
}
/* 動畫設定 */
@keyframes oxxo {
0% {background: red;}
100% {
background: black;
width: 20px;
height: 20px;
}
}
.a .in {
view-timeline: --a 50px 100px; /* 建立名稱和數值 */
animation-timeline: --a; /* 讀取名稱 */
}
.b .in {animation-timeline: view(50px 100px);}
</style>
animation-range 動畫執行範圍
animation-range
表示「動畫執行範圍」的樣式屬性,可以針對 view()
或 scroll()
進行數值範圍設定,這個樣式是 animation-range-start
和 animation-range-end
的縮寫格式,這些樣式屬性都沒有繼承特性,通常只使用 animation-range
來實現效果。
特別注意
animation-range
使用的數值和view()
與scroll()
不同,animation-range
的數值表示「元素頂部距離出現位置的距離」。
下方會使用 animation-range
代替 view()
的設定,做出更容易理解位置的效果,和下方的另外一組對照組,呈現完全相同的效果。
<!-- HTML 程式碼 -->
animation-timeline: view();<br>
animation-range: 50px 150px;
<div class="out a">
<div>往下拉,觀察變化狀況</div><div class="in"></div><div></div>
</div>
animation-timeline: view(50px 50px);
<div class="out b">
<div>往下拉,觀察變化狀況</div><div class="in"></div><div></div>
</div>
<!-- CSS 程式碼 -->
<style>
.out {
width: 300px;
height: 200px;
overflow: scroll; /* 隱藏超出範圍的子元素並出現捲軸 */
margin: 5px 0 20px 0 ;
border: 1px solid #000;
}
/* 使用一般的 div 撐高畫面,並用漸層背景讓效果更明顯 */
div:not([class]) {
width: 400px;
height: 300px;
background: repeating-linear-gradient(45deg,#fff 0%, #fff 5%,#eee 5%, #eee 10%);
}
/* 動畫進度會改變的 div,注意只使用了動畫名稱而已 */
.in {
width: 100px;
height: 100px;
background: red;
animation-name: oxxo; /* 只使用動畫名稱 */
}
/* 動畫設定 */
@keyframes oxxo {
0% {background: red;}
100% {
background: black;
width: 20px;
height: 20px;
}
}
.a .in {
animation-timeline: view(); /* 使用可視範圍控制動畫進度 */
animation-range: 50px 150px; /* 元素頂部為 50px 開始動畫,值到距離 150px 為止 */
}
.b .in {
animation-timeline: view(50px 50px); /* 出現時 50px,結束值為 300px-250px */
}
</style>
對於 view()
而言,animation-range
也可以將上述的數值,搭配下方的屬性值,實現更進階的設定。
屬性值 | 說明 |
---|---|
normal | 預設值,按照上述數值控制元素動畫進度。 |
contain | 元素完全被包含在可視範圍中。 |
cover | 元素在可視範圍進入到完全退出的過程。 |
exit | 元素開始退出可視範圍。 |
exit-crossing | 元素開始穿越可視範圍結束邊緣 ( 類似 exit )。 |
entry | 元素開始進入可視範圍。 |
entry-crossing | 元素開始穿越可視範圍起始邊緣 ( 類似 entry )。 |
下方會使用六個 div
,從左到右分別是使用 contain
、cover
、exit
、exit-crossing
、entry
和 entry-crossing
所呈現的效果,拖拉捲軸後,可以觀察元素進入和離開可視範圍時動畫進度的表現。
<!-- HTML 程式碼 -->
<div class="out">
<div></div>
<div class="in a"></div>
<div class="in b"></div>
<div class="in c"></div>
<div class="in d"></div>
<div class="in e"></div>
<div class="in f"></div>
<div></div>
</div>
<!-- CSS 程式碼 -->
<style>
.out {
width: 350px;
height:250px;
overflow: scroll; /* 隱藏超出範圍的子元素並出現捲軸 */
margin: 5px 0 20px 0 ;
padding: 20px;
border: 1px solid #000;
background: repeating-linear-gradient(45deg,#fff 0, #fff 25px,#eee 25px, #eee 50px);
background-attachment: local;
}
/* 使用一般的 div 撐高畫面,並用漸層背景讓效果更明顯 */
div:not([class]) {
width: 400px;
height: 300px;
clear: both;
}
/* 動畫進度會改變的 div,注意只使用了動畫名稱而已 */
.in {
width: 40px;
height: 100px;
margin: 5px;
float: left;
background: red;
animation-name: oxxo; /* 動畫名稱 */
animation-fill-mode: forwards; /* 動畫結束後停留在最後一格 */
animation-timeline: view(); /* 使用可視範圍控制動畫進度 */
}
/* 動畫設定 */
@keyframes oxxo {
0% {background: red;}
100% {
background: black;
height: 200px;
}
}
.a {animation-range: contain;}
.b {animation-range: cover;}
.c {animation-range: exit;}
.d {animation-range: exit-crossing;}
.e {animation-range: entry;}
.f {animation-range: entry-crossing;}
</style>
因為 animation-scroll
無法讀取捲軸移動距離,而透過 animation-range
就能讀取捲軸捲動的數值,透過下拉捲軸的距離控制動畫進度,下方範例會將三個 div
套用不同的 animation-range
,執行後將捲軸向下拉動,可以發現三個 div
會在不同高度捲軸時開始執行動畫進度,產生有趣的視覺效果。
<!-- HTML 程式碼 -->
<div class="out">
<div class="in a"></div>
<div class="in b"></div>
<div class="in c"></div>
<div></div>
</div>
<!-- CSS 程式碼 -->
<style>
.out {
width: 350px;
height:250px;
overflow: scroll; /* 隱藏超出範圍的子元素並出現捲軸 */
padding: 20px;
border: 1px solid #000;
background: repeating-linear-gradient(45deg,#fff 0, #fff 25px,#eee 25px, #eee 50px); /* 用漸層背景讓效果更明顯 */
background-attachment: local; /* 固定背景 */
}
div:not([class]) {
width: 400px;
height: 1000px;
clear: both;
}
.in {
width: 100px;
height: 100px;
margin: 5px;
float: left;
background: red;
animation-name: oxxo; /* 動畫名稱 */
animation-fill-mode: forwards; /* 動畫結束後停留在最後一格 */
animation-timeline: scroll(); /* 根據捲軸播放動畫進度 */
}
/* 動畫設定 */
@keyframes oxxo {
0% {background: red;}
100% {
background: black;
height: 300px;
}
}
.a {animation-range: 10px 100px;} /* 動畫範圍是捲軸移動的 10px~100px */
.b {animation-range: 50px 150px;} /* 動畫範圍是捲軸移動的 50px~150px */
.c {animation-range: 100px 200px;} /* 動畫範圍是捲軸移動的 100px~200px */
</style>
小結
一但可以透過元素在「可視範圍的位置」或「捲軸位置」控制動畫進度,就更能大幅發揮 CSS 強大的動畫能力,甚至不需要 JavaScript 就能做出許多有趣的捲軸效果,可惜目前還有部分瀏覽器不支援,不過相信這種好用的功能,不久的未來應該會都支援囉。
延伸閱讀:
意見回饋
如果有任何建議或問題,可傳送「意見表單」給我,謝謝~