CSS浮动特效与文字环绕:深入解析shape-outside属性
CSS浮动特效与文字环绕:深入解析shape-outside属性
CSS中的浮动特效一直以来都被误解为布局工具,而其真正的设计初衷是为了实现文字环绕效果。本文将深入探讨浮动特效的原理,并结合shape-outside属性,展示如何实现复杂的文字环绕效果。
浮动特效的基本概念
使用蒙版和剪切路径来重塑元素外观虽然有趣,但从页面布局的角度看,该元素仍然是一个普通的矩形框。想让基础布局拥有更复杂的形状,就需要用到shape-outside
属性。这会让文本这样的行内内容(inline content)以另外一种方式环绕在该元素周围,不同于以往沿着元素的直角边进行排列。
shape-outside
属性非常适合与遵循相同轮廓的蒙版、剪切路径或圆角半径相结合来实现一些页面特效。此时,页面元素看似有了不同的形状,文字也紧密环绕在相同的形状周围。图1展示了这样一个典型案例:猎鹰图片被剪切成圆形,一旁的文字也围绕在由shape-outside
属性定义的同心圆周围。
【图 1 定义形状会让行内内容围绕该形状的轮廓进行排列】
要使用shape-outside
属性,元素就必须设为浮动(floated)元素。设为浮动元素后,该元素将向页面左右两侧的某一侧靠拢,并让行内内容围绕其外边距盒(margin box)排布。鉴于浮动本身就是个复杂的话题,本节将先行阐述它的基本概念与工作原理,然后再介绍与形状(shapes)相关的知识。
浮动的基本原理
浮动设置会将一个元素(通常为图片)拉到所在容器的左侧或右侧,让文档流环绕在该元素周围。某浮动元素在一段文字内的渲染效果如图2所示。由于这样的布局在报纸和杂志排版中非常常见,CSS也为此添加了浮动这一特效。
【图 2 多行文本环绕在浮动元素周围的效果示意图】
浮动元素会从常规文档流中移除,并被带往所在容器的边缘位置。随后,文档流重新排布,但行内内容会围绕浮动元素当前占据的空间进行排列。浮动元素并没有从文档流中的初始位置向上或向下移动;换句话说,其最终位置取决于该元素在HTML文本中的初始方位。
通过声明float: left
或者float: right
可以将一个元素浮动到指定的方向。如果将多个元素浮动到同一个方向,这些元素就会并排排列,如图3所示。
【图 3 并排排列的两个浮动元素效果图】
下面我们将在示例页中添加一些内容,实现图片浮动到一旁的效果。然后再设置一些剪切特效,让文字排列呈现出某个形状。请根据代码清单1同步更新本地HTML示例页面。您既可以删除<body>
元素中的内容,也可以将示例代码添加到现有内容的下面。
代码清单 1 带浮动图片的示例页 HTML 标记
<div>
<div class="poem-image"><!-- 待浮动的元素 -->
<img src="images/eagle.jpg" alt="Golden Eagle">
</div>
<h1>Hope is the thing with feathers</h1>
<p><cite>Emily Dickinson</cite></p>
<p>
“Hope” is the thing with feathers<br>
That perches in the soul,<br>
And sings the tune without the words,<br>
And never stops at all,
</p>
<p>
And sweetest in the Gale is heard;<br>
And sore must be the storm<br>
That could abash the little bird<br>
That kept so many warm.
</p>
<p>
I’ve heard it in the chillest land,<br>
And on the strangest sea;<br>
Yet, never, in extremity,<br>
It asked a crumb of me.
</p>
</div>
提示
在本例中,<img>
元素被放在了一个<div>
容器中。这并非浮动样式的专属写法。但我发现在某些情况下,在 “普通”(“normal”) 元素上处理形状比在图片元素上更简单。从专业技术的角度看,图片元素在HTML中是一个可替换元素(replaced element);在某些情况下,该元素的尺寸大小及方位可能与普通元素的行为模式略有不同。具体来说,在Chrome浏览器中使用object-position
属性会出现一个Bug,可能会对环绕在该元素附近的文本位置产生不良影响。
接着,我们将设置一些CSS样式来适当调整图片的尺寸,并将其浮动到页面左侧。完成设置后的页面最终效果将如图4所示。
【图 4 将图片浮动到文字左侧的页面效果图】
请将代码清单2中的CSS样式添加到本地样式表,并在浏览器中查看渲染效果。我们在图片上声明了display: block
,这样行高就不会给父级<div>
元素增加额外的高度 —— 尽管在重置样式(reset styles)中我也常常这样声明(详见第8章)。
代码清单 2 将图片元素浮动到页面左侧的示例样式代码
.poem-image {
float: left;
margin-right: 15px;
}
.poem-image > img {
display: block;
height: 350px;
width: 350px;
object-fit: cover;
}
在HTML中,浮动元素在其余行内内容中的位置,对于确定哪些文本行将会环绕该元素而言至关重要。图片上方的文字仍旧在图片上方不变,但与图片同行或者在该行下方渲染的文字将会环绕该浮动元素排列。在本例中,我们希望整首诗歌都围绕在图片的右侧,因此需要将图片放到HTML文档的最前面。
如果对浮动的基本特性不太熟悉,建议您挪一挪该浮动元素来切身感受它的特点。比如将其放到段落中间,看看渲染出的最终位置。如果浮动元素是一个行内元素(inline element),还可以将其放到某个句子中。
浮动元素的另一个有趣、偶尔也会让人猝不及防的行为特征,是它们不会给所在的父元素贡献任何高度。如果一个浮动元素是某个<div>
元素的唯一子元素,则该<div>
的高度将渲染为0。这么做是为了让您可以在某个段落(<p>
)内浮动一张图片时,让后续段落的内容也能继续围绕该图片排列,如图5所示。
【图 5 某容器内的浮动样式会扩展到下一个容器,让两个容器内的文字均围绕该浮动元素排列】
浮动的这一行为特征,不仅在两个容器均为段落元素时是有意义的,且当它们是两个<div>
元素、或者其他任意块级元素(block-level element)时也同样适用。若偏要让另一个元素出现在浮动元素的下方,则需要使用clear: both
;当然除了both
外,也可以使用left
或right
来清除指定方向上的元素浮动。
此外,还可以在容器的::after
伪元素上设置clear
属性来让容器强行包含其浮动的子元素。这样一来容器元素的高度便可以扩展到浮动子元素的底部:
.contain-floats::after {
display: block;
content: "";
clear: both;
}
在CSS引入Flexbox布局或网格布局之前,浮动元素(floats)是构建页面复杂布局的唯一选择。由于这并非浮动样式的设计初衷,因此在实现页面布局的过程中往往存在一定的困难,实现手法也不太优雅;但随着全新布局技术的出现,这样的局面已然成为了历史。人们可以仅仅出于浮动设计的本意来使用浮动,即:当确实需要让文字环绕在某元素周围时,再考虑启用浮动。
定义形状
让示例页中的图片浮动起来后,就可以利用shape-outside
属性为一旁的文字定义形状了。该属性的合法值与clip-path
属性大同小异,都支持:margin-box
、border-box
、padding-box
、content-box
、inset()
、circle()
、ellipse()
以及polygon()
;最显著的区别在于处理SVG路径时的path()
函数。尽管这个值已经纳入CSS规范,但目前在任何浏览器中shape-outside
均不支持属性值为path()
。没准今后就可以用了。
这就意味着,人们通常可以给元素的剪切路径和形状指定完全相同的属性值。一个值得推荐的做法是:将这个值赋给某个自定义属性,然后在设置这两个属性时直接引用这个值。该做法尤其适用于需要定义某个复杂多边形的应用场景中。请根据代码清单3同步更新本地样式表,按照上述思路将这个属性值设为圆形。
代码清单 3 剪切路径和形状皆为圆形时的示例样式代码
.poem-image {
float: left;
margin-right: 15px;
--shape: circle(50%);
clip-path: var(--shape);
shape-outside: var(--shape);
}
.poem-image > img {
display: block;
height: 350px;
width: 350px;
object-fit: cover;
}
这样一来,猎鹰图片按圆形剪切,并且旁边的文字会整机环绕在它周围,最终效果如图6所示(与前面展示的图1相同)。
【图 6 文字同样按圆形环绕排列的效果图】
您也可以将自定义属性--shape
改为其他值,例如polygon()
、ellipse()
或者其他有效取值,然后观察文字对应的环绕情况。如果想让文字紧密环绕浮动元素的圆角半径border-radius
时,也可以考虑以下属性值:margin-box
、border-box
、padding-box
以及content-box
。
另外,鉴于圆形也可以通过设置圆角半径为50%
来实现,因此也可以不通过剪切路径、而改用圆角半径来达到同样的效果,如代码清单4所示。注意,此时需要同时在图片元素(为了剪切图片)和图片所在的<div>
容器上(为了影响shape-outside
属性)设置圆角半径。
代码清单 4 围绕边框圆角定义的形状样式代码
.poem-image {
float: left;
margin-right: 15px;
shape-outside: margin-box; /* 围绕圆角边框轮廓定义的形状 */
border-radius: 50%;
}
.poem-image > img {
display: block;
height: 350px;
width: 350px;
object-fit: cover;
border-radius: 50%;
}
在本例中,我们用了一个右外边距来将图片和环绕文字隔开一小段距离。也可以使用shape-margin
属性,例如声明shape-margin: 15px
将在shape-outside
指定的轮廓周围再增加15px
的外边距。
但是设置的shape-margin
外边距并不会延伸到浮动元素的外边距之外。此时,剪切路径只能到达元素的边缘而非外边距边缘,这并非我们想要的效果。尽管上下两端的文字依然会环绕圆形轮廓排列,但中间的文本环绕的却是由元素外边距盒(margin box)构成的直角边。并且在使用shape-margin
前,最好能确保环绕形状的边缘与元素边缘留有足够的空间才敢放心使用。关于这一点稍后我会用一个案例来演示说明。
译注
改为shape-margin
后的这段描述感觉有点抽象,为便于理解,这里附上我本地实测的效果截图:
【补图:容器样式改为 shape-margin: 15px 后的实际文字环绕效果】
让形状贴合渐变蒙版
shape-outside
属性也可以像蒙版那样对某个图片元素生效。这对渐变特效来说尤为适用,因为这样一来就能将元素沿某个斜边进行剪切并让文字环绕斜边排列,如图7所示。
【图 7 利用线性渐变定义的对角线形状】
在本例中,我们在图片蒙版以及环绕形状上同时设置了相同的线性渐变特效,倾斜角均为65度。试根据代码清单5提供的渐变样式代码同步修改本地样式表。注意,示例代码中并未限制图片的宽度,这样可以给斜边提供更多空间。
代码清单 5 利用渐变特效定义的环绕形状的样式代码
.poem-image {
float: left;
--gradient: linear-gradient( /* 对角线型渐变样式 */
65deg,
black 0 375px,
transparent 375px);
mask: var(--gradient);
shape-outside: var(--gradient);
shape-margin: 15px;
}
.poem-image > img {
display: block;
height: 350px;
object-position: -100px 0; /* 将猎鹰调整到画面中心 */
}
将颜色节点设置在375px
的位置,可以实现从黑色到透明的突变效果。该节点的位置坐标、连同65度的倾斜角,都是在浏览器的开发者工具中微调得到的。接着我又设置了object-position
属性,将猎鹰重新调至画面正中位置,否则会被右侧的环绕形状切掉一部分。建议您在浏览器中多改改这些数值,看看渐变效果会怎样变化。
由于浮动元素向右延伸并超出了环绕形状的边缘,此时改用shape-margin
也是没问题的。此外,也可以使用径向渐变,例如:将自定义属性--gradient
改为radial-gradient(circle at 175px, black 175px, transparent 175px)
将得到一个圆形的剪切路径特效,几乎与代码清单3中演示的circle(50%)
的效果完全相同。
在本例的线性渐变特效中,我们定义了一个从黑色突变到透明色的颜色节点,但这并不是什么固定操作。如果定义的是一个由黑色平滑过渡到透明的渐变特效,则环绕形状的边缘将由不透明度变为0%
时的位置来界定。
您也可以通过shape-image-threshold
属性来动手调整渐变效果对应的边缘。例如,指定shape-image-threshold: 0.3
会让文字紧密环绕在图片不透明度渐变到30%
时所形成的形状周围。您也可以利用该方法实现文字与图片的重叠渲染,让文字环绕在图片仍然依稀可见的位置,效果如图8所示。
【图 8 设置容器的 shape-image-threshold 属性可以定义形状边缘对应的不透明度】
本章演示的页面特效能够让您的页面从网上大量的常见样式设计中脱颖而出。这些功能特性往往更接近人们在报刊杂志或者其他类型的印刷设计中见到的样式效果。随着Flexbox弹性布局和Grid网格布局的出现,利用浮动元素来实现页面布局的做法几乎销声匿迹了,但浮动样式仍然有它的用武之地,尤其是当您想实现某些独具风格且引入注目的页面特效时,情况更是如此。
本章小结
- 滤镜具备的颜色管理功能与模糊特效将会在整个元素范围内生效。
- 背景滤镜的滤镜特效只对透过当前元素背后渲染出来的图片或内容部分生效。
- 蒙版可以根据蒙版图片或蒙版渐变特效中的透明部分有选择地隐藏被遮挡元素的局部区域。
- 剪切路径可以根据其路径定义的形状有选择地隐藏元素的局部区域。该路径通常为圆形、椭圆形或其他自定义的多边形。
- 设置了浮动的页面元素将被拉到所在容器的左侧或右侧,并使得行内内容(inline content)围绕浮动元素边缘排列。
- 浮动元素的文字环绕形状可以通过
shape-outside
属性手动调整。其属性值可以利用元素的剪切路径或者蒙版图片来定义。这样一来,一旁的文字将紧密环绕在元素可见区域形成的轮廓外围。