開始使用 CSS 形狀

沿著自訂路徑排列內容

Razvan Caliman
Razvan Caliman

長期以來,網頁設計師都必須在矩形的限制內進行創作。網路上大多數內容仍侷限在簡單的方框中,因為大多數非矩形版面配置的創意嘗試都以失敗告終。不過,Chrome 37 推出 CSS 形狀後,這種情況即將改變。CSS 形狀可讓網頁設計師將內容包覆在自訂路徑周圍,例如圓形、橢圓形和多邊形,進而擺脫矩形的限制。

形狀可以手動定義,也可以從圖片推論。

我們來看一個非常簡單的例子。

或許您和我一樣,在首次浮動含有透明部分的圖片時,期待內容會包覆並填補空隙,但最後卻發現元素周圍仍有矩形包覆形狀,因而感到失望。CSS 形狀可用來解決這個問題。

從圖片擷取形狀
<img class="element" src="image.png" />
<p>Lorem ipsum…</p>

<style>
.element{
  shape-outside: url(image.png);
  shape-image-threshold: 0.5;
  float: left;
}
</style>

shape-outside: url(image.png) CSS 宣告會指示瀏覽器從圖片中擷取形狀。

shape-image-threshold 屬性定義了用於建立形狀的像素不透明度下限。值必須介於 0.0 (完全透明) 和 1.0 (完全不透明) 之間。因此,shape-image-threshold: 0.5 表示只有不透明度 50% 以上的像素會用來建立形狀。

float 屬性是關鍵。雖然 shape-outside 屬性定義了內容環繞的區域形狀,但如果沒有浮動,就無法看到形狀的效果。

元素的浮動區域位於 float 值的對側。舉例來說,如果含有咖啡杯圖片的元素向左浮動,浮動區域就會建立在咖啡杯右側。即使您設計的圖片兩側都有間隙,內容也只會沿著浮動屬性指定的形狀對側 (左側或右側,絕不會同時出現在兩側) 繞排。

未來,隨著 CSS 排除項目的推出,shape-outside 將可用於非浮動元素。

手動建立形狀

除了從圖片中擷取形狀,您也可以手動編寫程式碼。您可以選擇幾個函式值來建立形狀:circle()、ellipse()、inset() 和 polygon()。每個形狀函式都會接受一組座標,並與建立座標系統的參考方塊配對。稍後會進一步說明參考方塊。

circle() 函式

circle() 形狀值的插圖

圓形值的完整標記法為 circle(r at cx cy),其中 r 是圓形的半徑,cx 和 cy 則是圓心在 X 軸和 Y 軸上的座標。圓心座標為選用項目。如果省略,系統會預設使用元素的中心點 (對角線的交點)。

.element{
  shape-outside: circle(50%);
  width: 300px;
  height: 300px;
  float: left;
}

在上述範例中,內容會圍繞在圓形路徑外側。單一引數 50% 會指定圓形的半徑,在這個特定案例中,半徑相當於元素寬度或高度的一半。變更元素尺寸會影響圓形半徑。這是 CSS 形狀如何回應的基本範例。

在繼續之前,請先注意:請務必記住,CSS 形狀只會影響元素周圍浮動區域的形狀。如果元素有背景,則不會被形狀裁剪。如要達到這種效果,您必須使用 CSS 遮罩中的屬性,也就是 clip-path 或 mask-image。clip-path 屬性非常實用,因為它採用與 CSS Shapes 相同的標記法,因此您可以重複使用值。

`circle()` 形狀 + clip-path 的插圖

本文中的插圖使用剪輯功能來突顯形狀,協助您瞭解效果。

回到圓形。

如果使用百分比表示圓形半徑,系統會使用稍微複雜的公式計算值:sqrt(width^2 + height^2) / sqrt(2)。瞭解這一點很有幫助,因為如果元素尺寸不相等,您就能想像產生的圓形形狀。

所有 CSS 單位類型都可用於形狀函式座標,例如 px、em、rem、vw、vh 等。您可以根據需求,選擇彈性或剛性足夠的材質。

您可以為圓心座標設定明確的值,藉此調整圓形的位置。

.element{
  shape-outside: circle(50% at 0 0);
}

這會將圓形中心置於座標系統的原點。什麼是座標系統?這時參考方塊就派上用場。

CSS 形狀的參考方塊

參考方塊是元素周圍的虛擬方塊,可建立用於繪製及定位形狀的座標系統。座標系統的原點位於左上角,X 軸指向右側,Y 軸指向下方。

CSS 形狀的座標系統

請注意,shape-outside 會改變內容圍繞的浮動區域形狀。浮動區域會延伸到 margin 屬性定義的方塊外緣。這稱為 margin-box,如果未明確提及,則為形狀的預設參考框。

以下兩個 CSS 宣告會產生相同結果:

.element{
  shape-outside: circle(50% at 0 0);
  /* identical to: */
  shape-outside: circle(50% at 0 0) margin-box;
}

我們尚未設定元素的邊界。此時,可以安全地假設座標系統的原點和圓心位於元素內容區域的左上角。設定邊界後,情況會有所改變:

.element{
  shape-outside: circle(50% at 0 0) margin-box;
  margin: 100px;
}

座標系統的原點現在位於元素內容區域外 (向上 100 像素,向左 100 像素),圓心也是如此。圓形半徑的計算值也會增加,以因應 margin-box 參考框建立的座標系統表面增加。

有和沒有邊界的邊界方塊座標系統
有幾種參考方塊選項可供選擇:`margin-box`、`border-box`、`padding-box` 和 `content-box`。其名稱代表其邊界。我們之前已說明過 `margin-box`。`border-box` 會受到元素邊框的外緣限制,`padding-box` 會受到元素邊框間距的限制,而 `content-box` 則與元素內內容使用的實際表面區域相同。
所有參考方塊的插圖

shape-outside 宣告一次只能使用一個參考方塊。每個參照框都會以不同且有時難以察覺的方式影響形狀。另一篇文章則深入探討並協助您瞭解 CSS 形狀的參考方塊。

ellipse() 函式

ellipse() 形狀值的插圖

橢圓形看起來像壓扁的圓形。定義為 ellipse(rx ry at cx cy),其中 rx 和 ry 是橢圓在 X 軸和 Y 軸上的半徑,cx 和 cy 則是橢圓中心的座標。

.element{
  shape-outside: ellipse(150px 300px at 50% 50%);
  width: 300px;
  height: 600px;
}

系統會根據座標系統的維度計算百分比值。不必費心計算。您可以省略橢圓中心的座標,系統會從座標系統的中心推斷。

X 軸和 Y 軸的半徑也可以使用關鍵字定義:farthest-side 產生的半徑等於橢圓中心與參考方塊最遠側之間的距離,而 closest-side 則代表完全相反的意思 - 使用中心與一側之間的最短距離。

.element{
  shape-outside: ellipse(closest-side farthest-side at 50% 50%);
  /* identical to: */
  shape-outside: ellipse(150px 300px at 50% 50%);
  width: 300px;
  height: 600px;
}

如果元素尺寸 (或參照框) 可能會以無法預測的方式變更,但您希望橢圓形狀能配合調整,這個方法就很實用。

circle() 形狀函式中的半徑,也可以使用相同的 farthest-side 和 closest-side 關鍵字。

polygon() 函式

polygon() 形狀值的插圖

如果圓形和橢圓形太受限,多邊形形狀函式可提供多種選項。格式為 polygon(x1 y1, x2 y2, ...),您可以在其中指定多邊形各個頂點 (點) 的 x y 座標組合。指定多邊形時,至少需要三組經緯度,也就是三角形。

.element{
  shape-outside: polygon(0 0, 0 300px, 300px 600px);
  width: 300px;
  height: 600px;
}

頂點會放在座標系統上。如果是回應式多邊形,您可以為部分或所有座標使用百分比值。

.element{
  /* polygon responsive to font-size*/
  shape-outside: polygon(0 0, 0 100%, 100% 100%);
  width: 20em;
  height: 40em;
}

fill-rule 參數為選用,從 SVG 匯入,可指示瀏覽器在路徑自我交錯或形狀封閉時,如何判斷多邊形的「內部」。Joni Trythall 清楚說明瞭 SVG 中 fill-rule 屬性的運作方式。如未定義,fill-rule 預設為 nonzero。

.element{
  shape-outside: polygon(0 0, 0 100%, 100% 100%);
  /* identical to: */
  shape-outside: polygon(nonzero, 0 0, 0 100%, 100% 100%);
}

inset() 函式

inset() 形狀函式可建立矩形,並在周圍排列內容。這聽起來可能違反直覺,因為最初的前提是 CSS 形狀可以讓網路內容擺脫簡單的方塊。很有可能。我還沒找到 inset() 的用途,是無法透過浮動和邊界或 polygon() 實現的。不過,inset() 確實比 polygon() 更能清楚表達矩形形狀。

內凹形狀函式的完整標記法為 inset(top right bottom left border-radius)。前四個位置引數是從元素邊緣向內偏移。最後一個引數是矩形的邊框半徑。這是選用屬性,因此可以省略。這項屬性遵循您在 CSS 中使用的 border-radius 簡寫標記法。

.element{
  shape-outside: inset(100px 100px 100px 100px);
  /* yields a rectangular shape which is 100px inset on all sides */
  float: left;
}

從參考框建立形狀

如果未在 shape-outside 屬性指定形狀函式,可以允許瀏覽器從元素的參考方塊衍生形狀。預設參考框為 margin-box。到目前為止,這與浮點數的運作方式並無不同。但只要套用這項技術,就能重複使用元素的幾何圖形。我們來看看 border-radius 屬性。

如果使用這個屬性將浮動元素的邊角設為圓角,就會產生裁剪效果,但浮動區域仍會是矩形。新增 shape-outside: border-box,包住 border-radius 建立的輪廓。

使用 border-box 參考框,從元素的 border-radius 擷取形狀
.element{
  border-radius: 50%;
  shape-outside: border-box;
  float: left;
}

當然,你可以用這種方式使用所有參考方塊。衍生形狀還有另一個用途:偏移引文。

使用 content-box 參考框建立偏移的引言

只要使用 float 和 margin 屬性,就能達到偏移摘錄的效果。但這需要您在 HTML 樹狀結構中,將引用元素放在要顯示的位置。

以下說明如何達到相同的偏移醒目引文效果,並增加彈性:

.pull-quote{
  shape-outside: content-box;
  margin-top: 200px;
  float: left;
}

我們明確設定了形狀座標系統的 content-box 參考框。在這種情況下,醒目引文中的內容數量會定義外部內容環繞的形狀。這裡使用 margin-top 屬性來定位 (偏移) 摘錄,無論其在 HTML 樹狀結構中的位置為何。

形狀邊界

你會發現,如果讓內容圍繞形狀,內容可能會與元素太過貼近。您可以使用 shape-margin 屬性在形狀周圍加上間距。

.element{
  shape-outside: circle(40%);
  shape-margin: 1em;
  float: left;
}

效果與您使用一般 margin 屬性時類似,但 shape-margin 只會影響 shape-outside 值周圍的空間。只有在座標系統中有空間時,才會在形狀周圍加上間距。因此,在上述範例中,圓形半徑設為 40%,而非 50%。如果半徑設為 50%,圓形就會佔用座標系統中的所有空間,導致 shape-margin 無法產生效果。請注意,形狀最終會受限於元素的 margin-box (元素加上周圍的 margin)。如果形狀較大且溢出,系統會將其裁剪為 margin-box,最終會呈現矩形。

請注意,shape-margin 只接受單一正值。沒有長寫標記法。圓形的 shape-margin-top 到底是什麼?

建立形狀動畫

您可以將 CSS 形狀與許多其他 CSS 功能 (例如轉場和動畫) 結合使用。不過,我必須強調,使用者在閱讀時,如果文字版面配置發生變化,會感到非常困擾。如果決定使用動畫形狀,請密切注意使用者體驗。

只要 circle() 和 ellipse() 形狀的半徑和中心點是以瀏覽器可內插的值定義,您就可以為這些形狀製作動畫。可以從 circle(30%) 轉換為 circle(50%)。不過,在 circle(closest-side) 到 circle(farthest-side) 之間建立動畫會導致瀏覽器卡頓。

.element{
  shape-outside: circle(30%);
  transition: shape-outside 1s;
  float: left;
}

.element:hover{
  shape-outside: circle(50%);
}
GIF 動畫:圓圈

為 polygon() 形狀建立動畫時,可以達到更有趣的效果,但請注意,多邊形在兩個動畫狀態之間必須有相同數量的頂點。如果新增或移除頂點,瀏覽器就無法插補。

其中一個技巧是加入所需的最大頂點數量,並在動畫狀態中將頂點聚集在一起,讓形狀看起來有較少的邊。

.element{
  /* four vertices (looks like rectangle) */
  shape-outside: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  transition: shape-outside 1s;
}

.element:hover{
  /* four vertices, but second and third overlap (looks like triangle) */
  shape-outside: polygon(0 0, 100% 50%, 100% 50%, 0 100%);
}
三角形動畫 GIF

在形狀內自動換行

《愛麗絲夢遊仙境》試用版螢幕截圖,其中使用 CSS 形狀包裝內容

CSS Shapes 規格的初始草稿包含 shape-inside 屬性,可讓您在形狀內包裝內容。Chrome 和 Webkit 甚至曾實作過這項功能。不過,如要將任意位置的內容包裝在自訂路徑中,則需要投入更多心力和研究,才能涵蓋所有可能的情況並避免錯誤。因此,shape-inside 屬性已延後至 CSS Shapes Level 2,且已撤銷相關實作。

不過,只要稍加努力並做出一些妥協,您仍然可以達到在自訂形狀內包裝內容的效果。解決方法是使用兩個具有 shape-outside 的浮動元素,並將其放置在容器的兩側。折衷做法是使用一或兩個不具語意意義的空白元素,但做為支柱,在內部營造形狀的錯覺。

<div>
  <div class='left-shape'></div>
  <div class='right-shape'></div>

  Lorem ipsum...
</div>

.left-shape 和 .right-shape 支柱元素在容器頂端的位置很重要,因為它們會浮動到左側和右側,以便夾住內容。

.left-shape{
  shape-outside: polygon(0 0, ...);
  float: left;
  width: 50%;
  height: 100%;
}

.right-shape{
  shape-outside: polygon(50% 0, ...);
  float: right;
  width: 50%;
  height: 100%;
}
插圖:Alice 示範 shape-inside 的解決方法

這種樣式會讓兩個浮動支柱佔用元素內的所有空間,但 shape-outside 屬性會為其餘內容留出空間。

如果瀏覽器不支援 CSS 形狀,就會將所有內容向下推,造成不美觀的效果。因此,請務必以漸進增強的方式使用這項功能。

在先前的形狀動畫範例中,您會發現文字移動可能會造成困擾。並非所有用途都適合使用動畫形狀。不過,您可以為與 CSS 形狀互動的其他屬性加上動畫效果,在適當的地方增添效果。

在 CSS Shapes 的愛麗絲夢遊仙境示範中,我們使用捲動位置來變更內容的上邊界。文字擠在兩個浮動元素之間。向下移動時,必須根據兩個浮動元素的 shape-outside 重新版面配置。這會給人一種文字掉進兔子洞的印象,並增添故事敘述體驗。瀕臨不當內容?不一定。但看起來很酷。

由於文字版面配置是由瀏覽器原生完成,因此效能比使用 JavaScript 解決方案更好。不過,在捲動時變更 margin-top 會觸發許多重新版面配置和繪製事件,可能會明顯降低效能。務必謹慎使用!不過,如果使用 CSS 形狀但不加上動畫,就不會對效能造成明顯影響。

漸進增強

一開始先假設瀏覽器不支援 CSS Shapes,並在偵測到這項功能時,再以此為基礎建構。Modernizr 是偵測功能的好方法,且「非核心偵測」部分有 CSS Shapes 的測試。

部分瀏覽器會透過 @supports 規則在 CSS 中提供功能偵測,不需要外部程式庫。Google Chrome 也支援 CSS Shapes,並能解讀 @supports 規則。以下說明如何使用這項功能漸進增強:

.element{
  /* styles for all browsers */
}

@supports (shape-outside: circle(50%)){
  /* styles only for browsers which support CSS Shapes */
  .element{
    shape-outside: circle(50%);
  }
}

Lea Verou 撰寫了更多關於如何使用 CSS @supports 規則的文章。

CSS 排除條件的消歧義

我們現在所知的 CSS 形狀,在規格早期稱為 CSS 排除和形狀。命名方式的切換可能看似細微,但實際上非常重要。CSS 排除現在是獨立規格,可讓內容圍繞任意位置的元素,不需使用浮動屬性。想像一下,將內容包覆在絕對定位的元素周圍,這就是 CSS Exclusions 的用途。CSS 形狀只會定義內容環繞的路徑。

因此,形狀和排除項目並不相同,但兩者相輔相成。目前瀏覽器已支援 CSS 形狀,但 CSS 排除項目尚未與形狀互動功能整合。

使用 CSS 形狀的工具

您可以在傳統圖像製作工具中建立路徑,但截至本文撰寫時,這些工具都無法匯出 CSS Shapes 值所需的語法。即使可以,這種工作方式也不太實際。

CSS 形狀適用於瀏覽器,會與頁面上的其他元素互動。這項功能非常實用,可讓您預覽編輯形狀對周圍內容的影響。以下幾項工具可協助您完成這項工作流程:

Brackets:Brackets 的 CSS Shapes Editor 擴充功能會使用程式碼編輯器的即時預覽模式,疊加互動式編輯器,用於編輯形狀值。

Google Chrome:Google Chrome 的 CSS Shapes Editor 擴充功能可擴充瀏覽器的開發人員工具,提供建立及編輯形狀的控制項。在所選元素上方放置互動式編輯器。

Google Chrome 的檢查器內建醒目顯示形狀的功能。將滑鼠游標懸停在具有 shape-outside 屬性的元素上,該元素就會亮起,顯示形狀。

圖片形狀:如果您想生成圖片,並讓瀏覽器從中擷取形狀,Rebecca Hauck 撰寫了一篇實用的 Photoshop 教學課程。

Polyfill:Google Chrome 是第一個推出 CSS Shapes 的主要瀏覽器。我們即將支援 Apple iOS 8 和 Safari 8 的這項功能。其他瀏覽器供應商日後可能會考慮採用。在此之前,您可以使用 CSS Shapes polyfill 取得基本支援。

結論

在網頁上,內容大多侷限在簡單的方塊中,而 CSS 形狀可讓您建立富有表現力的版面配置,縮小網頁和印刷設計之間的擬真度差距。當然,形狀也可能遭到濫用,造成干擾。不過,只要運用得當,形狀就能提升內容呈現效果,並以獨特的方式吸引使用者注意。

最後,我提供了一份作品集,其中大部分是印刷品,展示了非矩形版面配置的有趣用途。希望這能激發你嘗試 CSS Shapes,並實驗新的設計構想。

非常感謝 Pearl Chen、Alan Stearns 和 Zoltan Horvath 審閱本文並提供寶貴見解。