開始使用 CSS 形狀

圍繞自訂路徑來納入內容

Razvan Caliman
Razvan Caliman

長期以來,網頁設計師都必須在矩形的限制範圍內進行設計。由於大多數創意嘗試採用非矩形版面配置的結果都令人失望,因此網站上的大部分內容仍侷限於簡單的方塊。不過,這項情況即將改變,因為 Chrome 37 以上版本將推出 CSS 形狀功能。CSS Shapes 可讓網頁設計人員在自訂路徑 (例如圓形、刪節號和多邊形) 周圍環繞內容,因此不再受限於矩形的限制。

您可以手動定義形狀,也可以從圖片推斷出形狀。

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

想像一下,你像我一樣,第一次在圖片上漂浮,並預期內容可以包裝及填補空隙,只因為在元素周圍的長方形環繞形狀感到失望。您可以使用 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 值的另一側有浮動區域。舉例來說,如果含有咖啡杯圖片的元素浮動至左側,浮動區域就會建立在咖啡杯的右側。雖然您可以設計出兩側都有空白的圖片,但內容只會在浮動屬性指定的對角形狀 (左或右) 上左右對齊,不會同時在兩側對齊。

日後,您可以在不浮動元素上使用 shape-outside,這項功能是透過 CSS 排除項目引入。

手動建立形狀

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

circle() 函式

circle() 形狀值的插圖

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

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

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

在繼續閱讀之前,請先注意一點:CSS 形狀只會影響元素周圍浮動區域的形狀。如果元素有背景,則不會遭到形狀裁剪。如要達到這種效果,您必須使用 CSS 遮罩的屬性,也就是 clip-pathmask-imageclip-path 屬性非常實用,因為它遵循 CSS 形狀的符號,因此您可以重複使用值。

`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 px,左方 100 像素),以及圓形中心之外。隨著 margin-box 參考方塊建立的座標系統表面增加,圓形半徑的計算值也會隨之增加。

有邊框和無邊框的 Margin-box 座標系
您可以選擇幾個參考框選項:`margin-box`、`border-box`、`padding-box` 和 `content-box`。這些名稱暗示了各自的邊界。我們先前已說明 `margin-box`。`border-box` 受元素邊框外緣的限制,`padding-box` 受元素邊框間距的限制,而 `content-box` 則與元素內內容使用的實際表面面積相同。
插圖:所有參考方塊

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

ellipse() 函式

橢圓形() 形狀值的插圖

刪節號看起來很像擠壓的圓圈。這些屬性定義為 ellipse(rx ry at cx cy),其中 rxry 是 X 軸和 Y 軸上橢圓形的半徑,而 cxcy 則是橢圓形中心的座標。

.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-sideclosest-side 關鍵字。

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;
}

從 SVG 匯入的選用 fill-rule 參數會指示瀏覽器如何在遇到自相交路徑或封閉形狀時,判斷多邊形的「內部性」。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 Shapes 可讓網站內容免於簡單方塊的限制,這項做法可能違反直覺。那好吧。我還沒有發現 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;
}

當然,您也可以以這種方式使用所有參考資料方塊。以下是衍生形狀的另一種用途:偏移的引文。

使用內容方塊參考方塊建立偏移提取引述

只要使用浮動和邊界屬性,就能達到偏移的引文效果。但這需要您在 HTML 樹狀結構中將引號元素置於要算繪的點。

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

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

我們明確設定形狀座標系統的 content-box 參考框。在這種情況下,引文中內容的數量會決定外部內容的換行形狀。無論在 HTML 樹狀結構中的哪個位置,這裡都會使用 margin-top 屬性來定位 (偏移) 引文。

形狀邊界

您會發現將內容繞著形狀時,可能會使得圖片與元素重疊。您可以使用 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 只接受單一正值。沒有長寫法。總之,圓形的形狀邊界是什麼?

為形狀製作動畫

您可以將 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 形狀規格的初始草稿包含 shape-inside 屬性,讓您可以將內容納入形狀內部。甚至在 Chrome 和 WebKit 中實作了一段時間。不過,要包裝任意位置的內容到自訂路徑中,需要花費更多心力和研究,才能涵蓋所有可能情境並避免錯誤。因此 shape-inside 屬性已延後至 CSS 形狀層級 2,且已撤銷該屬性的導入方式。

不過,只要稍微調整一下,您還是可以讓內容在自訂形狀中換行。這個技巧是使用兩個浮動元素搭配 shape-outside,並將其放在容器的兩側。折衷之道是,您必須使用一或兩個沒有語意意義的空元素,但可做為支柱,營造出內部有形狀的錯覺。

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

  Lorem ipsum...
</div>

.left-shape.right-shape strut 元素在容器頂部的位置非常重要,因為這類元素會漂浮在左右兩側,以利填滿內容。

.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-outside 屬性會為其餘內容劃出空間。

如果瀏覽器不支援 CSS 形狀,則會將所有內容向下推,產生難看的效果。因此,請務必逐步加強使用這項功能。

前面的形狀動畫範例會發現,文字在移動時移動可能很麻煩。並非所有用途都需要使用動畫形狀。不過,您可以為與 CSS 形狀互動的其他屬性設定動畫,在適當位置加入效果。

在「Alice in Wonderland」示範的 CSS Shapes 中,我們使用了捲動位置來變更內容的頂端邊界。文字會擠在兩個浮動元素之間。往下移動時,必須根據兩個浮動元素的 shape-outside 來重新版面配置。這樣能讓觀眾感受到文字落入兔子洞,故事的情節也更豐富。無謂的邊界?不一定。但看起來很酷。

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

漸進增強

一開始,請假設瀏覽器不支援 CSS 形狀,然後在偵測到這項功能時,再根據該功能進行建構。Modernizr 是功能偵測的好解決方案,「非核心偵測」一節中已針對 CSS 形狀進行測試。

部分瀏覽器會透過 @supports 規則在 CSS 中提供功能偵測功能,不需要外部程式庫。Google Chrome 也支援 CSS 形狀,並可解讀 @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 排除條件的用途。CSS Shape 只會定義內容包裝的路徑。

因此,形狀和排除項目其實不同,但能相輔相成。瀏覽器現已推出 CSS 形狀,但 CSS 排除條件尚未導入形狀互動功能。

使用 CSS 形狀的工具

您可以在傳統圖片編寫工具中建立路徑,但這些工具皆無法建立路徑,因此在本文撰寫期間,系統會匯出 CSS Shapes 值的必要語法。即使他們這麼做,這樣的工作也不實用。

CSS 形狀應在瀏覽器中使用,以便對網頁上的其他元素做出反應。這項功能可讓你以視覺化方式,瞭解編輯形狀對周圍內容的影響。您可以使用以下幾個工具完成這項工作流程:

BracketsBrackets 專用的 CSS 形狀編輯器擴充功能會使用程式碼編輯器的即時預覽模式,重疊互動式編輯器,以便編輯形狀值。

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

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

使用圖片中的形狀:如果您偏好產生圖片,並讓瀏覽器從圖片中擷取形狀,Rebecca Hauck 撰寫了實用的 Photoshop 教學課程

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

結論

在網頁中,內容多半會侷限在簡單的方塊中,CSS Shapes 可讓您以生動的方式建立版面配置,彌補網頁和平面設計之間的忠實度差距。當然,形狀也可能遭到濫用,造成干擾。不過,如果運用得當,形狀就能提升內容呈現效果,並以使用者獨特的方式吸引他們的注意力。

我會留給他人的一系列作品,其中大多為紙本內容,示範非矩形版面配置的有趣用途。希望以上說明可鼓勵您試用 CSS Shapes 並嘗試新的設計構想。

許多感謝 Pearl Chen、Alan Stearns 和 Zoltan Horvath 看了這篇文章,並提供了寶貴的見解。