路徑、形狀、裁剪和遮罩

HTML 算繪是建構在方塊模型之上,但生活 (和網頁設計) 不只有矩形。CSS 支援多種方式來變更元素的算繪區域,開發人員可自由建立支援所有形狀和大小的設計。剪裁可使用幾何形狀,遮罩則會影響像素層級的可視度。

路徑和形狀

CSS 會使用函式定義形狀。我們在 CSS 函式模組中介紹了函式的一般資訊。本節將說明如何使用 CSS 建立形狀。下列所有範例都使用您透過 clip-path 屬性建立的形狀,將可見區域縮減為形狀內的部分。這樣一來,元素在視覺上就會與元素方塊有所不同。我們稍後會詳細說明剪輯功能。

CSS 中定義的形狀可以是基本形狀 (例如圓形、矩形和多邊形),也可以是路徑 (可定義複雜和複合形狀)。

基本形狀

circle()ellipse()

circle()ellipse() 函式會定義圓形和橢圓形,半徑與元素相關。circle() 函式接受單一大小或百分比做為引數。根據預設,這兩個函式都會將形狀放置在元素中心。兩者都接受 at 關鍵字後方的選用位置,可以長度、百分比或位置關鍵字表示。

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: circle(50%);
}

如果 circle() 函式的引數為 50%,系統就會算繪出正圓。

上例顯示使用 circle() 函式的圓形剪輯路徑。請注意,50% 的半徑會建立元素全寬的圓形。ellipse() 函式會接受兩個引數,分別代表形狀的水平和垂直半徑。

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: ellipse(50% 25%);
}

ellipse() 函式會透過百分比引數產生橢圓。如果引數為 50% 和 25%,產生的橢圓會沿著 X 軸延伸,半徑是 Y 軸的兩倍。

上例顯示使用 ellipse() 函式的橢圓形剪輯路徑。請注意,50% 的半徑會建立元素全寬的橢圓。以下範例顯示的橢圓中心位於元素頂端。

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: ellipse(50% 25% at center top);
}

rect()inset()

rect()inset() 函式提供不同的方式來定義矩形,方法是設定矩形邊相對於元素邊的位置。您可以建立與元素預設方塊在視覺上不同的矩形。這些函式可選擇性接受 round 關鍵字,使用與 border-radius 簡寫屬性相同的語法,建立圓角矩形。

rect() 函式會定義矩形頂端和底部的相對位置 (相對於元素頂端),以及左右兩側的相對位置 (相對於元素左側)。這個函式會接受四個大小或百分比單位做為引數,分別定義頂端、右側、底部和左側。如果希望矩形在元素大小變更時不會縮放,或是希望矩形在元素變更時維持相同比例,可以選擇 rect() 函式。

.my-element {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: rect(15px 75px 45px 10px);
}

rect() 函式接受四個引數,用於定義矩形大小。在本例中,引數為 15px、75px、45px 和 10px。

上例顯示使用 rect() 函式定義的矩形剪輯路徑。如圖所示,這些維度是以元素的頂端和左側邊緣為基準。

inset() 函式會根據元素各邊的內縮距離,定義矩形各邊的位置。這個函式接受一到四個大小或百分比單位做為引數,可讓您一次定義多個邊。如要建立隨元素縮放的矩形,或與元素邊緣保持固定距離的矩形,可以選擇 inset() 函式。

.my-element {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px);
}

inset() 函式可從元素的內建大小中減去值。這個圖表中的函式引數為 15px、5px、15px 和 10px。

上例顯示使用 inset() 函式定義的矩形剪輯路徑。尺寸是相對於元素兩側。

rect()inset() 函式可選擇接受 round 關鍵字,使用與 border-radius 簡寫屬性相同的語法,建立圓角矩形。以下範例顯示先前矩形的圓角版本。

.rounded-rect {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px round 5px);
}

.rounded-inset {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px round 5px);
}

polygon()

如果是三角形、五邊形、星形等其他形狀,您可以使用 polygon() 函式,以直線連接多個點來建立形狀。polygon() 函式接受由兩個長度或百分比單位組成的配對清單。每個配對都描述多邊形上的一個點:第一個值是與元素左側邊緣的距離,第二個值是與元素頂端邊緣的距離。您不必封閉多邊形,因為系統會將最後一個點與第一個點連線,完成多邊形。

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: polygon(
    50% 0,
    0 100%,
    100% 100%
  );
}

polygon() 函式接受數量可變的引數,以便繪製複雜形狀。在本例中,引數的設計方式是建立三角形。

上例會定義三個點,建立三角形裁剪路徑。

根據預設,polygon() 函式會將重疊區域算為已填入。您可以透過選用的第一個引數 (稱為填滿規則) 變更這項行為。如要在填滿和未填滿的區域之間交替,請將填滿規則設為 evenodd。如要使用預設填滿規則,請將其設為 nonzero

上例顯示如何使用 polygon() 函式和三角函數,建立正多邊形和星形。這不會建立可放入元素內或置中的最大可能正多邊形,我們將這項練習留給您嘗試。本例中的星形也示範了 nonzeroevenodd 填滿規則。

複雜形狀

如果基本形狀函式不足以描述複雜形狀,CSS 會提供使用更精細語法的函式,描述曲線和線條等特徵。這些函式也適用於複合形狀 (由多個形狀組成的形狀,例如有洞的圓形)。

path()

path() 函式會接受 SVG 路徑語法字串,用來描述形狀。這樣一來,您就能使用描述構成形狀的線條和曲線的指令,建立複雜形狀。直接編輯 SVG 語法可能很複雜,因此建議您考慮使用專屬的視覺化編輯器,在透過 path() 函式建立形狀時匯出語法。

path() 函式不會使用 CSS 大小單位,所有值都會解讀為像素。也就是說,使用路徑函式建立的形狀不會根據元素或容器大小調整。建議只將 path() 用於尺寸固定的形狀。

shape()

shape() 函式會使用指令語法描述形狀,與 path() 函式類似。不過,shape() 函式指令是原生 CSS,可以使用 CSS 大小單位。這樣一來,使用 shape() 函式定義的形狀就能以回應式方式調整大小。

上例使用 path()shape() 函式定義愛心形狀和中央有洞的圓形。這個範例在兩個函式中都使用相同的像素值,但 shape() 函式也可以使用其他 CSS 大小單位,例如百分比或容器相對單位。

剪輯

剪裁會定義元素的哪些區域可見,類似於從雜誌剪裁圖片。clip-path 屬性會設定用於定義裁剪區域的路徑。

如先前章節的範例所示,任何基本形狀或路徑函式都可以做為 clip-pathclip-path 屬性也支援在 SVG clipPath 元素中定義的路徑,這些路徑可以內嵌或位於獨立檔案中。

剪裁對圖片的影響 (以圖片為例):在這張圖片中,小貓的相片同時剪裁成圓形,以及完整勾勒出小貓輪廓的複雜剪裁路徑。

上圖顯示在圖片元素中加入 clip-path 後,圖片的可見區域會如何變化。上方剪輯路徑使用 circle() 函式,下方則使用 SVG clipPath。請注意,使用 circle() 函式建立的圓圈預設會置中於元素。

clip-path 屬性僅接受單一路徑。如要使用多個不重疊的形狀裁剪元素,請使用 path()shape() 函式定義複合路徑,或使用 SVG clipPath。在複雜情境中,您也可以使用遮罩而非剪輯,我們會在後續章節中說明。

使用形狀裁剪

如要使用基本形狀或路徑函式裁剪,請將 clip-path 屬性設為函式傳回的值,如上述範例所示。每個函式都會以不同方式相對於元素放置剪裁形狀,因此請參閱每個函式的參照。

在上例中,兩個元素都使用 .clipped 類別套用圓形 clip-path。請注意,clip-path 是相對於每個元素定位,且 clip-path 內的文字不會重新排版來配合形狀。

裁剪路徑的參照方塊

根據預設,元素的裁剪路徑會包含元素的邊框。使用其中一個基本形狀函式時,您可以將剪輯路徑的參照方塊設為只包含邊框內的元素區域。參考方塊的有效值為 stroke-box (預設值) 和 fill-box (僅包含邊界內的區域)。

上例顯示具有大型 (20px 邊框) 的元素,每個元素都使用 inset() 函式設定 clip-path。相對於元素邊界裁剪的元素仍會顯示部分邊界。相對於邊界內區域裁剪的元素不會顯示任何邊界,而且即使使用相同的插邊值,也會較小。

使用圖形剪裁

剪裁路徑可以在 SVG 文件中定義,並嵌入 HTML 文件或從外部參照。這項功能可用於定義在繪圖程式中建立的複雜剪裁路徑,或合併多個形狀的剪裁路徑。

<img id="kitten" src="kitten.png">

<svg>
  <defs>
    <clipPath id="kitten-clip-shape">
      <circle cx="130" cy="175" r="100" />
    </clipPath>
  </defs>
</svg>

<style>
  #kitten {
    clip-path: url(#kitten-clip-shape);
  }
</style>

在上述範例中,具有 kitten-clip-shape idclipPath 會套用至 <img> 元素。在本例中,可擴充向量圖形文件會嵌入 HTML。如果 SVG 文件是名為 kitten-clipper.svg 的外部檔案,則 clipPath 會改為參照 url(kitten-clipper.svg#kitten-clip-shape)

遮罩

遮罩是另一種方法,可定義要顯示或隱藏元素的哪些區域。裁剪是使用基本形狀或路徑,而遮罩則是使用圖片或漸層的像素來決定可見度。與剪裁不同,遮罩可讓元素區域呈現半透明狀態。您可以對元素套用多張遮罩圖片,產生各種效果。

如要套用遮罩,請設定 mask-image 屬性。這個屬性可接受一或多張圖片、漸層,或 SVG 文件中 <mask> 元素的參照。如要套用多個遮罩圖片,請以半形逗號分隔。

.my-element {
  mask-image: url(my-mask.png),
              linear-gradient(black 0%, transparent 100%);
}

在上述範例中,.my-element 會使用 PNG 圖片遮蓋,然後套用線性漸層。系統預設會將多個遮罩加在一起,建立最終遮罩。

上例顯示套用一或多個遮罩的圖片。切換各個遮罩,即可查看遮罩如何加總,產生最終效果。

Alpha 遮罩與亮度遮罩

你可以使用圖片的 alphaluminance 套用遮罩。如果根據 alpha 遮蓋,系統會將遮罩圖片中每個像素的透明度套用至元素,並忽略該像素的任何色彩資訊。根據 luminance 遮罩時,系統會將每個像素的透明度和值 (亮度或暗度) 套用至元素。亮度遮罩會將較亮的顏色視為可見,較深的顏色視為不可見。

如要設定遮罩模式,請使用 mask-mode 屬性。根據預設,mask-mode 屬性會設為 match-source,並根據遮罩圖片類型設定模式。如果是圖片和漸層,這項設定預設為 alpha。如果是 SVG 遮罩,這項屬性會預設為 <mask> 元素的 mask-type 屬性值,如果沒有定義 mask-type,則會預設為 luminance

在上例中,顯示不同顏色和 Alpha 值的測試模式會做為遮罩。切換 mask-mode,即可查看 alpha 模式的透明度,以及 luminance 模式的色彩亮度和透明度。

其他遮蓋屬性

CSS 提供額外屬性,可微調遮罩的行為。每個屬性都接受以半形逗號分隔的值清單,這些值會與 mask-image 屬性設定的遮罩清單相符。如果值少於遮罩,系統會重複清單,直到為每個遮罩設定值為止。如果值的數量多於遮罩,系統會捨棄多餘的值。

屬性 說明
mask-clip

設定要套用元素遮罩的參照方塊。預設值為 border-box.

mask-composite

設定將多個遮罩套用至同一元素時,遮罩之間的互動。預設值為 add

mask-origin

設定做為遮罩來源的參照方塊。預設為 border-box。這項功能與 background-origin 類似,且接受相同的關鍵字。

mask-position

設定遮罩相對於 mask-origin 的位置。接受位置關鍵字值,例如 topcenter、百分比、大小單位,或與位置關鍵字相關的值。這與 background-position 的行為類似,且接受相同的引數類型。

mask-repeat

如果遮罩元素大於遮罩,設定遮罩的重複方式。預設為 repeat。這與 background-repeat 的行為類似,且接受相同的引數類型。

mask-size

設定遮罩相對於遮罩元素大小的縮放方式。預設為 auto。這與 background-size 的行為類似,且接受相同的引數類型。

遮罩速記

您可以使用遮罩簡寫,一次設定多個遮罩屬性。將每個遮罩的所有屬性分組在一起,即可簡化多個遮罩的設定。遮罩簡寫相當於依序設定下列屬性:mask-imagemask-modemask-positionmask-sizemask-repeatmask-originmask-clipmask-composite。不一定要納入所有屬性,未納入的屬性會重設為初始值。每個遮罩最多可支援八個屬性,因此建議您備妥完整參考資料

.longhand {
  mask-image: linear-gradient(white, black),
              linear-gradient(90deg, black, transparent);
  mask-mode: luminance, alpha;
  mask-position: bottom left, top right;
  mask-size: 50% 50%, 30% 30%;
}

.shorthand {
  mask: linear-gradient(white, black) luminance bottom left / 50% 50%,
        linear-gradient(90deg, black, transparent) alpha top right / 30% 30%;
}

在上述範例中,每個類別都套用了兩個遮罩。第一個範例使用個別屬性,第二個範例則使用 mask 簡寫。這兩種樣式彼此等效。

浮動元素周圍的文字重排

裁剪或遮罩元素時,您只會變更方塊內的顯示區域,方塊本身則維持不變。也就是說,浮動元素會根據原始的周框影響文件流程,而不是元素的顯示部分。如要定義元素周圍的流動方式,請使用 shape-outside 屬性和裁剪路徑。

shape-outside 屬性會定義內容在元素周圍流動的形狀。這個形狀可以是任何基本形狀函式,但不能是使用 path()shape() 函式定義的形狀,也不能是 SVG 文件中定義的 clipPath

shape-outside 屬性也接受圖片或漸層。與遮罩一樣,形狀的邊界取決於圖片或漸層的透明度。shape-image-threshold 屬性會設定形狀內要考量的透明度等級。

動畫中的形狀

建立 clip-path 動畫

您可以為 clip-path 屬性加入動畫效果,在形狀之間混合。您必須為每個關鍵影格使用相同的形狀函式,才能製作流暢的動畫。使用 polygon()shape() 函式時,每個關鍵影格必須使用相同數量的點。

在上一個範例中,元素的 clip-path 會在五邊形和星形之間轉換,這些形狀是使用 polygon() 函式定義。這個範例使用 evenodd 填滿規則,說明動畫點如何建立重疊區域。

使用 offset-path 製作動畫

您也可以沿著使用這些形狀函式建立的路徑,以動畫呈現元素。offset-path 屬性會設定要用做路徑的形狀,而 offset-distance 則會設定該路徑上的位置。您也可以搭配 offset-path 屬性使用 ray() 函式,沿直線製作動畫。

上例示範如何對 clip-pathoffset-path 使用相同的多邊形。動畫會使用 offset-distance,沿著大星星使用的相同多邊形移動較小的星星,做為 clip-path

隨堂測驗

下列何者是有效的形狀函式?

circle()
答對了!
square()
答錯了。
hexagon()
答錯了。
polygon()
答對了!
rectangle()
答錯了。
inset()
答對了!

是非題:使用 path() 函式定義的形狀可以使用百分比定義

答錯了。
答對了!

是非題:設定元素的剪裁路徑不會改變元素周圍的文字流程

答對了!
答錯了。

下列何者可做為剪裁路徑?

基本形狀
答對了!
SVG clipMask 元素
答對了!
點陣圖
答錯了。
漸層
答錯了。

下列何者可用做遮罩?

點陣圖
答對了!
漸層
答對了!
SVG 遮罩元素
答對了!
基本形狀,例如 circle()rect()
答錯了。