一、不堪回首的過往
在應用 CSS3 漸變/動畫時,有個控制時間的屬性
steps() 第一個參數 number 為指定的間隔數,即把動畫分為 n 步階段性展示,第二個參數默認為 end,設置最后一步的狀態,start 為結束時的狀態,end 為開始時的狀態。
又如:
steps 有兩個參數
第一個肯定是分幾步執行完
第二個有兩個值
start 第一幀是第一步動畫結束
end 第一幀是第一步動畫開始
年少無知的我輕易就相信了大家的說法,每次應用 steps() 函數時都要先考慮一番:嗯,start 對應末態,end 對應初態,末態是 OOOO,初態是 XXXX……臥槽!跑起來不對!
二、一探究竟
被坑得團團轉之后,只好向組織求助。于是查到了這樣的 規定 :
steps
Specifies a stepping function, described above, taking two parameters. The first parameter specifies the number of intervals in the function. It must be a positive integer (greater than 0). The second parameter, which is optional, is either the value ‘start’ or ‘end’, and specifies the point at which the change of values occur within the interval. If the second parameter is omitted, it is given the value ‘end’.
粗略翻譯如下: steps 函數指定了一個階躍函數,第一個參數指定了時間函數中的間隔數量(必須是正整數);第二個參數可選,接受 start 和 end 兩個值,指定在每個間隔的起點或是終點發生階躍變化,默認為 end。
這樣理解起來可能還是有點抽象,我們來個實例:
#demo {
animation-iteration-count: 2;
animation-duration: 3s;
}
這是一個 3s * 2 的動畫,我們分別對它應用 steps(3, start) 和 steps(3, end) ,做出階躍函數曲線如下:
1. steps(3, start)
steps() 第一個參數將動畫分割成三段。當指定躍點為 start 時,動畫在每個計時周期的起點發生階躍(即圖中 空心圓 → 實心圓 )。 由于第一次階躍發生在第一個計時周期的起點處(0s),所以我們看到的第一步動畫(初態)就為 1/3 的狀態,因此在視覺上動畫的過程為 1/3 → 2/3 → 1 。如果翻譯成 JavaScript,大致如下:
var animateAtStart = function (steps, duration) {
var current = 0;
var interval = duration / steps;
var timer = function () {
current++;
applyStylesAtStep(current);
if (current < steps) {
setTimeout(timer, interval);
}
};
timer();
};
2. steps(3, end)
當指定躍點為 end,動畫則在每個計時周期的終點發生階躍(即圖中 空心圓 → 實心圓 )。 由于第一次階躍發生在第一個計時周期結束時(1s),所以我們看到的初態為 0% 的狀態;而在整個動畫周期完成處(3s),雖然發生階躍跳到了 100% 的狀態,但同時動畫結束,所以 100% 的狀態不可視。因此在視覺上動畫的過程為 0 → 1/3 → 2/3 (回憶一下數電里的異步清零,當所有輸出端都為高電平的時候觸發清零,所以全為高電平是暫態)。同樣翻譯成 JavaScript 如下:
var animateAtEnd = function (steps, duration) {
var current = 0;
var interval = duration / steps;
var timer = function () {
applyStylesAtStep(current);
current++;
if (current < steps) {
setTimeout(timer, interval);
}
};
timer();
};
如果這樣的解釋還是讓你覺得云里霧里,可以參考這個交互 DEMO
三、實際應用
雖然寫了這么多,但還是不得不說一句 timing-function: steps() 在實際設計中的應用少之又少,但是配合一些奇淫技巧還是能做出一些不錯的效果:
1. 定時遮罩
在CSS3 環形進度條這篇文章里,我們曾用到 timing-function 來使半圓環定時顯示/隱藏:
$precent: 5; // 進度百分比
$duration: 2s; // 動畫時長
@keyframes toggle {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.progress-right {
// 初態為 opacity: 0; 動畫周期結束后為 opacity: 1;
opacity: 1;
animation: toggle ($duration * 50 / $precent) step-end; // step-end = steps(1, end)
}
.progress-cover {
// 初態為 opacity: 1; 動畫周期結束后為 opacity: 0;
opacity: 0;
animation: toggle ($duration * 50 / $precent) step-start; // step-start = steps(1, start)
}
這里的關鍵是使用了 step-start 與 step-end 控制動畫,因為動畫只有兩個關鍵幀,參考上文可以得出:
step-start:動畫一開始就跳到 100% 直到周期結束
step-end:保持 0% 的樣式直到周期結束
要注意的是, timing-function 是 作用于每兩個關鍵幀之間,而不是整個動畫 ( the ‘animation-timing-function’ applies between keyframes, not over the entire animation ),所以就有了張鑫旭老師在 小tip: CSS3 animation漸進實現點點點等待提示效果 一文中得出的結論:
>step-start, 顧名思意,“一步一步開始”,表現在動畫中就是一幀一幀播放、一頓一頓畫面
2. Sprite 精靈動畫
在CSS3 實現奔跑動畫這篇文章里,我們使用 CSS3 Animation 來實現游戲開發中的精靈動畫:
$spriteWidth: 140px; // 精靈寬度
@keyframes run {
0% {
background-position: 0 0;
}
100% {
background-position: -($spriteWidth * 12) 0; // 12幀
}
}
#sprite {
width: $spriteWidth;
height: 144px;
background: url("../images/sprite.png") 0 0 no-repeat;
animation: run 0.6s steps(12) infinite;
}
其原理是:使用一張含有多幀靜態畫面的圖片,通過切換 background-position 使其變為連續的動畫。