發布日期:2026 年 2 月 19 日
Chrome 在 2025 年推出的 CSS 功能之一是
corner-shape。
您可以使用 bevel 和 scoop 等關鍵字,定義具有 border-radius 的邊角形狀。您也可以使用 superellipse 函式,接收介於 -Infinity 和 Infinity 之間的值。
如要瞭解這項功能及其運作方式,請參閱 Amit Sheen 在 Frontend Masters 撰寫的詳盡文章。
在 2025 年初實作這項功能時,我遇到了一些有趣且複雜程度不一的挑戰。我學到許多有關超橢圓、Blink 中的邊框繪製,以及使用向量數學進行 2D 繪圖的知識。
這份文件分享了我學到的一些內容,或許其他人也會感興趣。
凸形和凹形的對稱
傳統上,superellipse (k) 值介於 0 和 Infinity 之間,其中 0 到 1 之間的值為凹面,其餘值為凸面 (1 為 bevel),但 CSS 規格中的 superellipse 值介於 -Infinity 和 Infinity 之間,並代表 2k。這樣一來,任何正值看起來都會像是對應負值的鏡像。
不過,根據預設,superellipse 公式不會這樣運作。
superellipse 公式為:xk + yk = 1。反向公式 x1/k + y1/k = 1 不會產生視覺上對稱的曲線。
舉例來說,如果 k 為 2:
- 藍色曲線代表一輪
superellipse(y=xn)。 - 紅線代表
scoopsuperellipse,其標準公式為 (y=x1/n)。 - 黃色曲線代表與藍色曲線 (
y=1-(1-x)n) 視覺上對稱的曲線。
如圖所示,這些形狀並不相同!
我不會深入探討相關數學概念,但這與對偶範數和我們對曲率的感知有關。
就規格和實作而言,我們在此呈現的是視覺內容,因此在計算凹形時,會使用對稱的等效項目。其餘數學運算則會針對凸面形狀 (k>=1 或正超級橢圓值) 進行。
已關閉表單公式
下一個挑戰是以封閉形式表示曲線或 superellipse 的周長,也就是由簡單算術運算組成的公式。這對效能至關重要,可讓系統將 superellipse 算繪作業交給圖形引擎。
Skia 等圖像引擎熟悉貝茲曲線,因此使用少量貝茲曲線來近似表示 superellipse 的周長,可提升 superellipse 曲線的算繪效能。
幸好,我們可以透過符號迴歸找出公式,將凸角的一半表示為單一三次貝茲曲線。
三次方貝茲曲線有四個點:
- 第一個點是 (
0, 1)。 - 最後一個點是實際的超橢圓半角:
0.51/k,0.51/k。 - 第一個控制點會與起點位於同一層級,並向內延伸:(
a, 1)。 - 第二個控制點位於半角對角:
(0.51/k - b,0.51/k + b)。
這裡使用的半角值恰好是重要的座標,我們稍後會用於其他計算。
其中 a 和 b 是使用符號迴歸從 k 計算而來。
計算這四個點,並在這些點之間算繪三次貝茲曲線,即可提供具有指定 k 的封閉式凸半角。接著,旋轉結果以填滿其餘角落,套用至其他角落,然後翻轉結果以算繪凹面等效項目。
我們不會深入探討數學細節,但計算 a 和 b 的公式如下:
p0 = 1.2430920942724248
p1 = 2.010479023614843
p2 = 0.32922901179443753
p3 = 0.2823023142212073
p4 = 1.3473704261055421
p5 = 2.9149468637949814
p6 = 0.9106507102917086
s = log2(k)
slope = p0 + (p6 - p0) * 0.5 * (1 + tanh(p5 * (s - p1)))
base = 1 / (1 + exp(slope * p1))
logistic = 1 / (1 + exp(slope * (p1 - s)))
a = (logistic - base) / (1 - base)
b = p2 * exp(-p[3] * (s ^ p4))
框線和陰影
除了計算角落周邊的路徑,系統也會計算向內偏移 (邊框或插邊 box-shadow) 或向外偏移 (outline 或一般 box-shadow) 時的外觀。在傳統的圖形程式庫中,這是透過筆觸完成。
不過,CSS 中的邊框和陰影的算繪特性與筆觸不同:
- 邊框不一致。
- 舉例來說,頂端邊框可以是 10 像素,右側邊框可以是 5 像素,而角落則會在這兩者之間插值。
- 此外,這類耳機的音訊會傳入耳內,而不是傳到兩側。
- 陰影和外框的算繪方式與筆觸不盡相同。
- 而是會調整,讓邊角呈現銳利感。
一般邊框和陰影的算繪路徑適用於圓形或比圓形更凸的 corner-shape 值 (例如 squircle),且可旋轉 90 度,適用於比 scoop 更凹的形狀,但這個預設值不適用於介於 -1 和 1 之間的 corner-shape 值,因為將邊框或陰影與邊緣平行偏移會產生寬度不均的角。
舉例來說,如果以 bevel 邊角為基準,將邊框向兩側偏移幾個像素,就會產生「腹部」效果,也就是邊角中間看起來比兩側寬。
為此,目標是建立類似筆觸的效果,也就是找出開頭的角曲線法線,並將其長度設為 border 或 shadow-spread 的寬度。
幸好,這只適用於子橢圓 (介於 bevel 和圓形之間),因為超橢圓 (例如 squircle) 可正常運作。
如要找出子橢圓曲線的法線,只要找出對應二次曲線的法線即可,因為子橢圓和對應的二次曲線非常接近。
使用先前計算的相同半角,即可找出具有相同中點的二次曲線、推導出二次控制點,然後直接計算法線。
正常狀態會繼續以與 border-width 或 shadow-spread 相同的長度延伸,然後使用邊緣 (邊框的內緣,陰影的外緣) 裁剪產生的曲線,建立連續路徑。
有許多數學上更準確的方法可計算 superellipse 的切線,但這個方法效率高,且產生的結果足以用於算繪邊框和陰影。
顏色接合
瀏覽器中發生的有趣繪圖作業,在 CSS 中未指定。這個屬性會算繪顏色或樣式不一致的邊框。 舉例來說,如果元素有綠色實心上框線和黃色虛線右框線,在這些情況下,「斜接」是切口線,位於邊框邊緣的相關角落和邊框間距邊緣的相關角落之間。在相鄰邊緣之間建立界線。雖然未指定,但瀏覽器之間的算繪方式大致一致。
Blink (和其他瀏覽器) 的實作方式如下。即將繪製的邊緣會粗略剪裁,就像在斜接處交叉的多邊形一樣,計算方式會納入相關邊緣,但不納入任何其他邊緣。這樣可避免溢出,以錯誤的樣式和顏色繪製其他其中一個邊緣。
到目前為止,這個多邊形的計算相對簡單,因為使用一般圓角時,角落區域絕不會重疊。不過,這會隨著次橢圓形而改變,尤其是凹面超橢圓形 (負 superellipse 值)。這些形狀可能會建立相當有趣的形狀,導致原始交集多邊形非常容易重疊和「溢出」。
請參考以下 CSS:
.weird {
width: 200px;
height: 200px;
corner-shape: scoop round;
border-radius: 80% 20% / 50% 50%;
border-width: 10px;
border-color: orange purple black blue;
border-style: solid dotted;
}
我們要分別剪下每個邊緣 (橘色、紫色虛線、黑色、藍色虛線),然後繪製路徑。
如要達成這個目標,且不與其他三個角重疊,就必須仔細剪裁。
以橘色 (頂端) 邊緣為例。
很難找到包含整個邊緣的多邊形,且不會溢出到紫色、黃色,甚至是黑色邊緣。其他形狀則較難辨識。
這個程序會用到三個片段。
第一個短片包含整個邊緣,以及完整的角落 (沒有斜接)。例如:
這由兩個角 (一個 scoop,一個圓角) 組成,兩者之間有最小邊緣,並在兩端相連。
從這個形狀開始,可避免與對邊重疊,現在只剩下兩個斜接處需要處理。
方法是從這個角落剪下多邊形,該多邊形會通過邊框邊緣和邊框間距邊緣的角落,並在即將與邊緣相交時停止:
系統會找出從邊界邊緣到邊框間距邊緣的線條,與相關起點的曲線切線相交的點 (如果曲線是凹形)。
如果該點位於算繪區域內,程序就會停止,並沿著該切線繼續,直到再次與邊界方塊相交,完成四邊形。
否則,簡單的三角形可能會遭到裁剪。
摘要
網頁平台為網頁設計師和開發人員提供強大的表現力。 有時,採用單一數值的 CSS 屬性會隱藏底層的重大複雜性,確保準確且一致地算繪。
corner-shape 功能的複雜程度令人意外。這份文件旨在協助日後開發這項功能的開發人員,無論是在 Blink、其他瀏覽器或規格中。