将内容封装在自定义路径中
长久以来,Web 设计师一直被迫在矩形的约束下进行创作。由于大多数尝试采用非矩形布局的广告素材最终都以失败告终,因此网络上的大多数内容仍局限于简单的框架。随着 CSS Shapes 的引入,这种情况即将改变。CSS Shapes 从 Chrome 37 开始提供。 借助 CSS 形状,Web 设计师可以将内容换行到自定义路径(例如圆形、椭圆形和多边形)中,从而摆脱矩形的约束。
形状可以手动定义,也可以从图片中推断出来。
我们来看一个非常简单的示例。
也许您和我一样,在首次浮动包含透明部分的图片时,希望内容能够换行并填充空白,但最终却发现元素周围仍然保留着矩形换行形状,感到失望。您可以使用 CSS 形状来解决此问题。
![从图片中提取形状](https://web.developers.google.cn/static/articles/shapes-getting-started/image/extracting-shape-an-ima-35b58803a5775.jpg?hl=zh-cn)
<img class="element" src="image.png" />
<p>Lorem ipsum…</p>
<style>
.element{
shape-outside: url(image.png);
shape-image-threshold: 0.5;
float: left;
}
</style>
shape-outside: url(image.png)
CSS 声明用于告知浏览器从图片中提取形状。
shape-image-threshold
属性用于定义用于创建形状的像素的最小不透明度级别。其值必须介于 0.0
(完全透明)和 1.0
(完全不透明)之间。因此,shape-image-threshold: 0.5
表示仅会使用不透明度为 50% 或以上的像素来创建形状。
float
属性是关键。虽然 shape-outside
属性定义了内容将围绕的区域的形状,但如果没有浮点值,您将看不到形状的效果。
元素在其 float
值的对面有一个浮动区域。例如,如果包含咖啡杯图片的元素浮动到左侧,系统会在咖啡杯的右侧创建浮动区域。即使您可以设计出两侧都有间隙的图片,内容也只会沿着浮动属性指定的相反侧(左侧或右侧)环绕形状,而不会同时环绕两侧。
未来,您可以使用 CSS 排除项在未浮动的元素上使用 shape-outside
。
手动创建形状
除了从图片中提取形状之外,您还可以手动编码形状。您可以从以下几个函数值中进行选择,以创建形状:circle()
、ellipse()
、inset()
和 polygon()
。每个形状函数都接受一组坐标,并与用于建立坐标系的参考框搭配使用。稍后会详细介绍参考框。
circle() 函数
![circle() 形状值的插图](https://web.developers.google.cn/static/articles/shapes-getting-started/image/illustration-the-circle-41300698b1e1e.png?hl=zh-cn)
圆形形状值的完整表示法为 circle(r at cx cy)
,其中 r
是圆形的半径,而 cx
和 cy
是圆形中心在 X 轴和 Y 轴上的坐标。圆心坐标是可选的。如果您省略这些值,系统会将元素的中心(其对角线的交点)用作默认值。
.element{
shape-outside: circle(50%);
width: 300px;
height: 300px;
float: left;
}
在上面的示例中,内容将环绕圆形路径的外侧。单个参数 50%
用于指定圆形的半径,在本例中,该半径等于元素宽度或高度的一半。更改元素的尺寸会影响圆形的半径。下面是一个 CSS 形状如何响应的基本示例。
在继续之前,先说一点:请务必注意,CSS 形状只会影响元素周围浮动区域的形状。如果元素具有背景,则该背景不会被形状剪裁。如需实现此效果,您必须使用 CSS 遮罩中的属性,即 clip-path 或 mask-image。clip-path
属性非常实用,因为它遵循与 CSS 形状相同的表示法,因此您可以重复使用值。
![`circle()` 形状 + clip-path 的插图](https://web.developers.google.cn/static/articles/shapes-getting-started/image/illustration-circle-557dc199e52fc.png?hl=zh-cn)
本文档中的插图使用剪裁来突出显示形状,以帮助您了解相关效果。
返回圆形。
使用百分比表示圆形半径时,系统实际上会使用稍微复杂一些的公式 sqrt(width^2 + height^2) / sqrt(2) 来计算该值。了解这一点很有用,因为这有助于您想象如果元素的尺寸不等,最终的圆形会是什么形状。
形状函数坐标中可以使用所有 CSS 单位类型,例如 px、em、rem、vw、vh 等。您可以根据自己的需求选择灵活或严格的方案。
您可以通过为圆形的中心坐标设置显式值来调整圆形的位置。
.element{
shape-outside: circle(50% at 0 0);
}
这会将圆心放置在坐标系的原点。什么是坐标系?我们将在此处介绍参考框。
CSS 形状的参考框
参照框是元素周围的一个虚拟框,用于建立用于绘制和定位形状的坐标系。坐标系的原点位于其左上角,X 轴指向右侧,Y 轴指向下方。
![CSS 形状的坐标系](https://web.developers.google.cn/static/articles/shapes-getting-started/image/coordinate-system-css-sh-3d9f3d6433162.png?hl=zh-cn)
请注意,shape-outside
会更改内容将围绕的浮动区域的形状。浮动区域会延伸到由 margin
属性定义的框的边缘。这称为 margin-box
,如果未明确提及任何参考框,则它是形状的默认参考框。
以下两个 CSS 声明的结果相同:
.element{
shape-outside: circle(50% at 0 0);
/* identical to: */
shape-outside: circle(50% at 0 0) margin-box;
}
我们尚未为该元素设置边距。此时,可以安全地假设坐标系的原点和圆形中心位于元素内容区域的左上角。设置边距后,情况会有所不同:
.element{
shape-outside: circle(50% at 0 0) margin-box;
margin: 100px;
}
坐标系的原点现在位于元素的内容区域之外(向上 100 像素,向左 100 像素),圆形中心也是如此。计算出的圆形半径值也会随之增大,以反映 margin-box
参照框建立的坐标系的表面增大。
![带有和不带边距的边距盒坐标系](https://web.developers.google.cn/static/articles/shapes-getting-started/image/margin-box-coordinate-sys-e3bd8990048b4.png?hl=zh-cn)
![所有参考框的插图](https://web.developers.google.cn/static/articles/shapes-getting-started/image/illustration-all-referen-7496cb776193a.png?hl=zh-cn)
在给定时间内,只能使用一个包含 shape-outside
声明的引用框。每个参考框都会以不同的方式(有时是细微的方式)影响形状。我们还提供了另一篇文章,可帮助您更深入地了解 CSS 形状的参考框。
ellipse() 函数
![ellipse() 形状值示意图](https://web.developers.google.cn/static/articles/shapes-getting-started/image/illustration-ellipse-s-846127e1dee7f.png?hl=zh-cn)
椭圆形看起来像被压扁的圆形。它们定义为 ellipse(rx ry at cx cy)
,其中 rx
和 ry
是椭圆在 X 轴和 Y 轴上的半径,而 cx
和 cy
是椭圆中心的坐标。
.element{
shape-outside: ellipse(150px 300px at 50% 50%);
width: 300px;
height: 600px;
}
系统会根据坐标系的维度计算百分比值。无需进行复杂的计算。您可以省略椭圆形中心的坐标,系统会根据坐标系的中心推断出这些坐标。
X 轴和 Y 轴上的半径也可以使用关键字进行定义:farthest-side
会产生一个半径,该半径等于椭圆形中心与最远处于该中心的参考框边之间的距离,而 closest-side
则表示完全相反的意思,即使用中心与边之间的最短距离。
.element{
shape-outside: ellipse(closest-side farthest-side at 50% 50%);
/* identical to: */
shape-outside: ellipse(150px 300px at 50% 50%);
width: 300px;
height: 600px;
}
当元素的尺寸(或参考框)可能会以不可预知的方式发生变化,但您希望椭圆形能够相应调整时,这可能会派上用场。
同样的 farthest-side
和 closest-side
关键字也可以用于 circle()
形状函数中的半径。
polygon() 函数
![polygon() 形状值示意图](https://web.developers.google.cn/static/articles/shapes-getting-started/image/illustration-polygon-s-b96636bce2fe8.jpg?hl=zh-cn)
如果圆形和椭圆形限制太多,多边形形状函数可以为您提供无限的选择。格式为 polygon(x1 y1, x2 y2, ...)
,您可以在其中为多边形的每个顶点(点)指定 x y 坐标对。用于指定多边形的最小对数为 3,即一个三角形。
.element{
shape-outside: polygon(0 0, 0 300px, 300px 600px);
width: 300px;
height: 600px;
}
顶点放置在坐标系上。对于响应式多边形,您可以为部分或全部坐标使用百分比值。
.element{
/* polygon responsive to font-size*/
shape-outside: polygon(0 0, 0 100%, 100% 100%);
width: 20em;
height: 40em;
}
有一个可选的 fill-rule
参数(从 SVG 导入),用于指示浏览器如何在出现自相交路径或封闭形状的情况下考虑多边形的“内侧性”。Joni Trythall 非常详细地介绍了 SVG 中的 fill-rule 属性的运作方式。如果未定义,则 fill-rule
默认为 nonzero
。
.element{
shape-outside: polygon(0 0, 0 100%, 100% 100%);
/* identical to: */
shape-outside: polygon(nonzero, 0 0, 0 100%, 100% 100%);
}
inset() 函数
借助 inset()
形状函数,您可以创建矩形形状,并在其周围换行内容。考虑到 CSS Shapes 的初始前提是让 Web 内容摆脱简单的框架,这可能听起来不合常理。很有可能。我还没有找到 inset()
的用例,它无法通过浮点和边距或 polygon()
实现。不过,与 polygon()
相比,inset()
确实为矩形形状提供了更易读的表达式。
内嵌形状函数的完整表示法为 inset(top right bottom left border-radius)
。前四个位置参数是相对于元素边缘的内侧偏移量。最后一个参数是矩形形状的边框半径。此字段是可选字段,因此您可以将其省略。它遵循您在 CSS 中已使用的 border-radius
简写表示法。
.element{
shape-outside: inset(100px 100px 100px 100px);
/* yields a rectangular shape which is 100px inset on all sides */
float: left;
}
通过参考框创建形状
如果您未向 shape-outside
属性指定形状函数,则可以允许浏览器从元素的参考框中派生形状。默认的参考框是 margin-box
。到目前为止,没有什么特别的,浮点数就是这样运作的。不过,通过应用此技术,您可以重复使用元素的几何图形。我们来看看 border-radius
属性。
如果您使用它来使浮动元素的角变圆,则会获得剪裁效果,但浮动区域仍会保持矩形。添加 shape-outside: border-box
以环绕 border-radius
创建的轮廓。
![使用边框参考框从元素的边框半径中提取形状](https://web.developers.google.cn/static/articles/shapes-getting-started/image/extracting-shape-an-ele-5bf20ea5abb6d.png?hl=zh-cn)
.element{
border-radius: 50%;
shape-outside: border-box;
float: left;
}
当然,您可以以这种方式使用所有参考框。派生形状的另一种用途是偏移的引文。
![使用 content-box 参照框创建偏移引号](https://web.developers.google.cn/static/articles/shapes-getting-started/image/creating-offset-pull-quo-6cfdf95608a9d.png?hl=zh-cn)
只需使用浮动和边距属性,即可实现偏移的引号效果。不过,这需要您在 HTML 树中将引号元素放置在您希望其呈现的位置。
以下是如何实现同样的偏移引号效果,同时提高灵活性:
.pull-quote{
shape-outside: content-box;
margin-top: 200px;
float: left;
}
我们为形状的坐标系显式设置了 content-box
参照框。在这种情况下,引号内容的量决定了外部内容将围绕的形状。此处使用 margin-top
属性来定位(偏移)引号,无论其在 HTML 树中的位置如何。
形状边距
您会注意到,将内容封装在形状中可能会导致内容与元素过于紧密地摩擦。您可以使用 shape-margin
属性在形状周围添加间距。
.element{
shape-outside: circle(40%);
shape-margin: 1em;
float: left;
}
效果与您使用常规 margin
属性时所见类似,但 shape-margin
仅会影响 shape-outside
值周围的空间。只有在坐标系中有空间的情况下,才会在形状周围添加间距。因此,在上述示例中,圆形半径设置为 40%,而不是 50%。如果将半径设置为 50%,圆形将占据坐标系中的所有空间,而 shape-margin
的效果将没有空间显示。请注意,形状最终会受元素的 margin-box
(元素及其周围的 margin
)的约束。如果形状较大且超出边界,系统会将其剪裁到 margin-box
,最终得到矩形。
请务必注意,shape-margin
仅接受一个正值。它没有长写法。圆形的 shape-margin-top 到底是什么?
为形状添加动画效果
您可以将 CSS 形状与许多其他 CSS 功能(例如过渡和动画)搭配使用。不过,我必须强调的是,如果文本布局在用户阅读时发生变化,他们会感到非常烦恼。如果您决定为形状添加动画效果,请密切关注用户体验。
您可以为 circle()
和 ellipse()
形状的半径和中心添加动画效果,前提是它们的值是浏览器可以插值的值。可以从 circle(30%)
改为 circle(50%)
。不过,在 circle(closest-side)
和 circle(farthest-side)
之间添加动画会导致浏览器卡顿。
.element{
shape-outside: circle(30%);
transition: shape-outside 1s;
float: left;
}
.element:hover{
shape-outside: circle(50%);
}
![动画圆圈的 GIF](https://web.developers.google.cn/static/articles/shapes-getting-started/image/gif-animated-circle-f3bdb9d718fd8.gif?hl=zh-cn)
为 polygon()
形状添加动画时,可以实现更有趣的效果,但请务必注意,多边形在两个动画状态之间必须具有相同数量的顶点。如果您添加或移除顶点,浏览器将无法插值。
一种技巧是添加所需的最大顶点数量,并将它们在您希望形状的边缘感知度较低的动画状态中聚集在一起。
.element{
/* four vertices (looks like rectangle) */
shape-outside: polygon(0 0, 100% 0, 100% 100%, 0 100%);
transition: shape-outside 1s;
}
.element:hover{
/* four vertices, but second and third overlap (looks like triangle) */
shape-outside: polygon(0 0, 100% 50%, 100% 50%, 0 100%);
}
![动画三角形的 GIF](https://web.developers.google.cn/static/articles/shapes-getting-started/image/gif-animated-triangle-daa5ed7de7048.gif?hl=zh-cn)
将内容换行到形状内
![使用 CSS 形状来换行内容的《爱丽丝梦游仙境》演示的屏幕截图](https://web.developers.google.cn/static/articles/shapes-getting-started/image/screenshot-alice-wonder-62a3bd15c33c8.jpg?hl=zh-cn)
CSS Shapes 规范的初始草稿包含 shape-inside
属性,可让您将内容封装在形状内。在 Chrome 和 WebKit 中,甚至有一段时间也实现了该功能。但是,若要将任意位置的内容封装在自定义路径中,则需要付出更多努力并进行更多研究,以涵盖所有可能的情况并避免 bug。因此,shape-inside
属性已推迟到 CSS Shapes Level 2,并且已撤消其实现。
不过,只要您付出一些努力并做出一些妥协,仍然可以实现将内容换行到自定义形状中的效果。解决方法是使用两个浮动元素和 shape-outside
,将其放置在容器的两侧。折衷之处在于,您必须使用一个或两个没有语义意义的空元素,但它们可用作支柱,营造内部有形状的错觉。
<div>
<div class='left-shape'></div>
<div class='right-shape'></div>
Lorem ipsum...
</div>
容器顶部的 .left-shape
和 .right-shape
支撑元素的位置非常重要,因为它们会浮动到左侧和右侧,以便将内容夹在中间。
.left-shape{
shape-outside: polygon(0 0, ...);
float: left;
width: 50%;
height: 100%;
}
.right-shape{
shape-outside: polygon(50% 0, ...);
float: right;
width: 50%;
height: 100%;
}
![插图:Alice 演示中针对 shape-inside 的权宜解决方法](https://web.developers.google.cn/static/articles/shapes-getting-started/image/illustration-workaround-074b19a04e4da.jpg?hl=zh-cn)
这种样式会使两个浮动支柱占据元素内的所有空间,但 shape-outside
属性会为其余内容留出空间。
如果浏览器不支持 CSS 形状,则会将所有内容向下推,从而产生难看的效果。因此,以渐进增强的方式使用该功能非常重要。
在前面的形状动画示例中,您会注意到文本的移动可能会令人烦扰。并非所有用例都需要动画形状。不过,您可以为与 CSS 形状交互的其他属性添加动画,以便在适当的情况下添加效果。
在 CSS 形状的 Alice in Wonderland 演示中,我们使用滚动位置更改了内容的上边距。文本被挤在两个浮动元素之间。当它向下移动时,必须根据两个浮动元素的 shape-outside
重新布局。这会给人一种文字正在深入探究的感觉,从而增强故事讲述体验。是否接近无意义?或许可以。但它看起来很酷。
由于文本布局由浏览器原生完成,因此性能优于使用 JavaScript 解决方案。但是,滚动时更改 margin-top 确实会触发大量重新布局和绘制事件,这可能会明显降低性能。请谨慎使用!不过,在不为 CSS 形状添加动画的情况下使用它们不会明显降低性能。
采用渐进增强的方式
首先假设浏览器不支持 CSS Shapes,然后在检测到该功能时再进行相应处理。Modernizr 是执行功能检测的理想解决方案,“非核心检测”部分中提供了 CSS 形状测试。
某些浏览器通过 @supports
规则在 CSS 中提供功能检测,而无需外部库。Google Chrome 也支持 CSS 形状,并理解 @supports
规则。您可以通过以下方式使用它来逐步增强:
.element{
/* styles for all browsers */
}
@supports (shape-outside: circle(50%)){
/* styles only for browsers which support CSS Shapes */
.element{
shape-outside: circle(50%);
}
}
Lea Verou 撰写了一篇文章,详细介绍了如何使用 CSS @supports 规则。
通过 CSS 排除对象消除歧义
我们今天所说的 CSS 形状在规范的早期被称为 CSS 排除项和形状。这种命名方式的转变可能看起来只是一个细微之处,但实际上非常重要。CSS 排除对象现已成为一项单独的规范,可让您将内容封装在任意放置的元素周围,而无需使用浮动属性。假设您要将内容封装在绝对定位的元素周围;这就是 CSS 排除项的一个用例。CSS 形状只是定义内容将沿着哪条路径换行。
因此,形状和排除对象并不相同,但它们确实是相辅相成的。CSS 形状目前已在浏览器中推出,但 CSS 排除对象尚未实现形状交互。
用于处理 CSS 形状的工具
您可以在传统图片创作工具中创建路径,但在撰写本文时,这些工具都无法导出 CSS 形状值所需的语法。即使可以,这样做也不太实用。
CSS 形状旨在用于浏览器中,在浏览器中,它们会对网页上的其他元素做出响应。直观地了解编辑形状对其周围内容的影响非常有用。以下工具可帮助您完成此工作流程:
Brackets:适用于 Brackets 的 CSS Shapes Editor 扩展程序使用代码编辑器的实时预览模式叠加交互式编辑器,以便修改形状值。
Google Chrome:适用于 Google Chrome 的 CSS Shapes Editor 扩展程序可为浏览器的开发者工具添加控件,以便创建和修改形状。它会在所选元素上方放置一个交互式编辑器。
Google Chrome 中的检查器内置了对突出显示形状的支持。将鼠标悬停在具有 shape-outside
属性的元素上,该元素会亮起以说明其形状。
从图片中提取形状:如果您更倾向于生成图片,并让浏览器从中提取形状,Rebecca Hauck 撰写了一篇不错的 Photoshop 教程。
Polyfill:Google Chrome 是首个发布 CSS Shapes 的主要浏览器。Apple 的 iOS 8 和 Safari 8 即将支持该功能。其他浏览器供应商将来可能会考虑这样做。在此之前,您可以使用 CSS Shapes polyfill 来获得基本支持。
总结
在 Web 中,内容大多被困在简单的框中,CSS Shapes 提供了一种创建富有表现力的布局的方法,缩小了 Web 设计和印刷设计之间的保真度差距。当然,形状可能会被滥用,造成干扰。不过,如果运用得当,形状可以增强内容呈现效果,并以独特的方式吸引用户的注意力。
下面列出了其他设计师的一系列作品(大部分来自印刷媒体),这些作品展示了非矩形布局的有趣用法。希望这能激励您尝试 CSS Shapes 并尝试新的设计理念。
非常感谢 Pearl Chen、Alan Stearns 和 Zoltan Horvath 审核本文并提供宝贵的见解。