使用 CSS 過渡

CSS 過渡提供了一種控制 CSS 屬性變化時動畫速度的方法。您不再需要讓屬性變化立即生效,而是可以讓屬性變化在一段時間內發生。例如,如果您將元素的顏色從白色更改為黑色,通常變化是瞬時的。啟用 CSS 過渡後,變化會按照加速度曲線在時間間隔內發生,所有這些都可以自定義。

涉及在兩個狀態之間過渡的動畫通常被稱為隱式過渡,因為起始狀態和最終狀態之間的中間狀態是由瀏覽器隱式定義的。

A CSS transition tells the browser to draw the intermediate states between the initial and final states, showing the user a smooth transitions.

CSS 過渡讓您可以決定要動畫哪些屬性(透過明確列出它們)、動畫何時開始(透過設定延遲)、過渡將持續多長時間(透過設定持續時間),以及過渡如何執行(透過定義一個緩動函式,例如,線性或開始時快,結束時慢)。

哪些 CSS 屬性可以進行過渡?

Web 作者可以定義哪些屬性需要動畫以及以何種方式動畫。這允許建立複雜的過渡。然而,有些屬性是不可動畫的,因為動畫它們沒有意義。

注意:auto 值通常是一個非常複雜的情況。規範建議不要從 auto 動畫到 auto。一些使用者代理,例如基於 Gecko 的使用者代理,實現了此要求,而其他使用者代理,例如基於 WebKit 的使用者代理,則不那麼嚴格。將動畫與 auto 一起使用可能會導致不可預測的結果,具體取決於瀏覽器及其版本,應避免使用。

定義過渡

CSS 過渡使用速記transition屬性進行控制。這是配置過渡的最佳方式,因為它更容易避免參數不同步,這在 CSS 除錯中可能非常令人沮喪。

您可以使用以下子屬性控制過渡的各個元件

transition-property

指定應應用過渡的 CSS 屬性的名稱。只有此處列出的屬性在過渡期間進行動畫;所有其他屬性的更改照常立即發生。

transition-duration

指定過渡應發生持續時間。您可以指定適用於過渡期間所有屬性的單個持續時間,或指定多個值以允許每個屬性在不同的時間段內過渡。

transition-timing-function

指定一個函式來定義屬性的中間值如何計算。緩動函式確定如何計算過渡的中間值。大多數緩動函式可以透過提供相應函式的圖形來指定,該圖形由定義三次貝塞爾曲線的四個點定義。您還可以從緩動函式備忘單中選擇緩動。

transition-delay

定義屬性更改和過渡實際開始之間等待多長時間。

transition 速記 CSS 語法編寫如下

transition: <property> <duration> <timing-function> <delay>;

示例

基本示例

此示例執行一個四秒的字型大小過渡,並在使用者將滑鼠懸停在元素上和動畫效果開始之間有兩秒的延遲

css
#delay {
  font-size: 14px;
  transition-property: font-size;
  transition-duration: 4s;
  transition-delay: 2s;
}

#delay:hover {
  font-size: 36px;
}

多個動畫屬性示例

CSS

css
.box {
  border-style: solid;
  border-width: 1px;
  display: block;
  width: 100px;
  height: 100px;
  background-color: blue;
  transition:
    width 2s,
    height 2s,
    background-color 2s,
    rotate 2s;
}

.box:hover {
  background-color: #ffcccc;
  width: 200px;
  height: 200px;
  rotate: 180deg;
}

當屬性值列表長度不同時

如果任何屬性的值列表比其他屬性短,則其值會重複以使其匹配。例如

css
div {
  transition-property: opacity, left, top, height;
  transition-duration: 3s, 5s;
}

這被視為

css
div {
  transition-property: opacity, left, top, height;
  transition-duration: 3s, 5s, 3s, 5s;
}

同樣,如果任何屬性的值列表比transition-property的值列表長,它將被截斷,因此如果您有以下 CSS

css
div {
  transition-property: opacity, left;
  transition-duration: 3s, 5s, 2s, 1s;
}

這被解釋為

css
div {
  transition-property: opacity, left;
  transition-duration: 3s, 5s;
}

在突出顯示選單時使用過渡

CSS 的一個常見用途是當用戶將滑鼠游標懸停在選單項上時突出顯示它們。使用過渡可以輕鬆使效果更具吸引力。

首先,我們使用 HTML 設定選單

html
<nav>
  <a href="#">Home</a>
  <a href="#">About</a>
  <a href="#">Contact Us</a>
  <a href="#">Links</a>
</nav>

然後我們構建 CSS 來實現選單的外觀和感覺

css
nav {
  display: flex;
  gap: 0.5rem;
}

a {
  flex: 1;
  background-color: #333333;
  color: white;
  border: 1px solid;
  padding: 0.5rem;
  text-align: center;
  text-decoration: none;
  transition: all 0.5s ease-out;
}

a:hover,
a:focus {
  background-color: white;
  color: #333333;
}

此 CSS 建立了選單的外觀,當元素處於其:hover:focus狀態時,背景和文字顏色都會改變

過渡 displaycontent-visibility

此示例演示瞭如何過渡displaycontent-visibility。此行為對於建立進入/退出動畫很有用,例如,您希望使用 display: none 從 DOM 中刪除容器,但希望它使用opacity淡出而不是立即消失。

支援的瀏覽器透過離散動畫型別的變體過渡 displaycontent-visibility。這通常意味著屬性在兩種值之間動畫進行到 50% 時會翻轉。

但是有一個例外,那就是動畫到/從 display: nonecontent-visibility: hidden。在這種情況下,瀏覽器將在兩個值之間翻轉,以便過渡內容在整個動畫持續時間內顯示。

所以例如:

  • displaynone 動畫到 block(或其他可見的 display 值)時,該值將在動畫持續時間的 0% 處切換到 block,使其在整個過程中可見。
  • displayblock(或其他可見的 display 值)動畫到 none 時,該值將在動畫持續時間的 100% 處切換到 none,使其在整個過程中可見。

在過渡這些屬性時,需要在過渡上設定transition-behavior: allow-discrete。這有效地啟用了 display/content-visibility 過渡。

在過渡 display 時,需要@starting-style來為元素上設定的屬性提供一組起始值,當元素接收到其第一個樣式更新時,您希望從這些值開始過渡。這是為了避免意外行為。預設情況下,當元素首次出現在 DOM 中時,CSS 過渡不會在元素的第一個樣式更新時觸發,這包括當 displaynone 更改為另一個狀態時。content-visibility 動畫不需要在 @starting-style 塊中指定起始值。這是因為 content-visibility 不會像 display 那樣從 DOM 中隱藏元素:它只是跳過渲染元素的內容。

HTML

HTML 包含兩個<p>元素,中間有一個<div>,我們將從 display none 動畫到 block

html
<p>
  Click anywhere on the screen or press any key to toggle the
  <code>&lt;div&gt;</code> between hidden and showing.
</p>

<div>
  This is a <code>&lt;div&gt;</code> element that transitions between
  <code>display: none; opacity: 0</code> and
  <code>display: block; opacity: 1</code>. Neat, huh?
</div>

<p>
  This is another paragraph to show that <code>display: none;</code> is being
  applied and removed on the above <code>&lt;div&gt; </code>. If only its
  <code>opacity</code> was being changed, it would always take up the space in
  the DOM.
</p>

CSS

css
html {
  height: 100vh;
}

div {
  font-size: 1.6rem;
  padding: 20px;
  border: 3px solid red;
  border-radius: 20px;
  width: 480px;

  display: none;
  opacity: 0;
  transition:
    opacity 1s,
    display 1s allow-discrete;
  /* Equivalent to
  transition: all 1s allow-discrete; */
}

.showing {
  opacity: 1;
  display: block;
}

@starting-style {
  .showing {
    opacity: 0;
  }
}

請注意用於指定過渡起始樣式的 @starting-style 塊,以及在過渡列表中包含 display 屬性,並設定了 allow-discrete

JavaScript

最後,我們包含一些 JavaScript 來設定事件監聽器以觸發過渡(透過 showing 類)。

js
const divElem = document.querySelector("div");
const htmlElem = document.querySelector(":root");

htmlElem.addEventListener("click", showHide);
document.addEventListener("keydown", showHide);

function showHide() {
  divElem.classList.toggle("showing");
}

結果

程式碼渲染如下:

JavaScript 示例

注意:在使用過渡後立即使用時應謹慎

  • 使用 .appendChild() 將元素新增到 DOM
  • 移除元素的 display: none; 屬性。

這被視為初始狀態從未發生過,並且元素始終處於其最終狀態。克服此限制的簡單方法是在更改您打算過渡到的 CSS 屬性之前應用幾毫秒的 setTimeout()

使用過渡使 JavaScript 功能流暢

過渡是一個很好的工具,可以在不改變 JavaScript 功能的情況下使事物看起來更流暢。請看以下示例。

html
<p>Click anywhere to move the ball</p>
<div id="foo" class="ball"></div>
js
// Make the ball move to a certain position:
const f = document.getElementById("foo");
document.addEventListener("click", (ev) => {
  f.style.transform = `translateY(${ev.clientY - 25}px)`;
  f.style.transform += `translateX(${ev.clientX - 25}px)`;
});

使用 CSS,您可以平滑透過 JavaScript 應用的樣式。向元素新增過渡,任何更改都將平滑發生

css
.ball {
  border-radius: 25px;
  width: 50px;
  height: 50px;
  background: #cc0000;
  position: absolute;
  top: 0;
  left: 0;
  transition: transform 1s;
}

檢測過渡的開始和完成

您可以使用transitionend事件來檢測動畫是否已完成執行。這是一個TransitionEvent物件,除了典型的Event物件之外,它還有兩個附加屬性

propertyName

一個字串,指示其過渡完成的 CSS 屬性的名稱。

elapsedTime

一個浮點數,指示事件觸發時過渡已執行的秒數。此值不受transition-delay值的影響。

像往常一樣,您可以使用addEventListener()方法來監聽此事件

js
el.addEventListener("transitionend", updateTransition);

您可以使用transitionrun(在任何延遲之前觸發)和transitionstart(在任何延遲之後觸發)以相同的方式檢測過渡的開始

js
el.addEventListener("transitionrun", signalStart);
el.addEventListener("transitionstart", signalStart);

注意:如果過渡在完成之前中止,因為元素被設定為display: none或動畫屬性的值被更改,則不會觸發 transitionend 事件。

規範

規範
CSS 過渡

另見