Javascript CSS 動畫

2023-02-17 10:58 更新

CSS 動畫可以在不借助 Javascript 的情況下做出一些簡單的動畫效果。

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

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

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

也就是說:我們只需要改變某個屬性,然后所有流暢的動畫都由瀏覽器生成。

舉個例子,以下 CSS 會為 backgroud-color 的變化生成一個 3 秒的過渡動畫:

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

現(xiàn)在,只要一個元素擁有名為 .animated 的類,那么任何背景顏色的變化都會被渲染為 3 秒鐘的動畫。

單擊以下按鈕以演示動畫:

<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 提供了四個屬性來描述一個過渡:

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

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

請看以下例子,點擊按鈕生成 color 和 font-size 的過渡動畫:

<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)在讓我們一個一個展開看這些屬性。

transition-property

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

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

transition-duration

transition-duration 允許我們指定動畫持續(xù)的時間。時間的格式參照 CSS 時間格式:單位為秒 s 或者毫秒 ms。

transition-delay

transition-delay 允許我們設(shè)定動畫開始前的延遲時間。例如,對于 transition-delay: 1s,動畫將會在屬性變化發(fā)生 1 秒后開始渲染。

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

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

示例代碼

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

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

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

stripe.classList.add('animate');

我們也可以『從中間』開始,也就是說從某個特定數(shù)字開始,比方說,從當前的時間的秒數(shù)開始。這就要用到負的 transition-delay。

此處,如果你單擊這個數(shù)字,那么它會從當前的秒數(shù)開始渲染:

示例代碼

只需添加一行 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ù)描述了動畫進程在時間上的分布。它是先慢后快還是先快后慢?

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

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

貝塞爾曲線(Bezier curve)

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

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

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

時間函數(shù)描述了動畫進行的快慢。

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

最簡單的一種情況就是動畫勻速進行,可以通過設(shè)置曲線為 cubic-bezier(0, 0, 1, 1) 來實現(xiàn)。

看上去就像這樣:


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

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

示例代碼

這個里面的 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)。

圖像如下:


正如我們所見,這個過程起初很快:曲線開始迅速升高,然后越來越慢。

這是實際的效果演示:

示例代碼

CSS:

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

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

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

其它的名稱是以下貝塞爾曲線的簡寫:

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)




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

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

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

但是這看起來有點怪怪的。

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

曲線上的控制點的 y 值可以使任意的:不管是負值還是一個很大的值。如此,貝塞爾曲線就會變得很低或者很高,讓動畫超出其正常的范圍。

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

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

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

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

  • 起初,列車會反向運動:?left? 會變得小于 ?100px?。
  • 然后,它會變回往前運動,并且超過 ?400px?。
  • 最后再返回 —— 回到 ?400px?。

示例代碼

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


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

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

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

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

階躍函數(shù)(Steps)

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

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

為了達到效果,我們把動畫拆分為 9 段:

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

step(9, start) 生效時:

示例代碼

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

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

start 表示在動畫開始時,我們需要立即開始第一段的動畫。

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

具體的流程如下:

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

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

其流程如下:

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


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號