译者按:在WWDC 2014之前我就打算把这篇文章翻译出来与大家分享;好在WWDC 2014上CSS Shape也露了一脸。快点阅读本文,了解初出茅庐的CSS Shape是何方神圣吧! 方框套方框:这就是目前网页的样子。长久以来,我们努力尝试使用CSS来创建几何形状来突破这种限制。但是这些形状无法影响其包含内容的形状。同样,元素的形状也无法被其他元素所影响。

2012年中旬,由Adobe提出的CSS Shape规范打破了这个限制。该规范的目标是为设计师提供一种新的方式,改变任意复杂形状周围或者内部内容布局。这些事情之情从来没有被实现过,JavaScript也没有。

例如,注意看在下面这个设计中文本环绕圆形图片的样子。没有形状的话,文本变成长方形——抛弃了精致的边缘,这种精致可不是把设计带到了下一个高度。

注意例子中文本是如何环绕圆形的那个碗的。通过CSS Shape,我们可以在网页上创建类似的设计。

接下来我们一起来看看Shape是如何工作,并开始使用它们。

浏览器支持

目前只有Webkit Nightly和Chrome Canary支持CSS Shape,但是它的Model Level 1已经是Candidate Recommendation(候选推荐)了,因此在规范中定义的属性和语法已经很稳定了。看来要不了多久其他浏览器也会实现CSS Shape。本Level的规范主要聚焦在几个Shape属性,这些属性主要规定了形状周围的内容该如何环绕。更具体地说,它主要聚焦在形状外围属性和其相关的属性。

结合其他一些最新的特性,例如Clipping与MaskingCSS FiltersCompositing与Blending,无需借助像Photosho或者InDesign这样 的图像编辑器,CSS Shape允许我们创造更漂亮更精致的设计。

新版的CSS Shape规范也会聚焦形状内部的内容。例如,现在可以很轻松地使用CSS创建一个菱形:只需把元素旋转45度,然后把元素里面的内容旋转回来,相对页面是横向的。但是其形状没法受到其容器菱形的影响,因此还是矩形的。如果CSS Shape的shape-inside属性实现了,我们就可以让内容也是菱形的,要实现下这图这样的布局就没什么不可能了。

很快,CSS Shape将支持定义内部文本的形状,比如这些菱形,与它的容器边缘保持一致,而不溢出或者被截断。

为了在Chrome Canary中使用CSS Shape,你需要开启试验特性标记。如果你不知道怎么打开,可以看看Adobe博客上的这篇参考。

创建CSS Shape

你可以使用Shape属性来给元素添加形状。你需要给Shape属性传递一个Shape函数。你可以给这个Shape函数传递参数来定义元素的形状。

可以使用下面这几个函数来定义形状:

  • circle()
  • ellipse()
  • inset()
  • polygon()

每个形状都由一组坐标定义。有些函数接受坐标点作为参数,另外一些接受偏移量——但是最终它们都把形状当做元素上一系列的点来描绘。在下面的例子中,我们将为大家讲解每个方法所接受的参数。

一个形状还可以通过提取图片的一个Alpha通道来定义。如果把一张图片传递给Shape属性,浏览器就会基于图片的临界值提取形状。形状的定义基于图片上每一个点的Alpha通道是否高于临界值。不过图片必须是CORS compatible的。无论何种原因(比如图片不存在),只要图片不能正常显示,就不会产生形状。

可接受上面这些函数作为值的Shape属性有:

  • shape-outside:限制形状周围的内容
  • shape-inside:限制形状内部的内容

可以把shape-margin和shape-outside属性结合使用,定义形状周围的margin,以此隔开浮动的内容和形状,在形状和内容之间留出更多的空间。与shape-outside和shape-margin对应一样,shape-inside有对应的shape-padding属性,用来添加内间距。

一行代码就可以使用Shape属性和函数来定义一个形状:

.element {
    shape-outside: circle(); /* content will flow around the circle defined on the element */
}

或者:

.element {
    shape-outside: url(path/to/image-with-shape.png);
}

但是。。。

要让这行CSS生效,必须满足两个条件:

  • 元素必须是浮动的。新版的CSS Shape可以循序我们定义一个非浮动元素的形状,但是现在还不行;
  • 元素必须有确定的尺寸。元素的宽度和高度被用来建立这个元素上的坐标系统。 看上面函数的定义,形状都是由一组坐标定义的。因为这些点是坐标,所以需要坐标系统,这样浏览器才知道把这些点放在元素的什么位置上。因此,加上下面这段代码上面的例子就可以正常工作。
    .element {
        float: left;
        height: 10em;
        width: 15em;
        shape-outside: circle();
    }

给定固有的维数并不会影响响应性(随后我们进行更深入的探讨)。

既然形状是由一系列带坐标的点定义的,那更改这些点的坐标就可以相应的更改形状。举个例子,下面的六边形是使用polygon()这个方法创建的。整个形状由六个点构成。更改黄点的横坐标将会影响产生的形状,应用了此形状元素的内部或者外部的内容布局方式也会受到影响。

如果元素右浮动,且应用了这个形状,当在polygon()函数中黄点的横坐标变化的时候,在元素左侧的内容的浮动方式也会改变。

Shape的Reference Box

CSS Shape在一个Reference Box(参考框)里被定义和创建,这个Box用来绘制在元素上的形状。除了元素的宽高之外,元素的和模型——外边界Box、内容Box、内边界Box和边框Box——也会作为元素上形状大小的参考。

默认把外边界Box作为参考——因此,如果你应用了形状的元素的底部有外边界,则形状会延伸到外边jie上,而不是元素的边框区域。如果你想使用其他Box值,你可以在形状函数之后指定,然后传递给Shape属性。

shape-outside: circle(250px at 50% 50%) padding-box;

上面这条规则中的padding-box关键字,把形状限定在了元素的内边框Box中。circle()函数定义了一个环状的形状,包括这个形状的大小和在这个元素上的位置。

使用Shape函数定义Shape

我们从把信息环绕在圆形的头像上开始,我们常常在用户信息或者推荐中用到。

使用CSS Shape,让文本环绕圆形的用户头像。文本将不再是长方形的。

使用circle()函数给头像添加一个圆形:

<img src="http://reqianduan.qiniudn.com/qiniu/1040/image/743a46a4972eab470090ab9c4b75e72b.jpg" alt="profile image" />

<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum itaque nam blanditiis eveniet enim eligendi quae adipisci?</p>

<p>Assumenda blanditiis voluptas tempore porro quibusdam beatae deleniti quod asperiores sapiente dolorem error! Quo nam quasi soluta reprehenderit laudantium optio ipsam ducimus consequatur enim fuga quibusdam mollitia nesciunt modi.</p>

你可能会问,“为什么不使用border-radius来做出图片的圆角?”答案就是border-radius无法影响元素周围或者内部的内容的布局。它只能影响元素的边框和背景。背景区域会被裁剪成圆角,但是它功能仅限于此。元素内部的内容任然是矩形的,而且周围的内容还是把元素当做一个矩形的存在——本质上它还是。

我们使用border-radius让图片变圆——我们通常都是这样让图片等其他元素变圆的。图片bian'yuan

img {
    float: left;
    width: 150px;
    height: 150px;
    border-radius: 50%;
    margin-right: 15px;
}

不添加CSS Shape,文本任然把图片当做矩形的,因此还是围绕成一个矩形,而不是圆的。

在不支持CSS Shape属性的浏览器中,原型图片周围的内容就像环绕在一个不是圆形的图片周围。这就是在较老浏览器中CSS Shape降级的效果。的

为了改变内容布局以适应特定的形状,我们使用Shape属性:

img {
    float: left;
    width: 150px;
    height: 150px;
    border-radius: 50%;

    shape-outside: circle();
    shape-margin: 15px;
}

有了这些代码,文本会看到一个圆的形状在图片上,环绕之,就像第一张截图那样(你可以在这里看到)。在不支持的浏览器中,将降级为第二张图显示的哪样。

你可以像上面那样使用circle()函数,也可以传递参数给它。下面是它的语法:

circle() = circle( []? [at ]? )

问号标示这些参数是可选的。省略的参数将会被浏览器使用默认值补全。如果你直接使用circle()而未指定圆形的位置,它会定义一个放在元素正中间的圆形。

你可以指定圆形的半径,什么单位都可以(px、em、pt等等)。你甚至可以指定使用closest-side或者furthest-side作为半径,closest-side是默认值,即浏览器会把从中点到最近边的长度作为圆形的半径。furthest-side则是使用中心到最远边的距离。

shape-outside: circle(farthest-side at 25% 25%); /* defines a circle whose radius is half the length of the longest side, positioned at the point of coordinates 25% 25% on the element’s coordinate system*/

shape-inside: circle(250px at 500px 300px); /* defines a circle whose center is positioned at 500px horizontally and 300px vertically, with a radius of 250px */

eclipse()函数与circle()还是函数是一样,除了接受两个半径参数以外(后者是一个参数列表)——一个是x轴的半径,一个是y轴的——是一直的。

ellipse() = ellipse( [{2}]? [at ]? )

与circle和eclipse没有直接的关系,inset()函数再元素内部创建一个矩形。既然元素本身就是矩形的,我们当然不需要更多的矩形。inset()实际上可以帮助我们在元素内部创建一个带有圆角的矩形。文本内容可以环绕在圆角周围。

inset()函数接受1到4个值指定从参考Box边缘向内的距离。这可以控制这个矩形在元素内部的位置。这个函数还接受一个可选参数,设置内部矩形的圆角。且圆角的设置于border-radius的方法一致,使用一到四个值,于关键字round结合在一起。

inset() = inset( offset{1,4} [round ]? )

在一个浮动的元素中创建一个带圆角的矩形:

.element {
    float: left;
    width: 250px;
    height: 150px;
    shape-outside: inset(0px round 100px) border-box;
}

在这里查看实际的例子。

最后一个Shape函数是polygon(),它可以是用任意数量的点来定义更加复杂的形状。这个函数接受一系列的坐标,每个坐标定义多边形的一个边角,合在一起组成整个图形。

在下面的例子中,一张右浮动的图片使用viewport单位,占满了整个屏幕。我们希望左侧的文本可以沿着图片内部的沙漏浮动,因此,我们使用polygon()函数在图片上定义了一个不规则的的图形。

图片的CSS:

img.right {
    float: right;
    height: 100vh;
    width: calc(100vh + 100vh/4);
    shape-outside: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%);
}

你可以使用长度单位或者百分比来定义边角的坐标,我使用的是百分比。

这段代码就是显示出图片的效果,你可以看到,Shape函数无法影响形状外的图片。事实上,一个形状除了影响内容的浮动之外无法对元素造成其他任何影响,不管这个元素是图片还是容器或者其他的什么。

为了让我们创建的多边形更有存在感,我们需要把形状之外的图片抠掉。这就需要CSS Masking模块的clip-path属性来帮忙了。

clip-path属性接受同样的Shape函数和参数座位形状属性。如果我们把传递给shape-outside的多边形传递给clip-path属性,它会把形状之外的图片全部抠掉。

img.right {
    float: right;
    height: 100vh;
    width: calc(100vh + 100vh/4);
    shape-outside: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%);
    /* clip the image to the defined shape */
    clip-path: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%);
}

看效果:

目前,使用clip-path还需要使用前缀,这个例子clip-path使用了-webkit-前缀,因此可以在Chrome中工作,你点击查看demo

clip-path属性确实是Shape属性的好伙伴,因为它可以帮助凸显被创建的形状,抠掉元素上所有不再形状内部的部分。你自然会经常发现把clip-path个Shape属性结合使用。

polygon()函数还接受一个可以可选的参数fill,有两个值,nonzero和even odd。指明如何对待多边形自己内部的交错区域。更多信息可以查看SVG fill-rule

使用图片定义Shape

使用图片来确定一个形状,需要用到这张图片的alpha通道,浏览器可以据此提取出图形。

像素的alpha通道的值是否超过某个临界值,这是定义形状的依据。默认的临界值是0.0(完全透明),你也可以通过shape-image-threshold属性来定义。因此,所有不透明的像素点都是形状的一部分。

有可能,新版的CSS Shape不但可以使用alpha通道,还可以使用亮度。果真如此的话,shape-image-threhold将被扩展,支持亮度或者alpha通道,基于不同状态间的切换。

我们将使用下面这张图,定义元素的形状,让文本环绕之:

使用shape-outside属性,传递一个url()值,指向我们想要的图片。

.leaf-shaped-element {
    float: left;
    width: 400px;
    height: 400px;
    shape-outside: url(leaf.png);
    shape-margin: 15px;
    shape-image-threshold: 0.5;
    background: #009966 url(path/to/background-image.jpg);
    mask-image: url(leaf.png);
}

当然,如果元素有背景图片的话,需要把形状之外的部分去掉。不过clip-path无法接受一张带透明度的图片座位参数,我们 可以使用Masking Module的mask-image(需要对应的前缀)属性来实现。看看效果:

如果你需要创建一个复杂的形状,推荐你使用图片来定义。你可以通过Photoshop来定义alpha通道,比起手动指定定点方便多了。

还有,如果你元素上又多个浮动或者多个被排除的区域,你更愿意使用图片而不是形状。因为,至少是现在,你无法在一个元素上定义多个形状。但是如果图片包含多个区域,浏览器可以把这些区域提取出来并使用之。

响应式设计中的CSS Shape

CSS Shape可以适应响应式布局么?在目前的规范中,shape-outside已经可以满足了。因为它允许你使用百分比或者其他长度单位来指定元素的尺寸,而且形状也可以使用百分比(即形状函数的参数接收百分比)来定义。这意味着带着shape-outside的元素可以是响应式的,与其他使用百分比尺寸实现伸缩的方式没什么区别。

不过shape-inside还不是响应式的,原因是它被推迟到了Module Level 2中。当前版本中不尽人意的地方将在下一版本中解决。

Shape工具

使用Shape函数创建复杂形状令人生畏,尤其是使用ploygon()使用很多点很多左边来创建形状。这要感谢Adobe的Web平台开发组正在开发一个可交互的工具简化这个工作。Bear Travis已经创建了一组Shape工具,帮助我们可视化地创建形状,并帮助我们生成Shape函数,这很有用,但是它也有局限,在你想基于一张图来创建形状时,因为目前你没法往工具中插入一张图,然后为它创建一个形状。

Adobe Web平台开发组开发出了一个更高级,交互式的Shape工具。这个工具最近作为Brackets编辑器的一个插件公布出来,Brackets编辑器是Adobe团队打造的一个免费编辑器。它允许你直接在浏览器中可视化地编辑图形,还包含一个预览版的功能——在你在页面上编辑图形的同时,更新编辑器中的样式表。这给你提供了直接视觉反馈,可以让你看到你的形状是如何与页面上的其他元素交互的。

使用Brackets的预览模式,在右边的浏览器中编辑多边形。截图由Razvan Caliman提供。

这个工具不可或缺,因为它大大简化了形状的创建、编辑和调试。 Razvan Caliman再Brackets的博客上发布了一篇文章,说明如何再Brackets中使用Shape编辑器。

未来:CSS Exclusion

CSS Shape规范实际上包括CSS Shape和Exclusion两个草案,不过这两个草案是分开的。CSS Shape定义shape-inside和shape-outside属性,而CSS Exclusion则定义那些允许文本环绕在非浮动元素周围的属性。这些元素可能是绝对定位的元素。这使得内容从四面八方环绕真个形状成为可能,就如下面这张图所示:

将来,CSS Exclusion允许我们像引文一样让文本环绕着形状,如这张杂志的布局所示。引文甚至可以是圆形的,文本也可以很好的环绕在它周围。图片来自Justin Thomas Kay

类似的布局,具有形状的元素绝对定位在文本在中间——四面都有文本环绕——将来也没什么不可能。

将来的CSS Shape

目前的CSS Shape规范只是第一步,很快,会有新的选项,我们有更多的控制,创建形状,让文本填充在形状里面,或者环绕在其四周。让模型到活的设计更加方便,只需要简单的几行代码就行。目前,草案编辑把注意力放在发布shape-outside上,到2014年底,你将会看到有更多的浏览器支持CSS Shape。

现在你就可以使用CSS Shape,作为渐进增强的一部分,确保在不支持的浏览器中可以优雅的降级。我最近开始把它们用在我自己的网站上,降级很”正常”。对于更加复杂的设计,你可以使用脚本来检测浏览器是否支持Shape,提供降级方案。你也可以使用这段脚本扩展Modernizr的测试集,来测试浏览器是否支持shape-outside,也可以直接下载一份自定义的包含了这个测试的版本。

CSS Shape填平了印刷排版和Web设计之间的沟壑。本文中的示例都很简单,不过你应该学到的这些基础,有助于你实现像杂志或者海报一样复杂的设计——无需担心你还需要针对屏幕重新设计。无论你在寻找什么——从非矩形布局Shape动画——马上开始实验吧。

原文:CSS Shapes 101 · An A List Apart Article