當 CSS cubic-bezier() 的 x1 或 x2 超出封閉區間 0–1 時,該值即屬無效,瀏覽器會默默回退至關鍵字 ease。這就是為什麼在預覽工具中「看起來對」的曲線,在正式環境中卻可能呈現平淡的線性運動。除此之外,產生過程中還會出現其他幾種失敗模式:y1 與 y2 超出 0–1 區間時,可能使動畫內容超出其容器;曲線預覽器中顯示的中點數值常會回報錯誤的 y 值,因為經過時間軸並不等同於曲線參數;而複製貼上的宣告仍需搭配正確的屬性、距離、持續時間,以及對應的減 motion 備援,才能正常運作。CSS Cubic Bezier 產生器將上述所有限制內建於介面中,在輸入層級將 x 限制在有效的 CSS 範圍內,並於輸出前驗證每一筆輸入。但真正能讓你隔天不必再重新除錯同一條曲線的,是理解這些數字背後究竟出了什麼問題。

無效的 CSS 在瀏覽器中實際會發生什麼

cubic-bezier() 宣告需接受正好四個數字:x1、y1、x2、y2。第一與第三個數字描述沿著水平軸的輸入時間位置,因此 W3C 的 CSS Easing Functions Level 1 規範要求兩者皆須落在封閉區間 0–1 內。當其中一個 x 滑出該範圍時,整個宣告會在解析階段失效。瀏覽器不會拋出警告、不會在 DevTools 中標示出問題的屬性,也不會在主控台輸出任何訊息,而是直接代入關鍵字 ease 並繼續執行。這是自行產生 CSS cubic bezier 時最常見的單一隱性失效:你花了十分鐘微調出一條曲線,將它貼進樣式表,結果元素卻像頁面上所有其他 ease 一樣,以同樣平淡的加速度滑入。

你可以透過檢查元素上的計算後 timing-function 來確認這點。當自訂宣告被拒絕時,Chromium、Firefox 與 Safari 都會在 Computed 面板中回報 ease。重點在於:無效的 cubic-bezier 值並不會產生壞掉的 CSS 或解析錯誤,而是產生一份「能正常運作、只是不含你寫的那條曲線」的樣式表。CSS Cubic Bezier 產生器在輸入層級就將 x1 與 x2 限制在有效的 CSS 範圍內,因此在預覽中看起來連貫的曲線,也是瀏覽器必定會接受的曲線。

當 y 值逸出圖表範圍時,對動畫的影響

垂直座標 y1 與 y2 描述的是輸出進度而非輸入時間,CSS 允許它們超出 0–1 區間。負值 y 會產生 anticipation(預備動作),動畫數值會在朝目標前進前先稍微反向移動;而大於 1 的 y 值則會產生 overshoot(過衝),數值會先超過終點位置再回穩。這兩種效果常用於類彈簧按鈕、Toast 通知與彈跳點綴,而 W3C CSS Easing Functions Level 1 規範也明確允許它們。

出問題的時候,是當過衝幅度超過周圍版面所能吸收的程度。一個以 y2 = 1.8 平移 24 像素的按鈕,可能會超出目標 4 像素後再滑回,這看起來像是刻意設計。但一個從 1 縮放至 1、y2 同為 1.8 的按鈕,則會明顯放大至 1.8 倍再縮回,這經常會與相鄰元素重疊,或逸出原本預期能容納點擊範圍的裁切父層。在將 y 軸裁切為 0–1 的曲線圖預覽中,這個問題會被隱藏:線條看似離開可見的方框,但光看圖表無法判斷實際計算後的數值究竟走了多遠。CSS Cubic Bezier 產生器中的範例表格與動態預覽反映的是真實的計算後 easing,而非被裁切的圖表。因此要判斷過衝是否合理,唯一的方式就是讀取 t = 0.25、0.5、0.75 與 1 時的 y 輸出,並以實際距離觀察預覽的動態。

為什麼中點範例不等於曲線在 t = 0.5 時的數值

另一種隱性的失敗模式,出現在大多數產生器顯示於曲線旁的範例表格中。天真的實作會讀取曲線參數 t = 0.5 時的 y,並將其回報為「50% 進度」。這個讀數在 x 控制座標不對稱於 0.5 時會出錯,而這正是每個非線性 easing 關鍵字與大多數自訂曲線的情況。CSS easing 使用的是水平軸上的「經過輸入進度」,因此瀏覽器在時間中點實際套用的數值,是「使 x = 0.5 的那個曲線參數」所對應的 y 座標,而非「剛好等於 0.5 的曲線參數」所對應的 y 座標。

CSS Cubic Bezier 產生器正確地處理了這個問題。對於 0、0.25、0.5、0.75 與 1 各個輸入,引擎會先找出 x 座標符合該輸入的曲線參數,再於該參數上評估 y。實作上會先使用有界的牛頓迭代,當牛頓法會跨出有效範圍時,則退回到二分逼近法。實際效果是:當你在範例欄位中讀到 0.500 時,你看到的是動畫在實際持續時間中點會回報的數值。如果你切換到自訂曲線,而 50% 的範例數值仍接近 0.5,表示 easing 在中段大致上是線性的;如果它跳到 0.9,代表動畫在持續時間過半時就幾乎已經結束,這是過於積極的 ease-out 設計相當有用的線索。

產生一條通過驗證的 cubic-bezier

若要產生一份不會默默回退至 ease、也不會在執行階段出錯的 CSS cubic-bezier() 宣告,請依照以下步驟搭配 CSS Cubic Bezier 產生器操作:

  1. 若心中尚無四組數字,請從標準 easing 預設值開始。點選 ease、ease-in、ease-out、ease-in-out 或 linear,會載入 CSS Easing Functions Level 1 規範所定義的精確控制點,讓你從一條已知有效的曲線出發。
  2. 逐一調整 x1、y1、x2、y2。x 輸入受限於封閉區間 0–1,因此任何你能輸入的值,在語法上皆屬有效。y 輸入接受從負十到十之間的有限值,涵蓋所有合理的 anticipation 與 overshoot,又不會讓解析器面對 NaN 或無限大。
  3. 同時閱讀圖表與範例表格。圖表顯示的是從固定起點 (0,0) 到固定終點 (1,1) 的參數化形狀;表格顯示的是在 0、0.25、0.5、0.75 與 1 各時間標記上的實際 y 值。當兩者對「50% 看起來像什麼」有出入時,請以表格為準。
  4. 使用 Run 預覽,在兩個水平位置之間以 900 毫秒的 transform 過場播放該曲線。在確定採用之前,請以你預定的距離與屬性觀察其動態。
  5. 透過 Copy CSS 按鈕複製宣告。輸出為完整的 transition-timing-function 宣告,包含分號,數值四捨五入至小數三位。若剪貼簿存取被拒,該宣告仍會保持可見且可選取,讓你仍能手動貼上而不損失成果。

若貼上後曲線仍行為異常,請依序檢查 cubic-bezier 疑難排解清單,以排除如 transition 簡寫衝突或繼承而來的 timing function 等多層次原因。

五個標準關鍵字與它們實際載入的曲線

下表列出每個內建 CSS easing 關鍵字在 cubic-bezier() 形式下所代表的內容。這些數值取自 W3C CSS Easing Functions Level 1 規範中的關鍵字對應表,也是 CSS Cubic Bezier 產生器在你按下各關鍵字按鈕時所載入的同一組數字。

關鍵字x1y1x2y2
linear0011
ease0.250.10.251
ease-in0.42011
ease-out000.581
ease-in-out0.4200.581

若跳過這張表,容易出錯的地方有二。第一,你可能會以為關鍵字本身就是最安全的輸出,但此處每個關鍵字其實都是四個數字的簡寫;產生器刻意輸出這四個數字而非關鍵字,目的是讓宣告能被記錄、比較與修改,而不受隱含預設值影響。第二,你可能會因為 ease 與 ease-in-out 都含有「ease」一詞,就以為兩者行為相近。事實上,從表中可以看出,ease 的第一個控制點被前移至 (0.25, 0.1),第二個控制點則停留在 (0.25, 1);而 ease-in-out 是對稱的,開頭從未如此積極地加速。數值輸出使這項差異一目瞭然,僅憑關鍵字本身則無法看出。如需進一步參考,MDN 亦記載了同一個 cubic-bezier() 函式,並附上以瀏覽器為導向的範例與跨瀏覽器演進歷史。

複製之後:仍然會出錯的動態行為

有效的 transition-timing-function 宣告只是動畫能正常運作的其中一個要素。即使曲線正確,許多執行階段的行為仍可能讓人感覺不對。

第一個是屬性不匹配的問題。CSS Cubic Bezier Generator 中的 Run 預覽會在 transform 過渡上以 900 毫秒展示該曲線。同樣的曲線套用在 width、height 或 top 上感覺不會相同,因為每個屬性的算繪成本不同,畫素與感知動態之間的關係也不同。如果曲線在正式環境中感覺過於激進,原因通常在於屬性本身,而非緩動方式;同樣的數值在觸發版面配置的屬性上,與在 transform 或 opacity 上的表現並不相同。

第二個是對減少動態偏好的過衝問題。CSS 允許技術上有效,但對前庭敏感的使用者而言會感到不適的曲線。當 y2 為 2.0 時,會產生快速縮放超過結束位置的現象;如果該動畫屬於裝飾性質而非必要,請遵守 prefers-reduced-motion 媒體查詢,移除過渡或為受影響的使用者換上較平緩的曲線。MDN 將此視為可用性問題,而不僅僅是細節修飾。

第三個是焦點與指標行為。在元素進行過渡時,其邊界框可能因過衝而短暫超出靜止時的大小。如果該元素是可互動的,過衝可能會顯示或遮蔽鄰近的點擊目標、改變可見的懸停區域,或將元素拉出原本預期容納它的裁切父層。請務必在真實元件上測試曲線,而非在孤立的測試方塊上。

第四個是簡寫屬性的陷阱。transition: all 300ms cubic-bezier(...) 看起來很誘人,但 transition: all 同時也會對你並未預期的屬性進行動畫,包括某些會產生可見重排的屬性。明確指定屬性可以避免這個意外,並讓曲線對單一屬性的效果變得可預測。

第五個是圖表中的靜默裁切問題。自訂曲線的 y2 若遠高於 1,會使可見方塊停留在產生器的繪圖區域內。圖表維持穩定的 0–1 框架,以便輕鬆比較一般計時曲線,但曲線本身仍以完整精度運算,與線條在畫面上的位置無關。如果過衝對你的設計很重要,請檢視動態預覽與數值樣本;它們反映的是實際運算後的緩動,而非被夾住後的顯示結果。

大多數 cubic-bezier 的失敗都是靜默的。無效的 x 值會在沒有警告的情況下淡出為 ease,y 的過衝隱藏在被裁切的圖表中,而中點讀數可能會在無聲中回報錯誤的 y 值給所要求的時間。將驗證機制建置在輸入層——將 x 限制在 [0,1]、接受有界的 y 值、先解出 x 再讀取 y,並將結果序列化為完整的四個數字宣告——即可徹底消除這類靜默回退的失敗。一旦計時函式經過驗證,剩下的工作就是選擇與曲線搭配的屬性、距離與持續時間,並在非必要的動畫處遵守減少動態的偏好。

若想深入了解,請參閱 如何開始使用 CSS 三角形產生器。