打破盒子模式的限制,使用Clip-Path创建响应式图形
CSS的clip-path
属性是你改变传统单调的盒子布局,走向响应式设计的通行证。你将开始摆脱条条框框,自由地在您的网页上使用六边形、星形和八边形等等进行设计。一旦你真正开始使用clip-path
,你可以生成的形状是无限多的,只需要简单地调整几个值即可。
虽然这篇文章的重点是使用CSSclip-path
完成多边形裁剪,但是所有的demo都提供了内联SVG的参考,这是为了能额外获得Firefox的支持。只要你使用过CSSclip-path
创建响应图形,生成响应式的SVG裁剪图形是很容易的。我们后面再详细说说。
Clip-Path
使用clip-path
属性进行裁剪,类似于从一张矩形的纸上剪下一个图形(如圆形或五角形)。这个属性属于“CSS Masking Module Level 1”规范的内容。该规范指出,“CSS提供了两种方式来部分或全部隐藏可视元素:masking和clipping”。
第一种方法是蒙版,需要使用一个图形元素,比如PNG图像、CSS渐变或SVG元素,来作为另一个元素的蒙版。
第二种方法是clip-path
,包括了一条闭合的矢量路径,它可以是CSS中定义的基础形状,也可以是包含了clipPath
标签的SVG元素。在闭合路径内的内容会显示,而路径外边的都会被剪掉。
**注意:**蒙版不在这篇文章的讨论范围内,不过在CSS-Tricks 和 HTML5 Rocks上有很多详细的资料,有兴趣的可以查看。
下面是关于clip-path
是如何运作的一个简单的可视化介绍:
**注意:**这篇文章中的demo,包括上边的那个,在Firefox、WebKit以及Blink浏览器中(包括Chrome、Safari和Opera)都时可以运行的~~
Clip-Path不等于Clip
旧的CSS2.1有一个clip
属性,因为使用有较多限制,尤其是它只支持矩形。现在它已经被弃用,而clip-path
取而代之。
关于clip属性的语法,它的代码是这样写的:
.element {
clip: rect(30px, 30px, 20px, 20px);
}
Clip-Path的浏览器支持情况
在2014年8月,“CSS Masking Module”被作为“候选推荐标准”发表,这是由之前的“Last Call Working Draft”逐步发展而成的。在我们看浏览器的支持情况之前,非常重要的一点是考虑clip-path
被应用于元素的多种方法,因为浏览器的支持因应用的方法而异。
我们可以通过两种方法使用clip-path
:
使用CSS
CSS Shapes Module中的基础形状提供了一个非常方便的使用clip-path
的方法。它有多种不同的可以使用的形状如polygon
, circle
, ellipse
和 inset
;其中,inset
是矩形。
使用SVG
使用SVG,也就是另外创建一个形状,然后通过URL语法让这个形状来裁剪元素。使用SVG的方式也有两种:
- 内联SVG的引用(例如,在页面中本身就有的SVG元素);
- 外部SVG文件的引用。
这两种方式中,SVG内的clipPath
用于包裹决定裁剪路径的元素,这个元素可以是一个圆、多边形、path路径或其它的各种元素。在Firefox和WebKit(或Blink)内核的浏览器(比如Chrome)中对比这个demo,找找它们有什么差异。可以发现浏览器对不规则图像的支持不全。
**注意:**第三个图像在Safari中无法显示。尽管调试了很多次,我还是没有解决这个问题。如果你有解决方案的话,请在评论留言,非常感激~~
如果你已经在不同的浏览器中观察了上面的demo,就会发现浏览器对于clipping paths的支持是很古怪的,它取决于你裁剪元素的方式。
- **使用CSS:**Chrome 24+, Safari 7+, Opera 15+, iOS 7.1+, Android 4.4+, Opera Mobile 24+(注意:所有目前支持的浏览器都需要一个-webkit前缀)。
- **使用SVG:**上面列出的所有浏览器以及Firefox 3.5+。
IE现在还没有支持clip-path
,但目前正在审议把它加入“Masking Module”。
**注意:**对于SVG clipping path,有一点需要注意。只有使用内联SVG,WebKit和Blink内核的浏览器才支持clipping paths属性(也就是说,SVG和html在同一个文档里)。引入外部SVG文件只在Firefox中支持,如上面的demo。Chromium项目正在解决这个bug。【简单地说就是:Webkit或Blink支持CSS和inline SVG;Firefox只支持SVG,但是inline和外部引入都支持】
我们先总结一下,clip-path
,使用CSS和SVG的各自优势。
CSS的优势
- 清晰的语法,以及相对容易掌握的基础形状。
- 响应式很容易使用相对单位实现,例如百分比。
SVG的优势
- 浏览器支持更好,相比使用CSS多了Firefox的支持。
- 你可以剪裁复杂的形状,多个形状和文本。
虽然CSS提供了一个background-clip
属性,为我们提供了一些选项(包括非标准的文本剪裁规范)。但是,不论是background-clip
还是CSS的clip-path
属性,都无法达到SVG的clipping在现代浏览器中成就。通过CSS来熟悉clip-path
并不难(尤其是如果你不熟悉如何操作SVG),是为你学习SVG错综复杂的clipping paths做准备,还有“CSS Shapes Module”,能让内容自适应元素的形状对齐。
**注意:**如果你迫不及待地想要看SVG clipping的内容,Sara Soueidan的这篇概述是很棒的入门文章。
让我们来看看使用clip-path
逐步提高我们的设计的优点和缺点:
优点
- 不支持
clipping
属性的浏览器会直接忽略它。如果你能小心使用,使用不支持的浏览器的用户就不会注意到! - 一旦生成clipping-path形状,规范中声明:鼠标事件在裁剪区域之外的地方不能被调度(这是我们的理想情况)。所以点击事件被严格限制于图形本身和它的外边界。我们在下边的demo中再来看看。
- 你可以使用百分比或者其它任何的长度单位,比如像素或em,来定义CSS基础图形的坐标系统。浮动单位,如百分比可以用来创建响应式图形,完美的自适应布局。
缺点
- 所有的border、shadow、outline,只要是在裁剪区域之外的都会被裁剪。你不能添加border,然后期待它能幸运地不会被剪掉。我们等下会看看下面的替代方案。
- 规范还没有达到“Recommendation”的阶段,所以在这个期间,语法还可能会发生变化。
- 关于
clip-path
、3D转换、过渡和透明度,有一些bug已经被发现,下面的demo会对其进行演示。这些是需要了解的东西,避免组合属性时重复这些bug。
多边形Clip-Path:语法和应用
下面重点演示在一个设计中使用不同种类的多边形。其它基础图形(如inset
, circle
和 ellipse
)的语法是非常简单的,你能看到的也只是这些形状了。但是,多边形,它能够创建的实际形状是无限多的。
声明一个基础多边形图形的语法如下:
.element { clip-path: polygon(x1 y1, x2 y2, x3 y3, ...); }
列表中的每个参数对,表示多边形对应顶点的x
轴和y
轴坐标。
这是我们实际应用中写的(去掉支持WebKit前缀的版本):
.element { clip-path: polygon(0 100%, 0 0, 100% 0, 80% 100%); }
加上Firefox的支持,即引用内联SVG:
.element { clip-path: url("#clip-shape"); }
这是被裁剪的元素的类选择器的最终代码,跨浏览器支持:
.element {
-webkit-clip-path: polygon(0 100%, 0 0, 100% 0, 80% 100%);
clip-path: polygon(0 100%, 0 0, 100% 0, 80% 100%);
-webkit-clip-path: url("#clip-shape"); /* 在Webkit或Blink浏览器中需要添加,如果你只使用内联SVGclipping path,而不是CSS clip-path的话 */
clip-path: url("#clip-shape");
}
下面是内联SVG的代码,我们需要把它加入到html中。
<svg width="0" height="0">
<defs>
<clipPath id="clip-shape" clipPathUnits="objectBoundingBox">
<polygon points="0 1, 0 0, 1 0, 0.8 1" />
</clipPath>
</defs>
</svg>
这是最后的demo:
你可以按照以下方式创建一个响应式的SVG裁剪路径:
- 设置SVG的
width
和height
为0
; - 在SVG中,为
clipPath
元素设置一个ID,以便被CSS引用。内联或外部的SVG都可以,但是记住上面提到的浏览器支持问题; - 重复使用多边形CSS
clip-path
的百分比坐标值。只需将它们除以100
,然后作为无单位的多边形顶点添加到SVG中。 - 设置
clipPathUnits
属性的值为objectBoundingBox
,这样clipping path就继承了引用了它的HTML元素的边界。
Dudley Storey有关于这个步骤的更详细的介绍。
我们接下来看另一个demo,了解如何绘制多边形的坐标。
下面放了一副裁剪过的图像,背景颜色代表图片的原始尺寸。黑色的带有坐标的小黑块是绝对定位的div,它们的位置和多边形的顶点在百分比值上相对应。它们会一直保持自己的位置不变,即使你把浏览器的宽度调窄(例如,把窗口宽度调成400px或更大)。
使用Clip-Path的实例
**注意:**这篇文章中的所有demo都是使用CSSclip-path
,但是在html中添加了内联SVG,类名为clip-svg
,简单地用于把SVG的width和height都设置为0
。你也可以选择删除这个类,然后直接在SVG标签中设置width
和height
属性。
实例一:按照不同的多边形形状裁剪图像
如果你需要快速定义一个多边形,它必须是一个由直线组成并闭合的2D图形。
多边形是没有曲线的,而且必须闭合,并由三条以上的直线组成。历史上一些著名的多边形有三角形、四边形、五边形和六边形。星形也是多边形,因为多边形的边界可以相互交叉。
**注意:**这个demo中的图像是响应式的。通过使用ol的响应式图像方案img { max-width: 100%; height: auto; }
,并让CSS和SVG自适应clipping path,这样我们就可以自由地让多边形放大或缩小~
这个demo是了解绘图坐标来制作多边形图形的练习。我在下面的demo中加入了几个你可以在自己的设计中使用的图形。把鼠标移上去,你就可以看到原始图像的宽高比。
没有什么比Clippy更强大,这是一个由Bennett Feely制作的GUI工具,用于创建可视化图形。Clippy上的所有图形都提供了百分比坐标,还有自定义多边形的选项。你可以使用Clippy来生成用于裁剪的图形,然后在它们的基础上创建你自己的SVG,因为SVG有更好的浏览器支持。
实例二:使用CSS Transition制作过渡效果的基础图形
把鼠标移动到紫色的六边形上时,它会变成一个八边形。但是,指定的CSS过渡效果并没有生效。
Sara Soueidan在她的文章中对这个问题做了解释:“确定最终形状的点的数目,必须和初始形状的点的数目一样。”非常正确的答案!
由于六边形有六个坐标点,我们通过复制加入两个点,让它变成八个点,来和八边形的点的数量相对应。这些复制的坐标点不会影响六边形的形状。
下面是六边形的声明,表示默认有六个坐标点的六边形:
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
这个六边形的声明是带了八个坐标点的,最前面的两个点是复制的:
clip-path: polygon(50% 0%, 50% 0%, 100% 25%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
现在过渡将会是平滑的图形变换,可以在下面的demo中查看。
**注意:**对于那些只支持SVG clipping paths的浏览器(目前就是Firefox),我们需要添加一个SMIL动画,来获取一个悬停无缝过渡的效果。根据SMIL规范,声明动画可以被用于SVG中的路径和多边形坐标点,不过目前对于CSS还不行。
注意一些人在讨论将SMIL从Chrome和Chromium中分离,然后专注于实现Web动画的API接口,不过可惜的是这还是在草案阶段。
在下面的demo中(背景图来源于morgueFile),你可以看到我们把多边形顶点在mouseover
和mouseout
事件中添加了一个0.2
秒的过渡。查看SVG标记中的<animate>
标签。
实例三:为被裁剪的对象添加border
言而言之,裁剪区域外的border
、outline
和box-shadow
都会被删除。
我有点难过,因为我联系了W3C中CSS工作小组的成员。结论是,如果你正在使用基础图形,你没有办法这样做。Dirk Schulze回答了我的问题:“是的,所有属于元素的绘制操作都会被裁剪,包括outline和border。”
参阅下面的demo,将鼠标悬停在有border的菱形上,可以看到原始的没有被裁剪过的带有完整边框的版本。
当然,我们可以使用CSS破解来得到边框,这是我最后的绝招——ol生成的内容。
下面的demo通过伪元素(content:after
)创建了元素的副本,并将它设置为绝对定位。这可以创建一个有边框的错觉,让我们模拟出有趣的效果,比如在第二个八边形中看到的渐变边框,还有在第三个中通过CSS滤镜生成的box-shadow(不是很漂亮,但很有实用性)。注意CSS滤镜目前只在Firefox和WebKit或Blink浏览器中可以运行。
实例四:使用clip-path在菱形上创建一个钻石网格
下面是我们将要使用的图片。
这是我们的目标效果。当鼠标悬停在底部的三个盒子上,你可以看到背景颜色开始褪色,并显示出真正的背景。
这张图片的实际尺寸是600 × 600px
。因此,我们先创建四个空的300px
的div
元素,分别为它们应用相同的背景图片。我们添加一个604
像素的包裹元素,然后使用inline-block
属性排列图像。
我们现在分别改变每个图像background-position
属性的值为top
、left
、right
和bottom
。
我们把每个盒子都裁剪成菱形,然后在下面的三个图像上分别覆盖一个绝对定位的带有文本的图层。
现在我们把图片按行排列——第二个和第三个图像放到同一行上,把第一个和第四个单独放到它们自己单独的那一行上。
最后,我们使用负边距来把第二行和第三行往上移,让它们如下面的最后的demo那样排列。我们可以删除父元素的604px
的width
值,然后构建媒体查询这样四个菱形盒子就可以从小屏幕上的堆叠布局变成在较大屏幕上的内联布局。
在运行这个demo的时候,我注意到了Chrome中的一个bug,在裁剪区域之外的地方,鼠标事件仍然可以被调度了。这违反了规范的内容:“默认情况下,鼠标事件在被裁剪掉(即不可视)的区域,是不能被调度的。”我已经提交了这个bug。在这个demo中,通过给覆盖层设置pointer-events
属性的值为none
解决了这个问题。或者,你可以为覆盖层应用相同的clip-path
值来解决这个问题。
由于应用了负边距,这个demo在不支持clip-path
的浏览器中看起来是很奇怪的。你需要使用一些特性检测来应用边距(尽管我没有做过试验),或@supports
CSS功能查询。尽管我不建议在产品代码中应用后者。
实例五:创建一个带有六边形的个人主页
我们最后的页面是这样的:
我们从为body
添加一个六边形的背景图像开始(图片来源于Subtle Patterns)。
六边形的clip-path
值可以从上面的demo中找到,或者使用Clippy工具也可以。
第一个六边形使用了一个背景图像(因为我们把沉闷的栗色融合到了背景中,使用了background-blend-mode
属性)。使用生成的内容,一个绝对定位的覆盖层被裁剪成栗色的三角形,你可以在下面看到。在鼠标悬停的时候它会消失。
第二个六边形,带有“work”这个单词的,简单的黑灰色背景,在鼠标悬停的时候会改变颜色。
第三个六边形有一个渐变的边框,和前面那个使用clip-path
创建边框的类似。
这些六边形在小屏幕上是堆叠放置的,在比较大的屏幕上则是垂直居中的。我结合使用了display:table
,还有绝对居中的转换——当然,你可以使用flexbox,float
或其它任何可以让你的布局浮动的东西。
这是最后的demo。
我在创建这个demo的时候发现了clip-path
的一个bug。在和CSS过渡结合使用的时候,改变opacity
的值,会导致页面的闪烁。如果你想要使用clip-path
来逐步提高你的设计的话,你需要注意这一点。
关于clip-path
还有另外一个bug,当把backface-visibility
属性设置为hidden
的时候。这个bug已经被记录在Chromium的问题追踪文档中,我已经可以在Linux环境下的Chrome中使用基础图形语法来重现它。如果你想用clip-path
来做一个很酷的3D翻转或其它任何有使用CSS 3D转换的东西,你需要记住这一点。
使用SVG裁剪比较受欢迎,因为它的灵活性以及更多的图形选择。但是没有什么比直接用CSS裁剪元素要来的方便,所以事实上,相同的多边形坐标可以被很容易地回收,然后用来创建响应式的SVG,以获得更好的浏览器支持。使用clip-path
,你可以非常明显地改变页面的外观,而不需要太担心浏览器支持的问题,因为它会适当降低。如果你选择将clip-path
用于设计改进,要留心规范的内容,因为它即将进入到了“Recommendation”的阶段。
资源、工具和灵感
- “CSS Masking Module Level 1,” W3C 这是在有疑问时最好最规范的参考资料。
- “Clipping in CSS and SVG: The
clip-path
Property and<clipPath>
Element,” Sara Soueidan。Sara Soueidan关于clipping paths的非常详细的指导文章。重点针对SVG做了非常棒的介绍,为中级和高级的读者提供了大量的信息。 - “
clip-path
,” Sara Soueidan, Codrops。Sara Soueidan在Codrops上的这篇文章做了非常深入的研究,内容非常全面。把一个相当复杂的内容讲解得非常容易理解和吸收。 - “Clipping and Masking in CSS,” Chris Coyier, CSS-Tricks。Coyier的文章,附带了一些有用的demo,而且对剪裁和蒙版都做了讲解。
- Clippy。Bennett Feely是最好的clip-path制作工具,可以为CSS的
clip-path
生成众多预定义和常用的多边形图形、圆形和椭圆等。所有的值都是百分比形式的,因此,对响应式布局非常有用。 - Clip Path Generator。CSS Plant提供了一个更全面的用于裁剪和为元素添加蒙版的图形界面。跨浏览器支持有Firefox、Chrome、Safari和旧的iOS。裁剪是以像素为单位的,不是百分比。
- Species in Pieces
。它的效果是非常令人惊叹的,这30种濒危物种完全是只使用了CSS的
clip-path
制作的,没有使用任何的canvas或WebGL技术。在WebKit或Blink内核的浏览器中查看。 - W3cplus中有关于
clip-path
的中文教程
本文根据@Karen Menezes的《Creating Responsive Shapes With Clip-Path And Breaking Out Of The Box》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://www.smashingmagazine.com/2015/05/11/creating-responsive-shapes-with-clip-path/。Nike Zoom KD Trey 5 IV black Men Basketball Shoes EM