【轉】CSS和SVG中的剪切——clip-path屬性和 元素


本文由大漠根據SaraSoueidan的《Clipping in CSS and SVG – The clip-path Property and <clipPath> Element》所譯,整個譯文帶有我們自己的理解與思想,如果譯得不好或有不對之處還請同行朋友指點。如需轉載此譯文,需注明原作者相關信息http://sarasoueidan.com/blog/css-svg-clipping/

——作者:SaraSoueidan

——譯者:大漠

CSS和SVG有很多共同之處。CSS有很多特性都是從SVG中引入過來的。其中就有“剪切”的特性。CSS和SVG都允許我們對一個元素進行非矩形的剪切。在這篇文章中,將介紹CSS和SVG中的剪切技術。

特別聲明:本文提供的DEMO可能在你的瀏覽器中不能正常的演示,你應該查看這個表格了解更多相關的信息。你在閱讀這篇文章之時沒有必要查看提供的DEMO。不是所有的剪切特性都可以實現或者說只能實現部分剪切特性。本文的最大目的是告訴你CSS和SVG中的剪切是如何工作?僅用來做為參考。在文章中的代碼不帶有任何瀏覽器的私有前綴,但在示例中還是帶了瀏覽器的私有前綴。

剪切是什么

剪切是一個圖形化操作,你可以部分或者完全隱藏一個元素。被剪切的元素可以是一個容器也可以是一個圖像元素。元素的哪些部分顯示或隱藏是由剪切的路徑來決定的。

CSS和SVG中的剪切

剪切路徑定義了一個區域,在這個區域內的內容將會顯示,而不在這個區域內的內容不會顯示。這個區域被稱之為“裁剪區域”。只要在這個區域之外的任何元素都不會顯示。包括元素的內容、背景、邊框、文本、輪廓等,甚至還包括他的子元素。

剪切的元素可以是任何容器和圖片元素。

剪切路徑的概念就相當於在元素上定義了一個視窗。它決定了元素哪些部分在這個“視窗”中顯示,哪些部分不在這個“視窗”中顯示。但他不會影響自身文檔流和其他文檔流,因為他通常還是以一個矩形區域顯示在其他文檔流前面,哪怕是剪切出來的區域是不規則的矩形。如果你想改變周圍內容元素圍繞剪切出來的圖形,那就需要使用CSS的圖形屬性。如果你對這方面知識感興趣,可以閱讀我早前寫的相關文章

有關於CSS3 Shapes相關中文教程,可以閱讀早前翻譯的兩篇文章:

CSS中的剪切——clip-path屬性

clip-path屬性是CSS Masking模塊的一部分。自從2000年以來,剪切都只是SVG中的一部分,現在將這個功能引入到CSS的Msking模塊中,所以現在可以對HTML元素和SVG元素進行剪切。

clip-path屬性是指定一個應用到元素上的剪切路徑。應用在SVG中<clipPath>元素上的屬性值可以完全運用在clip-path的屬性上。你還可以使用CSS Shapes模塊中的基本形狀來定義剪切路徑。這些形狀你可以使用形狀函數來創建。這些形狀態函數包括polygon()circle()inset()(用來定義嵌入的矩形)和ellipse()

使用clip-path屬性將一個剪切路徑運用在一個元素上非常的簡單:

/* SVG中的clipPath的使用 */ .element { clip-path: url(#svgClipPathID); } /* 使用CSS中的基本圖形函數 */ .element { clip-path: polygon(...); /* 或者其他的圖形函數 */ }

例如,我們使用polygon()函數定義一個多邊形的剪切路徑,並且把這個路徑應用到一個圖像上,代碼看起來像這樣:

img { clip-path: polygon(626px 463px,765px 236px,687px 31px,271px 100px,70px 10px,49px 250px,133px 406px,374px 462px,529px 393px); }

應用上面的代碼之后,圖像顯示成這樣:

CSS和SVG中的剪切

查看DEMO

基本圖形函數允許我們創建一定數量的圖形,其中最復雜的就是多邊形。如果你想創建一個更為復雜的圖形,而且圖形看起來不是用直線畫出來的,這個時候你就需要使用SVG的<clipPath>元素。正如<clipPath>元素名稱所暗示的一樣,你可以使用這個元素繪制任意路徑的圖形。這也意味着,你可以使用<clipPath>元素繪制出任意圖形來做為一個剪切路徑。

在我們第二個示例中,使用SVG的clipPath定義一個路徑,這個剪切路徑看起來像這樣:

<svg height="0" width="0"> <defs> <clipPath id="svgPath"> <path fill="#FFFFFF" stroke="#000000" stroke-width="1.5794" stroke-miterlimit="10" d="M215,100.3c97.8-32.6,90.5-71.9,336-77.6 c92.4-2.1,98.1,81.6,121.8,116.4c101.7,149.9,53.5,155.9,14.7,178c-96.4,54.9,5.4,269-257,115.1c-57-33.5-203,46.3-263.7,20.1 c-33.5-14.5-132.5-45.5-95-111.1C125.9,246.6,98.6,139.1,215,100.3z"/> </clipPath> </defs> </svg> 

這剪切路徑看起來就像一個黑色的描邊圈了一個不規則的圖形,這是一個簡單的剪切路徑,不帶有任何的填充。

CSS和SVG中的剪切

在下一部分中,我們將着重討論SVG的<clipPath>元素。但現在,我們來看看如何將定義好的路徑運用到圖片上:

img { clip-path: url(#svgPath); }

最終效果如下所示:

CSS和SVG中的剪切

查看DEMO

事實上,<clipPath>元素包括很多個基本圖形(<rect><circle>等),<path>元素,甚至是<text>元素。

如果在<clipPath>里面通過<text>指定文本,那么這個文本就會當成是一個剪切路徑,不管文是否可見,文本外的區域都將被剪切掉。

注意,你可以使用任何文本做為剪切路徑。這為實現很多效果開啟了一扇大門。你可以使用動畫圖片(比如,gif),甚至是視頻,然后選擇你需要的文本進行剪切。這里是沒有任何限制的。

下面的示例,就是使用文本做為剪切路徑:

<svg height="0" width="0"> <defs> <clipPath id="svgTextPath"> <text x="0" y="300" textLength="800px" lengthAdjust="spacing" font-family="Vollkorn" font-size="230px" font-weight="700" font-style="italic"> Blossom </text> </clipPath> </defs> </svg> 

SVG中的<text>最酷的特點就是可以使用自定義字體,就像HTML文本。在我們的示例中使用了Google Web Fonts中的Vollkorn字體。使用textLength屬性,將文本的寬度設置的和圖片寬度一樣, 並且通過xy來定位文本。注意,xy坐標確認了文本左下角的位置(也就是文本的基線baseline)。

使用上面的文本路徑剪切圖來的圖片效果如下:

CSS和SVG中的剪切

查看DEMO

正如我們前面提到的,你還可以在<clipPath>中使用多個圖形形狀。在下一節中,將會深入介紹<clipPath>,這里我們先簡單的了解。在這個示例中,我們使用了多個<circle>形狀,他們大小不同,位置不同。

<svg height="0" width="0"> <defs> <clipPath id="svgPath"> <circle stroke="#000000" stroke-miterlimit="10" cx="50" cy="50" r="40" /> <circle stroke="#000000" stroke-miterlimit="10" cx="193.949" cy="235" r="74.576"/> <circle stroke="#000000" stroke-miterlimit="10" cx="426.576" cy="108.305" r="47.034"/> <circle stroke="#000000" stroke-miterlimit="10" cx="346.915" cy="255.763" r="43.644"/> <circle stroke="#000000" stroke-miterlimit="10" cx="255.39" cy="82.882" r="35.17"/> <!-- more circles... --> </clipPath> </defs> </svg> 

此時,圖像只會在圓中顯示,圓外就不會顯示:

CSS和SVG中的剪切

查看DEMO

正如我們這篇文章中介紹的,你可以使用clip-path屬性應用在SVG元素繪制的路徑。在上面演示的示例,剪切路徑都是應用在HTML中的<img>元素上。在接下來的示例中,將演示的是一個剪切路徑用於<svg>的根元素。同樣是櫻花圖片,使用下面SVG的中的<image>來引用。

SVG中的<image>元素用來引用一個完整的SVG或像素圖像。如果你在<image>中引用的SVG圖像,設置的widthheight屬性,將會用來設置SVG視窗的大小。如果你引用的是像素圖像(我們這里的例子就是這樣做的),圖像將會自動縮放到指定的widthheight。所以我們要確認好他們的長寬比例,避免圖像扭曲。

當你創建一個SVG時,你可以在<svg>元素上指定其寬度和高度的大小,用於創建一個窗口。任何超過這個窗口的內容都將不會顯示出來。你可以通過<clipPath>元素定制一個窗口。

<svg height="500" width="800"> <image xlink:href="flowers.jpg" x="0" y="0" width="800" height="500"/> <defs> <clipPath id="theSVGPath"> <rect x="0" y="0" stroke="#000000" stroke-miterlimit="10" width="108" height="500"/> <rect x="121.5" y="25.5" stroke="#000000" stroke-miterlimit="10" width="55" height="455"/> <rect x="192.5" y="9.5" stroke="#000000" stroke-miterlimit="10" width="60" height="484"/> <rect x="271.5" y="44.5" stroke="#000000" stroke-miterlimit="10" width="63" height="416"/> <rect x="349.5" y="25.5" stroke="#000000" stroke-miterlimit="10" width="208" height="447"/> <rect x="574.5" y="44.5" stroke="#000000" stroke-miterlimit="10" width="60" height="446"/> <rect x="644.5" y="9.5" stroke="#000000" stroke-miterlimit="10" width="68" height="471"/> <rect x="736.5" y="18.5" stroke="#000000" stroke-miterlimit="10" width="49" height="462"/> </clipPath> </defs> </svg> 

使用clip-path將定義好的<clipPath>運用到<svg>元素上:

svg { clip-path: url(#theSVGPath); }

CSS和SVG中的剪切

查看DEMO

更多有關於在SVG上使用clip-path的示例將會在下面介紹<clipPath>部分介紹。

一個剪切路徑的參考盒子

除了剪切路徑本身,還可以給剪切路徑應用一個<basic-shape>定義一個剪切路徑的參考盒子。也就是說,可以使用一個剪切的基本函數創建一個剪切路徑。其中參考盒子只能使用CSS的clip-path來指定形狀路徑,而不能使用SVG的<clipPath>。對於SVG的<clipPath>參考盒子是一個border-box元素。

因此剪切路徑的參考盒子用<basic-shape>來指定。如果是一個HTML元素被剪切,可以使用四種盒模型:margin-boxborder-boxpadding-boxcontent-box。每種盒模型都有其自己的解釋。

如果<basic-path>制作的剪切路徑運用在一個SVG元素上,參考盒子可以設置為下面三種的其中一種:

  • fill-box使用對像的邊緣做為參考盒子
  • stroke-box使用路徑做為參考盒子
  • view-box如果沒有指定viewBox將使用最近的SVG視窗做為參考盒子。如果viewBox的確創建了,則會根據viewBox的原點坐標和維度來創建參考盒子

如果為SVG元素設置CSS盒模型中的任何一種做為參考盒子,則會使用fill-box值;如果你使用SVG來做為一個HTML元素的參考盒子,則會使用border-box盒模型。

.element { clip-path: polygon(...) padding-box; }

如果參考盒子沒有使用clip-path來指定——也就是沒有定義任何形狀——瀏覽器將會使用指定的盒子的邊緣,包括圓角圖形(比如說使用了border-radius)做為剪切路徑。

例如,使用下面的代碼片段,使用了border-radius指定了一個圓角的剪切路徑:

.el { clip-path: border-box; border-radius: 25%; }

注意,在寫這篇文章之時,使用border-box指定一個參考盒子,在webkit內核中還無法得以支持,因為它還沒有運用於實戰當中。

疊加情況、指針事件和動畫下的clip-path的注意事項

特別注意,要知道,如果任何都設置默認值,clip-path屬性將會創建一個類似於透明元素。

此外,根據Masking規范,鼠標事件在圖形的clipped-out區域外是無效的。這意味着,如果沒有做剪切,鼠標事件還是有效的。這一部規范中做了詳細的描述,只是實現方式不同。

CSS和SVG中的剪切

綠色區域表示可以響應鼠標事件。左圖表示的是標簽規范的行為,右圖表示非標簽規范行為。

為了演示,我在前面示例的基礎上,給圖片添加了一個div容器,使用了SVG的<clipPath>制作剪切路徑。如果沒有使用剪切,你可以看到圖像有邊框。添加一個hover效果,鼠標經過圖片時,圖片會帶有一定的透明度。

如果使用Chrome瀏覽器(35.0.1916.153版本測試),就算鼠標在圖像的剪切之外的區域,圖片也會有一琮的透明度。這種行為就是符合標准規范的一種行為。

在Firefox瀏覽器(30.0版本測試),除了在可視區域圖像不會響應鼠標事件。這意味着,鼠標移到圖像剪切區域之外,會失去鼠標事件。(圖像不帶有透明度)

我必須得說,我更喜歡符合規范規范的那種行為,不知道你喜歡的是哪種行為?

查看DEMO

剪切路徑還可以使用動畫效果。如果使用SVG的<clipPath>制作剪切路徑,可以在其內部設置動畫(下一節中將會詳細介紹)。如果剪切路徑是使用的基本圖形函數創建,則何以運用CSS3的animation或者transition屬性。至於如何使用基本圖形創建一個動畫的路徑,感興趣的可以閱讀我前面寫的一篇文章:《Animating CSS Shapes with CSS Animations & Transitions》。

SVG中的剪切——<clipPath>元素

在SVG中使用<clipPath>元素來定義剪切元素的剪切路徑。如果不想使用CSS的clip-path來定義元素的剪切路徑,可以使用SVG中的clip-path屬性。

你看過或讀過我寫的“Styling and Animating Scalable Vector Graphics with CSS”的幻燈片?如果沒有,你應該看看里面介紹的,如何使用SVG的屬性和CSS樣式來美化SVG元素。你可以點擊這里閱讀.

<svg> <defs> <clipPath id="myClippingPath"> <!-- ... --> </clipPath> </defs> <!-- the element you want to apply the clipPath to can be any SVG element --> <g id="my-graphic" clip-path="url(#myClippingPath)"> <!-- ... --> </g> </svg> 

<clipPath>的內容

我們前面提到過,可以在SVG中的<clipPath>內創建任意數量的基本形狀,<path><text>元素。它甚至還可以包括很多其他的東西,這正也是SVG很有意思的地方。

<clipPath>元素中的內容可以是描述性的(如<title><desc><metadata>)。也可以是圖形(如:<circle>,<ellipse><line><path><polygon><polyline><rect>或者<text>)。一個<clipPath>可以包含一個<use>元素或者<script>。注意,在<clipPath>元素中使用<use>元素,只能引用一些簡單的SVG的圖形(前面提到的),例如,它在<clipPath>內不能用於群體的參照,它是沒辦法正常工作的。

最后一部分,但並不是最重要的一部分。<clipPath>可以包括一個使用<animate><animateColor>,<animateMotion>,<animateTransform>或 <set>創建的動畫。這為很多創造打開了一扇門,只要你敢想,就能做。

使用多個<circle>制作的剪路徑,並且添加了一個簡單的動畫效果來做為示例演示。每個<circle>都有一個簡單的動畫。為了保證示例的簡單,在所有圓上都使用了同一個簡單的動畫效果。當然,你可以為每個圓創建不同的動畫效果。演示示例的代碼如下:

<svg height="0" width="0"> <defs> <clipPath id="svgPath"> <circle stroke="#000000" stroke-miterlimit="10" cx="50" cy="50" r="40"> <animate attributeName="r" attributeType="XML" from="0" to="250" begin="0s" dur="3s" fill="freeze" repeatCount="indefinite"/> </circle> <circle stroke="#000000" stroke-miterlimit="10" cx="193.949" cy="235" r="74.576"> <animate attributeName="r" attributeType="XML" from="0" to="250" begin="0s" dur="3s" fill="freeze" repeatCount="indefinite"/> </circle> <!-- ... --> </clipPath> </defs> </svg> 

這個動畫就只指定了每個圓的尺寸大小,圓的半徑從0到250px,總共花了3秒時間,並且設置了動畫播放次數是無限次。

點擊下面的按鈕查看示例,使用Chrome或者Safari點擊查看案例之前,得告訴您,示例還存在一個bug(詳細介紹請點擊這里),所以我建議您使用Firefox查看示例,直到這個Bug已修復。

查看DEMO

請注意,<clipPath>的內容也不能包括<g>。例如我們給多個圓<circle>放在一個組里<g>,那么它不能正常工作,剪切路徑不會運用到圖片上。

<svg height="0" width="0"> <defs> <clipPath id="svgPath"> <!-- WILL NOT WORK --> <g> <!-- WILL NOT WORK --> <circle stroke="#000000" stroke-miterlimit="10" cx="193.949" cy="235" r="74.576"/> <circle stroke="#000000" stroke-miterlimit="10" cx="426.576" cy="108.305" r="47.034"/> <!-- ... --> </g> </clipPath> </defs> </svg> 

clipPathUnits屬性

<clipPath>元素包括很多個屬性,比如id,class,transform和像fillstroke這樣的顯示屬性以及其他更多屬性。其中最有用的是clipPathUnits屬性。

clipPathUnits主要用來給<clipPath>元素內容指定一個坐標系統。它具有兩個值:objectBoundingBoxuserSpaceOnUse,其中userSpaceOnUse是默認值。

clipPathUnits = "userSpaceOnUse | objectBoundingBox"

userSpaceOnUse

clipPath元素是用來當作參考物時,clipPath元素內容是以用戶坐標系統作為參考點。(例如:clipPath元素的用戶坐標系統是通過clip-path屬性來引用)。

用戶坐標系統(局部坐標系統)是目前激活的坐標系統,主要用來如何定位坐標和長度。一個HTML元素的坐標和CSS的盒模型有關,但不同的是SVG元素沒有這樣的盒模型。

對於CSS盒子的布局,用戶的坐標原點就在盒子的左上角,而且一個單位就是一個像素,視窗也可以根據盒子的寬度按百分比計算。我想你對這方面應該非常的熟悉。如果你有一個<clipPath>元素包含了一個<circle>,而且這個<circle>的中心點在cx=100cy=100。那么這個中心點就是距盒子左邊100px和頂邊100px的交匯處。

如果元素是一個SVG元素,因此他是沒一個類似於CSS盒模型的東西,用戶的坐標原點是距<svg>元素視窗左上角最近的一個地方。一般情況之下,最近的視窗的建立,他的寬度和高度接近於<svg>的祖先元素。如果你不嵌套<svg>元素,它就是你創建的<svg>元素。

注意,SVG元素的坐標系統可以使用viewBox屬性進行修改,其他屬性可能有助於改變坐標系統。這一部分的內容超出了本文的內容范圍。所以在本文中,我假設viewBox沒有進行過任何的修改。因此瀏覽器使用的默認坐標系統原點是在<svg>元素的左上角,大小也等於<svg>元素。

objectBoundingBox

坐標系統的原點是在元素的邊框盒子的左上角頂點處,同樣適於剪切路徑。這個邊框是SVG元素對象的邊框(它只是包含了一個或多個幾何圖形形狀)和一個HTML元素設置border-box的盒模型是相關聯的。

這個值對SVG元素非常有用,因為它允許你應用的元素自身的邊界做為剪切路徑。下圖顯示一個圖像應用SVG的剪切路徑顯示的效果,他們分別使用了userSpaceOnUseobjectBoundingBox。灰色的邊框表示的是SVG元素創建的一個視窗。右圖中的圖像,我添加了一個灰色的邊框用來表示剪切后的圖像邊框。

CSS和SVG中的剪切

在左圖中,剪切路徑的坐系統定位在SVG的視窗上。當使用了objectBoundingBox屬性之后,圖像自身的邊框就會做為剪切路徑的坐標系統。

有一點需要特別的注意:當你設置了objectBoundingBox值后,<clipPath>元素中的內容必須在指定的坐標[0,1]內。坐標系統將成為一個單元系統,剪切出來的形狀都在這個clipPath分值內。

CSS和SVG中的剪切

例如,如果剪切路徑包含一個<circle>元素,而且他定位在圓的中心上:

<clipPath> <circle cx="350" cy="350" r="300" /> </clipPath> 

圓的位置(半徑)會用分數表示:

<clipPath clipPathUnits="objectBoundingBox"> <circle cx=".5" cy=".5" r=".45" /> </clipPath> 

在這種情況下,分數就像百分比。

<clipPath>筆記

<clipPath>元素不會直接在頁面上呈現,他唯一的作用就是可以通過clip-path來引用。display屬性不能運用於<clipPath>元素上,因此,就算display設置none外的其他值,<clipPath>元素也不會直接呈現。

還記得我前面提到HTML元素剪切后的鼠標事件嗎?相同的標准定義的行為卻不同。鼠標事件在SVG的剪切區域外是無效。規范后面提到,可以讓SVG定義新屬性來控制剪切。

Firefox瀏覽器實現了相同非標准行為,在剪切區域之外不支持鼠標事件。

盡管Chrome為HTML元素的clip-path屬笥實現標准行為,當你在一個<svg>元素上使用<clipPath>時,實現的行為是一樣的。只有Firefox在可視區域能響應鼠標事件,我不知道這是一個特性還是一個Bug。

在接下來的示例中,一個SVG的<clipPath>應用在一個SVG的<image>上。這個剪切路徑我們前面使用過,圖像剪成很多個矩形。當你的鼠標懸浮在圖像上,圖像具有一定的透明度。

image { clip-path: url(#svgPath); } image:hover { opacity: .5; }

查看DEMO

請注意,一個空的剪切路徑同樣會被clip-path應用在元素上。

總結

剪切是一種圖形化操作,允許我們在一個矩形的頁面中創建不規則圖形。實際上剪切和CSS的Shapes是一對完美的搭檔。如果你有閱讀過我早前寫的關於CSS的Shapes的文章,你就會看到很多實例中使用了clip-path屬性。一旦CSS的Shapes可以運用於SVG的路徑上CSS Shapes Module Level 2),除了CSS的基本形狀之外,CSS的Shapes和剪切配合可以讓我們在視覺上制作出引人注目的設計,打破矩形的限制。

我希望這篇文章對你有所幫助,感謝您的閱讀。

譯者手語:整個翻譯依照原文線路進行,並在翻譯過程略加了個人對技術的理解。如果翻譯有不對之處,還煩請同行朋友指點。謝謝!

如需轉載煩請注明出處:

英文原文:http://sarasoueidan.com/blog/css-svg-clipping/

中文譯文:http://www.w3cplus.com/css3/css-svg-clipping.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM