Javascript CSS 動(dòng)畫

2023-02-17 10:58 更新

CSS 動(dòng)畫可以在不借助 Javascript 的情況下做出一些簡(jiǎn)單的動(dòng)畫效果。

你也可以通過(guò) Javascript 控制 CSS 動(dòng)畫,使用少量的代碼,就能讓動(dòng)畫表現(xiàn)更加出色。

CSS 過(guò)渡(transition)[#css-transition]

CSS 過(guò)渡的理念非常簡(jiǎn)單,我們只需要定義某一個(gè)屬性以及如何動(dòng)態(tài)地表現(xiàn)其變化。當(dāng)屬性變化時(shí),瀏覽器將會(huì)繪制出相應(yīng)的過(guò)渡動(dòng)畫。

也就是說(shuō):我們只需要改變某個(gè)屬性,然后所有流暢的動(dòng)畫都由瀏覽器生成。

舉個(gè)例子,以下 CSS 會(huì)為 backgroud-color 的變化生成一個(gè) 3 秒的過(guò)渡動(dòng)畫:

.animated {
  transition-property: background-color;
  transition-duration: 3s;
}

現(xiàn)在,只要一個(gè)元素?fù)碛忻麨?nbsp;.animated 的類,那么任何背景顏色的變化都會(huì)被渲染為 3 秒鐘的動(dòng)畫。

單擊以下按鈕以演示動(dòng)畫:

<button id="color">Click me</button>

<style>
  #color {
    transition-property: background-color;
    transition-duration: 3s;
  }
</style>

<script>
  color.onclick = function() {
    this.style.backgroundColor = 'red';
  };
</script>

示例代碼

CSS 提供了四個(gè)屬性來(lái)描述一個(gè)過(guò)渡:

  • ?transition-property?
  • ?transition-duration?
  • ?transition-timing-function?
  • ?transition-delay?

之后我們會(huì)詳細(xì)介紹它們,目前我們需要知道,我們可以在 transition 中以 property duration timing-function delay 的順序一次性定義它們,并且可以同時(shí)為多個(gè)屬性設(shè)置過(guò)渡動(dòng)畫。

請(qǐng)看以下例子,點(diǎn)擊按鈕生成 color 和 font-size 的過(guò)渡動(dòng)畫:

<button id="growing">Click me</button>

<style>
#growing {
  transition: font-size 3s, color 2s;
}
</style>

<script>
growing.onclick = function() {
  this.style.fontSize = '36px';
  this.style.color = 'red';
};
</script>

示例代碼

現(xiàn)在讓我們一個(gè)一個(gè)展開(kāi)看這些屬性。

transition-property

在 transition-property 中我們可以列舉要設(shè)置動(dòng)畫的所有屬性,如:left、margin-left、height 和 color。

不是所有的 CSS 屬性都可以使用過(guò)渡動(dòng)畫,但是它們中的大多數(shù)都是可以的。all 表示應(yīng)用在所有屬性上。

transition-duration

transition-duration 允許我們指定動(dòng)畫持續(xù)的時(shí)間。時(shí)間的格式參照 CSS 時(shí)間格式:?jiǎn)挝粸槊?nbsp;s 或者毫秒 ms。

transition-delay

transition-delay 允許我們?cè)O(shè)定動(dòng)畫開(kāi)始前的延遲時(shí)間。例如,對(duì)于 transition-delay: 1s,動(dòng)畫將會(huì)在屬性變化發(fā)生 1 秒后開(kāi)始渲染。

你也可以提供一個(gè)負(fù)值。那么動(dòng)畫將會(huì)從整個(gè)過(guò)渡的中間時(shí)刻開(kāi)始渲染。例如,對(duì)于 transition-duration: 2s,同時(shí)把 delay 設(shè)置為 -1s,那么這個(gè)動(dòng)畫將會(huì)持續(xù) 1 秒鐘,并且從正中間開(kāi)始渲染。

這里演示了數(shù)字從 0 到 9 的動(dòng)畫,使用了 CSS translate 方法:

示例代碼

如下在 tranform 屬性上應(yīng)用動(dòng)畫:

#stripe.animate {
  transform: translate(-90%);
  transition-property: transform;
  transition-duration: 9s;
}

在以上的例子中,JavaScript 把 .animate 類添加到了元素上,由此觸發(fā)了動(dòng)畫:

stripe.classList.add('animate');

我們也可以『從中間』開(kāi)始,也就是說(shuō)從某個(gè)特定數(shù)字開(kāi)始,比方說(shuō),從當(dāng)前的時(shí)間的秒數(shù)開(kāi)始。這就要用到負(fù)的 transition-delay。

此處,如果你單擊這個(gè)數(shù)字,那么它會(huì)從當(dāng)前的秒數(shù)開(kāi)始渲染:

示例代碼

只需添加一行 JavaScript 代碼:

stripe.onclick = function() {
  let sec = new Date().getSeconds() % 10;
  // for instance, -3s here starts the animation from the 3rd second
  stripe.style.transitionDelay = '-' + sec + 's';
  stripe.classList.add('animate');
};

transition-timing-function

時(shí)間函數(shù)描述了動(dòng)畫進(jìn)程在時(shí)間上的分布。它是先慢后快還是先快后慢?

乍一看,這可能是最復(fù)雜的屬性了,但是稍微花點(diǎn)時(shí)間,你就會(huì)發(fā)現(xiàn)其實(shí)也很簡(jiǎn)單。

這個(gè)屬性接受兩種值:一個(gè)貝塞爾曲線(Bezier curve)或者階躍函數(shù)(steps)。我們先從貝塞爾曲線開(kāi)始,這也是較為常用的。

貝塞爾曲線(Bezier curve)

時(shí)間函數(shù)可以用貝塞爾曲線描述,通過(guò)設(shè)置四個(gè)滿足以下條件的控制點(diǎn):

  1. 第一個(gè)應(yīng)為:?(0,0)?。
  2. 最后一個(gè)應(yīng)為:?(1,1)?。
  3. 對(duì)于中間值,?x? 必須位于 ?0..1? 之間,?y? 可以為任意值。

CSS 中設(shè)置一貝塞爾曲線的語(yǔ)法為:cubic-bezier(x2, y2, x3, y3)。這里我們只需要設(shè)置第二個(gè)和第三個(gè)值,因?yàn)榈谝粋€(gè)點(diǎn)固定為 (0,0),第四個(gè)點(diǎn)固定為 (1,1)

時(shí)間函數(shù)描述了動(dòng)畫進(jìn)行的快慢。

  • ?x? 軸表示時(shí)間:?0? —— 開(kāi)始時(shí)刻,?1? —— ?transition-duration?的結(jié)束時(shí)刻。
  • ?y? 軸表示過(guò)程的完成度:?0? —— 屬性的起始值,?1? —— 屬性的最終值。

最簡(jiǎn)單的一種情況就是動(dòng)畫勻速進(jìn)行,可以通過(guò)設(shè)置曲線為 cubic-bezier(0, 0, 1, 1) 來(lái)實(shí)現(xiàn)。

看上去就像這樣:


…正如我們所見(jiàn),這就是條直線。隨著時(shí)間 x 推移,完成度 y 穩(wěn)步從 0 增長(zhǎng)到 1。

例子中的列車勻速地從左側(cè)移動(dòng)到右側(cè):

示例代碼

這個(gè)里面的 CSS 就是基于剛才那條曲線的:

.train {
  left: 0;
  transition: left 5s cubic-bezier(0, 0, 1, 1);
  /* JavaScript sets left to 450px */
}

…那么,我們?nèi)绻憩F(xiàn)出減速行駛的列車呢?

我們可以使用另一條貝塞爾曲線:cubic-bezier(0.0, 0.5, 0.5 ,1.0)

圖像如下:


正如我們所見(jiàn),這個(gè)過(guò)程起初很快:曲線開(kāi)始迅速升高,然后越來(lái)越慢。

這是實(shí)際的效果演示:

示例代碼

CSS:

.train {
  left: 0;
  transition: left 5s cubic-bezier(0, .5, .5, 1);
  /* JavaScript sets left to 450px */
}

CSS 提供幾條內(nèi)建的曲線:linear、easeease-in、ease-out 和 ease-in-out

linear 其實(shí)就是 cubic-bezier(0, 0, 1, 1) 的簡(jiǎn)寫 —— 一條直線,剛剛我們已經(jīng)看過(guò)了。

其它的名稱是以下貝塞爾曲線的簡(jiǎn)寫:

ease* ease-in ease-out ease-in-out
(0.25, 0.1, 0.25, 1.0) (0.42, 0, 1.0, 1.0) (0, 0, 0.58, 1.0) (0.42, 0, 0.58, 1.0)




* —— 默認(rèn)值,如果沒(méi)有指定時(shí)間函數(shù),那么將使用 ease 作為默認(rèn)值。

所以,我們可以使用 ease-out 來(lái)表現(xiàn)減速行駛的列車:

.train {
  left: 0;
  transition: left 5s ease-out;
  /* transition: left 5s cubic-bezier(0, .5, .5, 1); */
}

但是這看起來(lái)有點(diǎn)怪怪的。

貝塞爾曲線可以使動(dòng)畫『超出』其原本的范圍。

曲線上的控制點(diǎn)的 y 值可以使任意的:不管是負(fù)值還是一個(gè)很大的值。如此,貝塞爾曲線就會(huì)變得很低或者很高,讓動(dòng)畫超出其正常的范圍。

在一下的例子中使用的代碼:

.train {
  left: 100px;
  transition: left 5s cubic-bezier(.5, -1, .5, 2);
  /* JavaScript sets left to 400px */
}

left 本該在 100px 到 400px 之間變化。

但是如果你點(diǎn)擊列車,你會(huì)發(fā)現(xiàn):

  • 起初,列車會(huì)反向運(yùn)動(dòng):?left? 會(huì)變得小于 ?100px?。
  • 然后,它會(huì)變回往前運(yùn)動(dòng),并且超過(guò) ?400px?。
  • 最后再返回 —— 回到 ?400px?。

示例代碼

為什么會(huì)這樣?看一眼給定的貝塞爾曲線的圖像你就會(huì)明白了。


我們把第二個(gè)點(diǎn)的 y 坐標(biāo)移動(dòng)到了小于 0 的位置,同時(shí)把第三個(gè)點(diǎn)的 y 坐標(biāo)移動(dòng)到了大于 1 的位置,因此曲線已經(jīng)不再像一個(gè)四分之一圓了。y 坐標(biāo)超出了常規(guī)的 0..1 的范圍。

正如我們所知,y 表示『動(dòng)畫進(jìn)程的完成度』。y = 0 表示屬性的初始值,y = 1 則表示屬性的最終值。因此,y < 0 意味著屬性值要比初始值小,而 y > 1 則表明屬性值要比最終值大。

當(dāng)然了,-1 和 2 還是比較緩和的值。如果我們把 y 設(shè)為 -99 和 99,那么列車將會(huì)偏離地更遠(yuǎn)。

但是,如何針對(duì)特定的任務(wù)尋找到合適的貝塞爾曲線呢?事實(shí)上,有很多工具可以幫到你。比方說(shuō),我們可以利用這個(gè)網(wǎng)站:http://cubic-bezier.com/。

階躍函數(shù)(Steps)

時(shí)間函數(shù) steps(number of steps[, start/end]) 允許你讓動(dòng)畫分段進(jìn)行,number of steps 表示需要拆分為多少段。

讓我們通過(guò)一個(gè)數(shù)字的例子來(lái)演示一下。我們將會(huì)讓數(shù)字以離散的方式變化,而不是以連續(xù)的方式。

為了達(dá)到效果,我們把動(dòng)畫拆分為 9 段:

#stripe.animate  {
  transform: translate(-90%);
  transition: transform 9s steps(9, start);
}

step(9, start) 生效時(shí):

示例代碼

steps 的第一個(gè)參數(shù)表示段數(shù)。這個(gè)過(guò)渡動(dòng)畫將會(huì)被拆分為 9 個(gè)部分(每個(gè)占 10%)。時(shí)間間隔也會(huì)以同樣的方式被拆分:9 秒會(huì)被分割為多個(gè)時(shí)長(zhǎng) 1 秒的間隔。

第二個(gè)參數(shù)可以取 start 或 end 兩者其一。

start 表示在動(dòng)畫開(kāi)始時(shí),我們需要立即開(kāi)始第一段的動(dòng)畫。

可以觀察到,在動(dòng)畫過(guò)程中:當(dāng)我們單擊數(shù)字之后,它會(huì)立馬變?yōu)?nbsp;1(即第一段),然后在下一秒開(kāi)始的時(shí)候繼續(xù)變化。

具體的流程如下:

  • ?0s? —— ?-10%?(在第一秒開(kāi)始的時(shí)候立即變化)
  • ?1s? —— ?-20%?
  • ?8s? – ?-80%?
  • (最后一秒,顯示最終值)

另一個(gè)值 end 表示:改變不應(yīng)該在最開(kāi)始的時(shí)候發(fā)生,而是發(fā)生在每一段的最后時(shí)刻。

其流程如下:

  • 0s —— 0
  • 1s —— -10%(在第一秒結(jié)束時(shí)第一次變化)
  • 2s —— -20%
  • 9s —— -90%


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)