CSS和SVG中的剪切——clip-path属性和<clipPath>元素
本文由大漠根据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的图形属性。如果你对这方面知识感兴趣,可以阅读我早前写的相关文章。
有关于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);
}
应用上面的代码之后,图像显示成这样:
基本图形函数允许我们创建一定数量的图形,其中最复杂的就是多边形。如果你想创建一个更为复杂的图形,而且图形看起来不是用直线画出来的,这个时候你就需要使用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>
这剪切路径看起来就像一个黑色的描边圈了一个不规则的图形,这是一个简单的剪切路径,不带有任何的填充。
在下一部分中,我们将着重讨论SVG的<clipPath>
元素。但现在,我们来看看如何将定义好的路径运用到图片上:
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中的<text>
最酷的特点就是可以使用自定义字体,就像HTML文本。在我们的示例中使用了Google Web Fonts中的Vollkorn字体。使用textLength
属性,将文本的宽度设置的和图片宽度一样, 并且通过x
和y
来定位文本。注意,x
和y
坐标确认了文本左下角的位置(也就是文本的基线baseline)。
使用上面的文本路径剪切图来的图片效果如下:
正如我们前面提到的,你还可以在<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>
此时,图像只会在圆中显示,圆外就不会显示:
正如我们这篇文章中介绍的,你可以使用clip-path
属性应用在SVG元素绘制的路径。在上面演示的示例,剪切路径都是应用在HTML中的<img>
元素上。在接下来的示例中,将演示的是一个剪切路径用于<svg>
的根元素。同样是樱花图片,使用下面SVG的中的<image>
来引用。
SVG中的<image>
元素用来引用一个完整的SVG或像素图像。如果你在<image>
中引用的SVG图像,设置的width
和height
属性,将会用来设置SVG视窗的大小。如果你引用的是像素图像(我们这里的例子就是这样做的),图像将会自动缩放到指定的width
和height
。所以我们要确认好他们的长宽比例,避免图像扭曲。
当你创建一个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);
}
更多有关于在SVG上使用clip-path
的示例将会在下面介绍<clipPath>
部分介绍。
一个剪切路径的参考盒子
除了剪切路径本身,还可以给剪切路径应用一个<basic-shape>
定义一个剪切路径的参考盒子。也就是说,可以使用一个剪切的基本函数创建一个剪切路径。其中参考盒子只能使用CSS的clip-path
来指定形状路径,而不能使用SVG的<clipPath>
。对于SVG的<clipPath>
参考盒子是一个border-box
元素。
因此剪切路径的参考盒子用<basic-shape>
来指定。如果是一个HTML元素被剪切,可以使用四种盒模型:margin-box
、border-box
、padding-box
和content-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
区域外是无效的。这意味着,如果没有做剪切,鼠标事件还是有效的。这一部规范中做了详细的描述,只是实现方式不同。
绿色区域表示可以响应鼠标事件。左图表示的是标签规范的行为,右图表示非标签规范行为。
为了演示,我在前面示例的基础上,给图片添加了一个div
容器,使用了SVG的<clipPath>
制作剪切路径。如果没有使用剪切,你可以看到图像有边框。添加一个hover
效果,鼠标经过图片时,图片会带有一定的透明度。
如果使用Chrome浏览器(35.0.1916.153版本测试),就算鼠标在图像的剪切之外的区域,图片也会有一琮的透明度。这种行为就是符合标准规范的一种行为。
在Firefox浏览器(30.0版本测试),除了在可视区域图像不会响应鼠标事件。这意味着,鼠标移到图像剪切区域之外,会失去鼠标事件。(图像不带有透明度)
我必须得说,我更喜欢符合规范规范的那种行为,不知道你喜欢的是哪种行为?
剪切路径还可以使用动画效果。如果使用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已修复。
请注意,<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
和像fill
和stroke
这样的显示属性以及其他更多属性。其中最有用的是clipPathUnits
属性。
clipPathUnits
主要用来给<clipPath>
元素内容指定一个坐标系统。它具有两个值:objectBoundingBox
和userSpaceOnUse
,其中userSpaceOnUse
是默认值。
clipPathUnits = "userSpaceOnUse | objectBoundingBox"
userSpaceOnUse
当clipPath
元素是用来当作参考物时,clipPath
元素内容是以用户坐标系统作为参考点。(例如:clipPath
元素的用户坐标系统是通过clip-path
属性来引用)。
用户坐标系统(局部坐标系统)是目前激活的坐标系统,主要用来如何定位坐标和长度。一个HTML元素的坐标和CSS的盒模型有关,但不同的是SVG元素没有这样的盒模型。
对于CSS盒子的布局,用户的坐标原点就在盒子的左上角,而且一个单位就是一个像素,视窗也可以根据盒子的宽度按百分比计算。我想你对这方面应该非常的熟悉。如果你有一个<clipPath>
元素包含了一个<circle>
,而且这个<circle>
的中心点在cx=100
和cy=100
。那么这个中心点就是距盒子左边100px和顶边100px的交汇处。
如果元素是一个SVG元素,因此他是没一个类似于CSS盒模型的东西,用户的坐标原点是距<svg>
元素视窗左上角最近的一个地方。一般情况之下,最近的视窗的建立,他的宽度和高度接近于<svg>
的祖先元素。如果你不嵌套<svg>
元素,它就是你创建的<svg>
元素。
注意,SVG元素的坐标系统可以使用viewBox
属性进行修改,其他属性可能有助于改变坐标系统。这一部分的内容超出了本文的内容范围。所以在本文中,我假设viewBox
没有进行过任何的修改。因此浏览器使用的默认坐标系统原点是在<svg>
元素的左上角,大小也等于<svg>
元素。
objectBoundingBox
坐标系统的原点是在元素的边框盒子的左上角顶点处,同样适于剪切路径。这个边框是SVG元素对象的边框(它只是包含了一个或多个几何图形形状)和一个HTML元素设置border-box
的盒模型是相关联的。
这个值对SVG
元素非常有用,因为它允许你应用的元素自身的边界做为剪切路径。下图显示一个图像应用SVG的剪切路径显示的效果,他们分别使用了userSpaceOnUse
和objectBoundingBox
。灰色的边框表示的是SVG
元素创建的一个视窗。右图中的图像,我添加了一个灰色的边框用来表示剪切后的图像边框。
在左图中,剪切路径的坐系统定位在SVG的视窗上。当使用了objectBoundingBox
属性之后,图像自身的边框就会做为剪切路径的坐标系统。
有一点需要特别的注意:当你设置了objectBoundingBox
值后,<clipPath>
元素中的内容必须在指定的坐标[0,1]内。坐标系统将成为一个单元系统,剪切出来的形状都在这个clipPath
分值内。
例如,如果剪切路径包含一个<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;
}
请注意,一个空的剪切路径同样会被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/
中文译文:https://www.fedev.cn/css3/css-svg-clipping.html
Nike Kyrie 4