CSS和SVG有许多共同点。我们如今使用的很多CSS特性都被引入到SVG中。其中的一项特性是剪裁操作。CSS和SVG都允许我们将元素剪裁为不规则的形状。在这篇文章中,我们将讲解CSS和SVG的剪裁技术,包括所有你需要知道的知识。
什么是剪裁(clip)?
剪裁(clip)是一项允许你全部或部分隐藏元素的图像操作。剪裁元素可以是任何元素或图像元素。哪部分元素将被显示是由剪裁路径(clipping path)决定的。
一个剪裁路径定义了一个范围,在这个范围之内的元素可以被显示,而范围之外的元素则被隐藏。任何在剪裁路径之外的元素都不会被绘制,任何内容,背景,边框,文字装饰,轮廓和可见的滚动机制的元素都可以应用裁剪路径。
剪裁路径从理论上说等效于为元素自定义一个窗口。剪裁路径只会影响图像在屏幕上的显示,而不会影响图像的固有形状,被剪裁的元素还是以原来的形态处于文档流中。即使是它被剪裁为一个不规则的图形,其它元素也会以一个规则的图形来对待它。如果你想改变环绕在剪裁图像周围的元素的“流动方式”,使它们按照裁剪路径进行排列,可以使用CSS Shapes属性。
在CSS中进行图像裁剪-Clip-Path属性
clip-path是CSS Masking Module的一部分。从2000年开始,剪裁操作成为SVG的一部分,并且被移植到 CSS Masking module 中,因此我们可以像SVG元素一样使用CSS来裁剪HTML元素。
clip-path属性用于指定裁剪的路径。你可以通过一个SVG 的<clipPath>指定的路径来作为clip-path,也可以使用CSS Shapes module中定义的基本图形来作为clip-path。这些图形可以通过shape 函数来创建,可用的图像函数有:polygon()、circle()、inset()(用于定义inset rectangles)和ellipse()。
在一个元素上使用clip-path来制作剪裁路径是非常简单的:
/* Referencing an SVG clipPath */
.element {
clip-path: url(#svgClipPathID);
}
/* Using a CSS basic shape function */
.element {
clip-path: polygon(...); /* or one of the other shape functions */
}
例如,如果你想使用polygon()函数来制作一个多边形的剪裁路径,然后将它应用到一幅图片上,代码应该这样写:
img {
clip-path: polygon(626px 463px,765px 236px,687px 31px,271px 100px,70px 10px,49px 250px,133px 406px,374px 462px,529px 393px);
}
上面的代码得到的结果如下图所示:
查看演示
基本的图形函数允许我们创建的图形是有限的。最复杂的的图形是多边形。如果你想创建更为复杂的图形,可以使用SVG <clipPath>元素。通过SVG <clipPath>你可以创建任何的图形。这意味着你可以使用<path>元素来创建任意的路径来剪裁图像。
在第二个例子中,我们将使用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>
下图是裁剪路径的形状,它是一个简单的不规则形状,没有填充,并添加了黑色的描边。
在文章的后面会有更详细的<svgPath>的讲解,现在我们只要引用它来啊制作裁剪图像:
img {
clip-path: url(#svgPath);
}
结果如下图所示:
查看演示
事实上,<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 <path>的时候我们还可以自定义字体,在例子中使用了一种谷歌字体,并使用textLength属性设置整个文本的宽度和图片的宽度一样宽,使用x和y坐标来定位文字。注意这里x和y坐标决定文字的左下角的位置。
结果如下图所示:
查看演示
前面还提到,可以在<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>
得到的结果如下图所示:
查看演示
正如文章开始的时候说到的,你也可以在SVG元素上通过clip-path属性来制作剪裁路径。在上面所有的例子中,都是将剪裁路径应用到HTML <img>元素上。下面这个例子中,将在一个<svg>根元素上应用剪裁路径。我们上面使用的樱花图片现在会通过<image>元素变成SVG的一部分。
SVG中的<image>元素是用于在SVG中包含一幅图片使其变为SVG的一部分。如果你在SVG中引用了一幅图片,那么它的宽度和高度属性将被用来建立SVG的视口。如果你引用的是光栅图像,图像会被缩放到指定的宽度和高度。在下面的例子中,我们要使用一幅光栅图像,并指定<image>的宽高比和图像的宽高比相同。
当你在创建一个<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属性将剪裁路径应用到<svg>上:
svg {clip-path: url(#theSVGPath);}
结果如下图所示:
查看演示
剪裁路径参考BOX
除了剪裁路径本身,你可以在剪裁路径应用到<basic-shape>时在clip-path属性上定义一个参考BOX,换句话说,通过基本的图形函数来创建剪裁路径。参考BOX只在CSS图形作为剪裁路径时使用,而不能是SVG <clipPath>。对于SVG <clipPath>,参考BOX只是HTML元素的边框。
现在知道参考模型只被应用到<basic-shape>的剪裁路径中。如果元素被一个HTML元素裁剪,参考BOX可以是四个基本盒模型box之一:margin-box、border-box、padding-box和content-box。
如果<basic-shape>剪裁路径被应用到SVG元素上,参考BOX可以设置3个关键值:
- fill-box:使用对象的bounding box作为参考。
- stroke-box:使用描边bounding box作为参考。
- view-box:如果没有
viewBox被指定,使用最近的SVG视口作为参考BOX。如果指定某个viewBox,那么通过viewbox的原点和尺寸来建立坐标系。
如果你为SVG元素使用任何的CSS的盒模型box来作为参考box,fill-box将被使用。反之,如果你在HTML元素上使用任何的SVG参考BOX,border-box将被使用。
.element {
clip-path: polygon(...) padding-box;
}
如果在clip-path属性中只有一个参考box被指定(也就是没有基本图形被指定),浏览器将使用指定box的边框,包括任何圆角图形来作为剪裁路径。
.el {
clip-path: border-box;
border-radius: 25%;
}
在SVG中进行图像裁剪-<ClipPath>元素
在SVG中,使用clipPath来指定剪裁路径。如果你比想使用CSS为元素应用剪裁路径,可以在SVG中使用clip-path属性来实现。
<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>元素。其实它还可以包含更多的内容。
<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变化到250像素,并且每隔3秒执行一次。这个动画过程是无限循环的。
你可以点击下面的按钮查看这个DEMO。在这之前,你需要了解一个 bug,因为这个bug的存在,可能在Chrome和Safari浏览器中看不到效果。我们建议你使用Firefox浏览器来查看这个DEMO。
查看演示注意:<clipPath>的内容也不可以包含一个组(<g>)。例如,如果我们在上面的例子中使用多个圆作为剪裁路径来添加到一个组中,那么demo将不能正常工作。
<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。transforms和 presentation attributes(类似fill和stroke),以及很多styling properties。其中有一个非常有用的属性是clipPathUnits属性。
clipPathUnits属性用于指定<clipPath>元素的坐标系统。它有一个活两个值:objectBoundingBox 和userSpaceOnUse。默认值是userSpaceOnUse。
clipPathUnits = "userSpaceOnUse | objectBoundingBox"
userSpaceOnUse
当clipPath元素被引用的时候,clipPath的内容代表在此刻用户坐标系统的值。
当前用户坐标系统是当前正在使用的坐标系统。一个HTML元素的用户坐标系统是和CSS box model相关联的,它不同于SVG元素。
对于和CSS layout box关联的元素,当前用户坐标系统的原点位于参考模型的左上角,单位是CSS的像素单位。视口的百分比值由参考模型的宽度和高度来定义。假如有一个<clipPath>包含一个中心点为cx = "100"和cy = "100"的<circle>,那么圆的中心点将被放置在参考模型距离左边100像素,距离上边100像素的地方。
如果元素是一个SVG,它就没有关联的CSS layout box,当前的坐标系统原点位于离它最近的viewport的左上角。多数情况下,最近的viewport是宽度和高度最接近的<svg>元素。如果没有最近的<svg>,它将使用根<svg>来作为viewport。
注意SVG的坐标系统可以使用viewBox属性来改变。
objectBoundingBox
坐标系统的原点位于应用剪裁路径元素的bounding box的左上角,坐标系统的宽度和高度和bounding box相同。一个bounding box是包围所有SVG元素(它进包含元素的几何图形)和关联box model的HTML border box的对象box。
对于SVG元素来使,它是非常有用的。因为它允许你在元素边界上应用剪裁路径,而不是使用坐标系统。下面的图片是在SVG画布上使用userSpaceOnUse和objectBoundingBox坐标系统的结果。灰色的边框是<svg>元素的viewport,右边的图片用灰色标出了bounding box的范围。

在左边的图中,坐标系统建立在SVG的viewport上,剪裁路径被定位于其中。当使用objectBoundingBox的时候,图片的bounding box被用来作为剪裁路径的坐标系统。
提醒注意一点:当使用objectBoundingBox的时候,clipPath的内容指定的坐标必须在[0, 1]范围之内-坐标系统变为一个单位系统,在clipPath中的图形坐标变为小数值。

例如,设置一个圆形为剪裁路径,那么它的中心位于剪裁元素的中心:
<clipPath>
<circle cx="350" cy="350" r="300" />
</clipPath>
圆的位置也可以用小数来表示:
<clipPath clipPathUnits="objectBoundingBox">
<circle cx=".5" cy=".5" r=".45" />
</clipPath>
这里的小数相当于百分比。