CSS 裁剪簡介
CSS 裁剪可以讓你定義元素的可見部分,同時隱藏其他部分,從而有效地將元素內容“裁剪”成特定的形狀或區域。透過裁剪,元素不再侷限於渲染成矩形,可以設計出更具視覺吸引力的樣式。本指南將探討 clip-path 屬性以及一些示例。
CSS 裁剪
裁剪是一種 CSS 技術,用於剪下(隱藏)元素的部分割槽域,僅顯示位於開發者定義的路徑內的元素區域。剪下區域由向量路徑建立;路徑內的任何內容都可見,而路徑外的內容則被隱藏。
clip-path 屬性
clip-path 屬性用於應用裁剪。它接受的值是一個向量路徑,定義了元素中應該保持可見的區域。該路徑可以使用盒子(box)、對 SVG <clipPath> 的引用或 CSS 形狀和路徑來定義。在下面的示例中,我們使用 polygon() 函式作為裁剪路徑,將一個藍色的方形 <div> 裁剪成一個菱形。
.diamond {
height: 200px;
width: 200px;
background-color: blue;
clip-path: polygon(0 50%, 50% 100%, 100% 50%, 50% 0);
}
透過 clip-path 屬性,你可以將元素裁剪為 <basic-shape> 或 SVG 源,從而建立複雜的形狀。如果宣告的各個狀態具有相同數量的向量點,你還可以對 clip-path 形狀進行動畫和過渡。
clip-path 屬性的值
要對元素進行視覺裁剪,clip-path 屬性可以設定為一個 <geometry-box>、一個指向 <clipPath> 裁剪源的 url,或一個用形狀函式建立的 <basic-shape>。
幾何盒子
clip-path 會隱藏裁剪區域之外的所有內容。最基本的裁剪是通過幾何盒子完成的。你可以根據元素的外邊距、邊框、內邊距或內容進行裁剪。這些視覺盒子值的效果可以透過其他 CSS 屬性實現,例如將 border-color 設定為透明,並將 background-origin 設定為所需的視覺盒子。我們主要關注這些值,是因為它們與我們稍後將要討論的形狀函式結合使用,以定義形狀裁剪路徑的原點。
在使用 clip-path 時,特別是與基本形狀一起使用時,理解 CSS 形狀所使用的參考盒非常重要,因為參考盒定義了形狀的座標系。
視覺盒子值
這個即時示例演示了 clip-path 屬性對一個元素的不同視覺盒子值的效果,並將其與 CSS background-origin 屬性進行比較。我們對 <blockquote> 應用了 border、background-color、background-image 和 padding。選擇一個單選按鈕來將 --value 更新為不同的 <geometry-box> 值,這將更新 background-origin 和 clip-path 的解析值。
blockquote {
width: 210px;
padding: 20px;
margin: 20px;
border: 20px dashed #dedede;
background-color: #ededed;
background-image: linear-gradient(rebeccapurple, magenta);
background-repeat: no-repeat;
}
.clippath {
background-origin: var(--value);
clip-path: var(--value);
}
.box-model {
background-origin: var(--value);
}
當 <geometry> 盒子與 <basic-shape> 結合指定時,該值定義了基本形狀的參考盒。如果單獨指定,它將使指定盒子的邊緣(包括任何圓角效果,如 border-radius)成為裁剪路徑。
形狀原點
前面的示例可能會讓你覺得 <geometry-box> 值沒什麼用,因為你可以用 background-origin 來代替。確實可以。但是,在使用基本形狀進行裁剪時,如果 <geometry-box> 與 <basic-shape> 一起作為 clip-path 的值,它就定義了該形狀的參考盒或原點。我們可以結合前面兩個示例來演示這一點。
blockquote {
width: 210px;
padding: 20px;
margin: 20px;
border: 20px dashed #dedede;
background-color: #ededed;
background-image: linear-gradient(rebeccapurple, magenta);
background-repeat: no-repeat;
background-origin: border-box;
clip-path: var(--value) polygon(0 50%, 50% 100%, 100% 50%, 50% 0);
}
另一個示例,請參閱 clip-path 形狀和幾何盒子。
即使像 clip-path: margin-box 這樣的值也很有用。除了透過將裁剪路徑的邊緣置於外邊距盒邊緣來創造視覺效果外,clip-path 的任何非 none 的計算值都會建立一個新的層疊上下文,就像 CSS opacity 屬性在值不為 1 時所做的那樣。
裁剪為基本形狀
clip-path 屬性對 <basic-shape> 值的支援提供了一種強大的元素塑形方式。各種形狀函式可以定義精確的裁剪區域,有效地將元素塑造成獨特的形態。基本形狀函式包括:
這些形狀的大小和位置由 <geometry-box> 值定義,如果 clip-path 值包含一個形狀但沒有 <geometry-box> 元件值,則預設使用 border-box 作為參考盒。
其中一些函式似乎只提供基本的預定義裁剪選項。它們可能看起來只是複製了你可以用 border-radius 建立的效果,但如果你在前面的示例中切換了 border-radius 屬性,你可能已經注意到了 CSS 裁剪的強大之處。形狀提供了更多的控制。例如,inset() 允許用精確的外邊距來裁剪一個矩形。而真正的強大和控制力來自於 path()、shape() 甚至 polygon(),它們允許建立自定義的多點形狀。
建立多邊形
使用 polygon(),透過定義代表形狀每個頂點的座標對,你可以建立複雜的形狀,如星星或抽象圖形。這些座標定義了由直線連線的向量點。
這裡我們使用 polygon() 函式來建立一個星星:
.star {
width: 200px;
height: 200px;
background: linear-gradient(rebeccapurple, magenta) blue;
clip-path: polygon(
50% 0%,
61% 35%,
100% 35%,
68% 57%,
79% 91%,
50% 70%,
21% 91%,
32% 57%,
0% 35%,
39% 35%,
50% 0%
);
}
Animation
透過為不同狀態宣告相同數量的向量點,可以對裁剪的形狀進行動畫和過渡。
@keyframes morphStar {
from {
clip-path: polygon(
50% 0%,
61% 35%,
100% 35%,
68% 57%,
79% 91%,
50% 70%,
21% 91%,
32% 57%,
0% 35%,
39% 35%,
50% 0%
);
}
to {
clip-path: polygon(
50% 10%,
65% 30%,
90% 20%,
75% 60%,
85% 95%,
50% 80%,
15% 95%,
25% 60%,
10% 20%,
35% 30%,
50% 10%
);
}
}
.star {
animation: morphStar alternate 3s infinite ease-in-out;
}
path() 函式
path() 函式可以使用 SVG 命令來繪製形狀。該函式接受與 SVG d 屬性等效的字串作為其引數。
前一個示例中的星星可以用 path() 來建立:
.star {
width: 200px;
height: 200px;
background: linear-gradient(rebeccapurple, magenta) blue;
clip-path: path(
"M100,0 L122,70 L200,70 L136,114 L158,182 L100,140 L42,182 L64,114 L0,70 L78,70 L100,0 Z"
);
}
曲線
使用 path(),我們不侷限於直線。在這個例子中,我們使用 path() 函式來建立一個心形:
.heart {
width: 200px;
height: 200px;
background: linear-gradient(rebeccapurple, magenta) blue;
clip-path: path(
"M20,70 A40,40,0,0,1,100,70 A40,40,0,0,1,180,70 Q180,130,100,190 Q20,130,20,70 Z"
);
}
將 SVG 作為源
除了將 SVG d 屬性字串作為 path() 函式的引數傳遞外,clip-path 屬性的值還可以直接引用 SVG <clipPath> 元素。
<div class="heart"></div>
<svg height="0" width="0">
<clipPath id="heart">
<path
d="M20,70 A40,40,0,0,1,100,70 A40,40,0,0,1,180,70 Q180,130,100,190 Q20,130,20,70 Z" />
</clipPath>
</svg>
<clipPath> 的 id 是 url() 函式的引數。
.heart {
width: 200px;
height: 200px;
background: linear-gradient(rebeccapurple, magenta) blue;
clip-path: url("#heart");
}
shape() 函式
SVG 路徑語法並不是最直觀的。因此,CSS 還提供了 shape() 函式。shape() 函式也接受路徑繪製指令,但其語法更易於人類閱讀。我們可以用更具宣告性的 CSS 來重現心形:
.heart {
clip-path: shape(
from 20px 70px,
arc to 100px 70px of 1% cw,
arc to 180px 70px of 1% cw,
curve to 100px 190px with 180px 130px,
curve to 20px 70px with 20px 130px
);
}
shape() 函式更為強大,因為它接受 CSS 的值和單位(path() 僅限於座標),包括使用像 calc() 這樣的 CSS 數學函式。透過使用變數,我們可以建立多種不同大小的形狀(和盒子):
:root {
--m: 10;
}
.heart {
width: calc(20px * var(--m));
height: calc(20px * var(--m));
display: inline-block;
background: linear-gradient(rebeccapurple, magenta) blue;
clip-path: border-box
shape(
from calc(2px * var(--m)) calc(7px * var(--m)),
arc to calc(10px * var(--m)) calc(7px * var(--m)) of 1% cw,
arc to calc(18px * var(--m)) calc(7px * var(--m)) of 1% cw,
curve to calc(10px * var(--m)) calc(19px * var(--m)) with
calc(18px * var(--m)) calc(13px * var(--m)),
curve to calc(2px * var(--m)) calc(7px * var(--m)) with
calc(2px * var(--m)) calc(13px * var(--m))
);
}
.small {
--m: 4;
}
.medium {
--m: 8;
}
.large {
--m: 12;
}
<div class="heart small"></div>
<div class="heart medium"></div>
<div class="heart large"></div>
讓文字環繞裁剪的形狀
被裁剪的元素仍然是矩形盒子。裁剪意味著你的元素看起來不像一個盒子,但它仍然是一個盒子。要讓內聯內容環繞你定義的非矩形(或矩形)形狀,請使用 shape-outside 屬性。預設情況下,內聯內容會環繞其外邊距盒;shape-outside 提供了一種自定義這種環繞的方式,使得文字可以圍繞你裁剪的元素,並遵循你複製的裁剪路徑,而不是元素的矩形盒子。
內容包括兩個要裁剪的元素,以及將圍繞它們進行塑形的文字。
<div class="leftTriangle"></div>
<div class="rightTriangle"></div>
<blockquote>
<q>
I've learned that people will forget what you said, people will forget what
you did, but people will never forget how you made them feel.</q
>
<cite>— Maya Angelou</cite>
</blockquote>
除了為 clip-shape 和 shape-outside 屬性應用相同的形狀外,被裁剪的元素還必須進行浮動,以便它與內容位於同一行。
.leftTriangle {
clip-path: polygon(0 0, 0 100%, 100% 0);
shape-outside: polygon(0 0, 0 100%, 100% 0);
float: left;
}
.rightTriangle {
clip-path: polygon(100% 0, 100% 100%, 0 100%);
shape-outside: polygon(100% 0, 100% 100%, 0 100%);
float: right;
}