原文:CSS Mastery
五、漂亮的盒子
在前面的章节中,你学习了 HTML 文档的每个元素都是由矩形框组成的:从保存页面结构部分的容器到段落中的文本行。然后,在最后一章中,您学习了如何设计页面文本内容的样式。
如果我们不能增强这些盒子的外观,或者用颜色、形状和图像来补充它们,网页设计就不会有创造性或灵活性。这就是背景、阴影和边框的 CSS 属性,以及通过 img 元素的内容图像和其他嵌入对象的来源。
在本章中,您将了解
-
背景颜色和不同种类的不透明度
-
使用背景图像和不同的图像格式
-
使用 calc()函数对长度进行数学计算
-
给你的盒子添加阴影效果
-
使用简单和高级边框效果
-
用 CSS 生成渐变
-
设置内容图像和其他嵌入对象的样式和大小
背景颜色
我们将从一个非常基本的例子开始,向整个页面的背景添加一种颜色。下面的代码将我们的背景设置为柔和的绿色:
body {
background-color: #bada55;
}
我们还可以使用 shorter background 属性设置背景颜色:
body {
background: #bada55;
}
这两个属性有什么区别?第二个是 background,它是一个简写属性,允许您同时设置许多其他与背景相关的属性。在前面的例子中,我们只在速记中声明了一个背景颜色,但是其他值(对于背景图像)也会受到影响——它们被重置为默认值。这可能会无意中覆盖您已经明确设置的内容,所以要小心使用它——我们将在本章的后面详细讨论它。
颜色值和不透明度
在前面的颜色示例中,我们用十六进制的符号设置值:一个哈希字符(也称为八位数、英镑符号或数字符号)后跟一个六字符的字符串。该字符串由三组两个字符组成,每组字符的范围为 0 到 F。十六进制意味着每个“数字”可以有 16 个不同的值,因此 0-9 由代表第 11 到第 16 个值的 A-F 来补充:
0123456789ABCDEF
这三对代表颜色的红色、绿色和蓝色(RGB)值。每个颜色通道有 256 个不同的可能值,因此每个颜色通道有两个字符(16 × 16 = 256)。
所有三对颜色在两个地方都有相同的值,允许缩短为三个字符:#aabbcc 变成#abc,#663399 变成#639,依此类推。
小费
您也可以使用许多可用的颜色关键字之一来指定颜色,如红色、黑色、蓝绿色、一枝黄花或暗海绿色。有一些非常奇怪的颜色关键字——它们源于一个名为 X11 的旧图形系统,开发人员依次从一盒蜡笔中选择一些颜色关键字!
很难找到任何好的理由来解释你为什么要使用这些关键字——除了可能为了调试的目的想快速地想出一种颜色。我们将通过使用更精确的方法向前推进。
设置 rgb 值可以用另一种方式完成,使用 RGB()函数符号。RGB 的每个值可以表示为数字(从 0 到 255)或百分比(0%到 100%)。以下是上一节中使用 rgb()符号的示例:
body {
background-color: rgb(186, 218, 85);
}
十六进制和 rgb()符号从 CSS 1 开始就存在了。最近,我们有了一些处理颜色的新方法:hsl()、rgba()和 hsla()。
首先,有 hsl()函数符号。十六进制和 RGB 表示法都是指计算机如何利用颜色在屏幕上显示它们——红、绿、蓝的混合。hsl()符号是指使用色调-饱和度-亮度(HSL)模型描述颜色的不同方式。色调从一个假设的色轮中获得其值(见图 5-1 ),其中颜色根据您选择的度数逐渐相互转换:红色在顶部(0 度),绿色在周围的三分之一处(120 度),蓝色在三分之二处(240 度)。
图 5-1。HSL 色轮
如果你使用过任何类型的图形设计软件,你可能在颜色选择器中看到过一个色轮。要使用 hsl()语法,您需要向它传递表示您想要选择的圆的角度的度数,以及两个百分比。这两个百分比首先代表你想要在颜色混合中使用的“颜料”(饱和度)的数量,然后是亮度。下面是如何用 hsl()符号编写前面的代码:
.box {
background-color: hsl(74, 64%, 59%);
}
重要的是要注意,选择这两种方法中的任何一种来书写颜色值都没有本质上的区别:它们只是表示同一事物的不同方式。
下一个新的颜色符号是 RGB 的 turbo-powered 版本,称为 rgba()。“a”代表 alpha ,控制透明度的是 alpha 通道。如果我们想要和上一个例子一样的基本背景色,但是现在想要它有 50%的透明度,我们可以用下面的例子:
.box {
background-color: rgba(186, 218, 85, 0.5);
}
rgba()函数参数中的第四个值是 alpha 值,它是一个介于 1.0(完全不透明)和 0(完全透明)之间的值。
最后,还有 hsla()符号。它与 hsl()的关系与 rgb()与 rgba()的关系相同:您为 alpha 通道传递一个额外的值来选择颜色的透明度。
.box {
background-color: hsla(74, 64%, 59%, 0.5);
}
现在你知道了如何让颜色变得或多或少的透明,需要注意的是,在 CSS 中还有另外一种控制透明度的方法。这可以通过不透明度属性来实现:
.box {
background-color: #bada55;
opacity: 0.5;
}
这会让我们。框元素的颜色和透明度与上一个示例相同。那么这里有什么不同呢?在前面的例子中,我们只将背景色设为透明,但是在这里我们将整个元素设为透明,包括其中的任何内容。当一个元素使用 opacity 设置为透明时,不可能使其中的子元素变得不透明。
实际上,这意味着具有透明度的颜色值非常适合制作半透明的背景或文本,而较低的不透明度会使整个元素淡出。
警告
注意文字和背景颜色的对比!虽然这本书不是关于设计理论本身,但我们想强调的是,网页设计是关于你的用户能够接受你创建的网页上的信息。背景和文本颜色对比选择不当会影响在阳光下用手机访问你的网站的人,屏幕不好的人,视力受损的人等等。关于色彩对比的一个很好的资源是在 http://contrastrebellion.com/的现场对比反叛。
背景图像基础
添加背景色是创建更有趣页面的好工具。有时我们想更进一步,在我们的元素上使用图像作为背景,无论是微妙的图案,解释用户界面的象形图,还是给页面增加一些额外字符的更大的背景图形(见图 5-2 )。CSS 有很多工具可以做到这一点。
图 5-2。https://teamtreehouse.com 上的博客使用了褪色和彩色的背景图片
背景图像与内容图像
首先:什么时候图像是背景图像?您可能知道有一个专门用于向网站添加内容图片的 HTML 元素:img 元素。我们如何决定在 CSS 中什么时候使用 img,什么时候使用背景图片?
简单的答案是,任何可以从网站上删除但仍然有意义的东西都应该作为背景图片来应用。或者换句话说:如果网站具有完全不同的外观和感觉,任何仍然有意义的东西都应该是内容图像。
可能会出现界限不清的情况,最终你为了达到特定的视觉效果而变通了规则。请记住,任何来自 img 元素的内容图片,如果纯粹是为了装饰你的网站,可能会出现在其他地方,在那里你的内容最好不要被打扰:例如,在 feed 阅读器和搜索结果中。
使用背景图像的简单示例
想象一下,我们正在设计一个页面,类似于 Twitter 或脸书等社交网站的个人资料页面上的大标题(见图 5-3 )。
图 5-3。上一页简介https://twitter.com
相反,我们的页面将是一个猫的社交网络,在这一章中,我们将使用各种属性来创建一个类似图 5-4 的头部组件。
图 5-4。带有文本和个人资料图片的巨型标题图像和个人资料框
首先,我们将添加一个默认的蓝灰色背景颜色和一个背景图像,以及一些尺寸到页面的大标题。如果图像加载失败,添加默认背景色很重要:
.profile-box {
width: 100%;
height: 600px;
background-color: #8Da9cf;
background-image: url(img/big-cat.jpg);
}
该组件的 HTML 可能如下所示:
<header class="profile-box">
</header>
这样做的结果可以在图 5-5 中看到:我们的图像被加载并平铺在整个轮廓框中。
图 5-5。背景图像沿两个方向平铺在轮廓框上
为什么它像那样平铺在整个盒子上?因为默认值的另一个属性与背景图像有关,名为 background-repeat。默认值 repeat 表示图像在 x 轴和 y 轴上重复。这对于包含图案的背景来说非常有用,但对于照片来说可能不是。我们可以通过将值设置为 repeat-x 或 repeat-y 来将其限制为任意方向,但现在我们将通过将其设置为 no-repeat 来完全移除平铺效果:
.profile-box {
background-image: url(img/cat.jpg);
background-repeat: no-repeat;
}
3 级背景和边框规范使用扩展的语法和新的关键字重新定义了该功能。首先,它允许您指定两个方向的重复值,用空格分隔关键字,因此下面的内容相当于设置 repeat-x:
.profile-box {
background-repeat: repeat no-repeat;
}
其次,它定义了一些新的关键字。在支持的浏览器中,您可以将 space 或 round 设置为一个或两个关键字。使用空间意味着,如果背景图像两次或更多次适合元素内部(不进行裁剪或调整大小),它将重复适合的次数并隔开,以便背景图像的第一个和最后一个“副本”接触元素的边缘。使用 round 意味着将调整图像的大小,使其多次适合元素内部。
老实说,这些新的背景重复功能可能不是非常有用。如果你想使用一个符号或重复的图案作为背景,并想在设计中保持某种对称,它们会很方便,但它们也很难保持图像的纵横比。支持也参差不齐:老版本的浏览器被遗漏了,但是即使是现代版本的 Firefox 也缺少支持。
加载图像(和其他文件)
在使用 url()函数符号时,就像我们在前面的例子中所做的那样,我们可以使用一个相对 URL——例如 url(img/cat.jpg)。浏览器将尝试在 img 子目录中查找与保存 CSS 本身的文件相关的文件 cat.jpg。如果路径以斜杠“/img/cat.jpg”开头,浏览器将在顶级 img 目录中查找图像,该目录与加载 CSS 文件的域相关。
我们也可以使用绝对 URL。绝对 URL 的一个例子是,如果我们精确地指定哪种协议、域和路径的组合指向图像,就像example.com/img/my-background.jpg
。
除了绝对和相对 URL,我们可以选择加载图像(和其他资源)而不指向任何文件,而是将数据直接嵌入样式表中。这是通过一种叫做数据 URI 的东西来完成的,在这里,文件中的二进制编码数据被转换成一长串文本。有很多工具可以帮你做到这一点,包括在线版本,如 http://duri.me/的。
然后,您可以将该文本粘贴到 url()函数中,并将数据保存为样式表的一部分。它看起来像这样:
.egg {
background-image:
url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC gAAAAoAQAAAACkhYXAAAAAjElEQVR4AWP…
/* ...and so on, random (?) data for a long time.. */
...4DwIMtzFJs99p9xkOXfsddZ/hlhiY/AYib1vsSbdn+P9vf/1/hv8//oBIIICRz///r3sPMqHsPcN9MLvn1s6SfIbbUWFl74HkdTB5rWw/w51nN8vzIbrgJDuI/PMTRP7+ByK//68HkeUg8v3//WjkWwj5G0R++w5WyV8P1gsxB2EmwhYAgeerNiRVNyEAAAAASUVORK5CYII=);
}
带数据的起始位:image/png;base64 告诉浏览器应该得到什么样的数据,其余的是转换成字符串的图像的实际像素数据。
使用嵌入式数据 URIs 有好有坏——使用它们的主要原因是为了减少 HTTP 请求的数量,但同时它们会增加样式表的大小,所以要谨慎使用。
图像格式
您可以在网上使用几种不同格式的图像文件,它们都可以作为内容图像或背景图像。这里有一个简短的介绍:
-
JPEG: 一种位图格式,可以高度压缩,但在细节上有一些质量损失,适合照片。不支持透明。
-
PNG: 一种无损压缩的位图格式,这种格式不适合照片(它会创建非常大的文件),但对于图标或插图之类的“扁平”图形,它可以实现非常小的文件大小。可以是透明的。
-
GIF: 一种旧的位图格式,类似于 PNG,主要用于猫的动画图片。说真的,除了动画图像之外,它在很大程度上已经被 PNG 所取代:PNG 也支持动画图像,但浏览器支持有点落后。GIF 支持透明度,但不支持 alpha 级别,因此边缘看起来经常呈锯齿状。
-
SVG: 一种矢量图形格式,也是它自己的标记语言。SVG 既可以直接嵌入网页,也可以作为背景图像或内容图像的来源。
-
WebP: 由 Google 开发的一种新格式,压缩效率极高,结合了 JPEG(高度可压缩)和 PNG(透明)的特点。到目前为止,浏览器支持非常不稳定(只有 Chrome 和 Opera 等基于 Blink 的浏览器),但这可能会很快改变。
除了 SVG 之外,所有这些都是位图格式,这意味着它们包含逐像素的数据,并且具有固有的尺寸(意味着“内置”的宽度和高度)。对于细节层次高的图形元素,如照片或详细的插图,这是有意义的。但是对于许多用途来说,真正有趣的格式是 SVG,它包含如何在屏幕上绘制特定形状的指令。这使得 SVG 图像可以自由调整大小或以任何像素密度显示在屏幕上:它们永远不会失去任何清晰度或细节水平。
SVG 本身是一个足以写几本书的主题(事实上,有许多这样的书存在),但是我们仍然希望在本书中让您对 SVG 的灵活性有所了解(特别是在第十一章,当我们看到 CSS 中一些更前沿的视觉效果时)。SVG 是一种古老的格式(它从 1999 年就出现了),但是近年来浏览器支持变得足够广泛,使得 SVG 成为一种可行的替代方案。唯一坚持的是有点古老的 Internet Explorer 版本(版本 8 和更早版本)和 Android 上的 WebKit browers 的早期版本(版本 2 和更早版本)。
背景图像语法
回到图 5-5 ,我们开始创建带有 JPEG 格式背景图像的个人资料页面示例,因为它是一张照片。到目前为止,我们已经把它放在了元素的背景中,但是它看起来还不是很好。我们将讨论让您调整背景图像的属性。
背景位置
我们可以试着把我们的图像放在元素的中心。背景图像的位置由 background-position 属性控制。
我们还使用了更大版本的图像文件,以确保它即使在更大的屏幕上也能覆盖元素(见图 5-6 )。在较小的屏幕上,边会被剪掉,但至少图像是居中的。
图 5-6。我们的页面有一个更大的,居中的背景图片来覆盖整个元素
.profile-box {
width: 100%;
height: 600px;
background-color: #8Da9cf;
background-image: url(**img/big-cat.jpg**);
background-repeat: no-repeat;
**background-position: 50% 50%;**
}
您可以使用关键字或单位(如像素、ems 或百分比)来设置背景位置属性值。最简单的形式是,该值由两个子值组成:一个表示从左侧的偏移量,一个表示从顶部的偏移量。
注意
有些浏览器支持 background-position-x 和 background-position-y 属性,这两个属性分别在每个轴上定位图像。这些最初是 IE 中的非标准属性,但是正在被标准化。在撰写本文时,基于 Mozilla 的浏览器仍然不支持它们。
如果使用 pixels 或 ems 设置这些值,图像的左上角将从元素的左上角开始按指定的像素数定位。因此,如果您指定 20 个像素的垂直和水平位置,图像的左上角将出现在距离元素左边缘 20 个像素和距离元素上边缘 20 个像素的位置。使用百分比的背景定位略有不同。百分比定位不是定位背景图像的左上角,而是使用图像上的相应点。如果您将垂直和水平位置设置为 20%,那么您实际上是将一个点定位在距离图像的顶部和左侧边缘 20%的位置,距离父元素的顶部和左侧边缘 20%的位置(参见图 5-7 )。
图 5-7。当使用像素定位背景图像时,使用图像的左上角。使用百分比定位时,会使用图像上的相应位置
关键字对齐的工作方式是将 x 轴和 y 轴的一个或两个测量值替换为 x 轴的左侧、中间或右侧,或者 y 轴的顶部、中间或底部。你应该养成这样的习惯,总是先声明 x,再声明 y。这是为了一致性和可读性,也是为了避免错误:规范允许你改变顺序,如果你使用两个关键字(比如左上),但是当一个是关键字,一个是长度时就不允许了。以下内容将被破坏:
.box {
background-position: 50% left; /* don’t do this */
}
背景定位的约束已经困扰设计师很久了。考虑图 5-8 中的设计:我们有一些未知长度的文本,在最右边有一个图标图像,周围有一些空白。使用像素或 ems 来定位图像是没有用的,因为我们不知道图像应该位于离左边多远的地方。
图 5-8。右边缘带有图标作为背景图像的一段文本
以前,除了为图标提供自己的包装元素和定位之外,唯一的解决方案是使用一个背景图像,将其定位在距离左边 100%的位置,并将右边的空白作为透明像素烘焙到图像文件中。这不是很优雅,因为它没有让我们通过 CSS 来控制这些空白。幸运的是,3 级背景和边框规范支持我们!
背景位置的新语法允许我们做我们所希望的事情,就像刚才描述的那样:我们可以用相应的 edge 关键字作为每个距离的前缀,我们想用它作为参考。看起来是这样的:
<p>
<a href="/activate" class="link-with-icon">Activate flux capacitor</a>
</p>
.link-with-icon {
padding-right: 2em;
background-image: url(img/icon.png);
background-repeat: no-repeat;
background-position: **right 1em top 50%**;
}
前面的例子意味着我们将图像定位在距离右边缘 1 em,距离顶部 50%的位置。问题解决了!可悲的是,这个版本的语法在 IE8 或 7 版之前的 Safari 中不起作用。根据您的使用情况,它可以作为一种增强,但在不受支持的浏览器中很难优雅地降级。
介绍 Calc
实际上,我们可以通过引入另一个具有更广泛支持的 CSS 构造(calc()函数符号)来获得与上一节中的示例相同的结果。使用 calc 可以让浏览器为你计算任何类型的数字(角度、像素、百分比等)。).它甚至可以处理混合单元,直到页面被渲染才知道!这意味着您可以说“100% + x 像素数”,例如,这对于以百分比表示的大小或位置与以 ems 或像素设置的其他距离相冲突的任何情况都非常有用。
在我们之前讨论的“背景图像从右侧定位”问题的情况下,我们可以使用 calc()符号来表示 x 轴上的相同位置:
.link-with-icon {
/* other properties omitted for brevity. */
background-position: **calc(100% - 1em) 50%**;
}
注意
Internet Explorer 9 确实支持 calc()符号,但遗憾的是,当它专门用于背景位置时,有一个严重的错误,导致浏览器崩溃。因此,前面的例子主要是理论上的。calc()函数对于许多其他情况也很有用——元素大小、字体大小等等。
calc()函数符号与加法(+)、减法(-)、乘法(*)和除法(/)这四个运算符一起使用。calc()表达式中可以有多个值;以下规则集中的声明也完全有效:
.thing {
width: calc(50% + 20px*4 - 1em);
}
注意
使用 calc()时,在使用加法和减法时,运算符两边需要有空格。这显然是为了更清楚地将运算符与数字上的任何符号区分开来,例如长度-10 px。
calc()符号是在 Level 3 值和单位规范中定义的,它有相当不错的支持。就像你之前看到的“四值”背景位置一样,IE8 和更早的版本,以及更老的 WebKit 浏览器,正在失去乐趣。一些稍旧版本的基于 WebKit 的浏览器确实支持它,但可能需要一个前缀,形式为-webkit-calc()。
背景剪辑和原点
默认情况下,用于背景的图像将绘制在元素的边框上,这意味着它们可能会覆盖元素的所有可见边缘。请注意,由于它们被绘制在任何边框的下面,半透明的边框可能会显示在图像的顶部。
背景剪辑属性可以改变这种行为。默认对应于背景剪辑:边框框。设置值 padding-box 将切换到裁剪边框内的图像,覆盖填充框,设置值 content-box 将裁剪任何填充内的图像,到内容框。图 5-9 显示了不同之处。
图 5-9。剪裁到边框(左)、填充框(中)和内容框(右)的背景之间的差异
.profile-box {
border: 10px solid rgba(220, 220, 160, 0.5);
padding: 10px;
background-image: url(img/cat.jpg);
**background-clip: padding-box;**
}
即使更改了背景剪辑值,背景位置的默认原点(即图像开始定位的参考点)仍然是填充框的左上角,这意味着定位值从元素上任何边框的内部开始。
幸运的是,您也可以通过背景-原点属性来影响原点位置。它接受与背景剪辑相同的与框模型相关的值:边框、填充框或内容框。
背景剪辑和背景源都是前面提到的 3 级背景和边界规范的一部分。它们已经存在了一段时间,但仍然缺乏在真正老的浏览器中的支持:再次,IE8 是主要的落后者,但这次即使是老的 Android 浏览器也实现了这些属性,尽管带有-webkit-前缀。
背景附件
背景附加到显示在后面的元素上。如果你滚动页面,背景也随之滚动。可以通过 background-attachment 属性来更改此行为。如果我们希望标题图像的背景在用户向下滚动时“粘”在页面上,我们可以使用下面的代码:
.profile-box {
background-attachment: fixed;
}
图 5-10 试图捕捉用户滚动页面时背景的行为:它给出了页眉隐藏在页面其余部分后面的外观,这可能是一个很酷的效果。
图 5-10。带有固定背景附件的个人资料标题
除了固定和默认值,滚动,你可以设置背景附件到本地。很难在纸上说明,但是局部值影响元素滚动位置内的附件:当元素有滚动条时,它通过将溢出属性设置为 auto 或 scroll 并使内容足够高以突出元素,使元素内容一起滚动。如果我们在标题上这样做,那么当页面滚动时,背景图像将随着元素一起滚动,但是当内部滚动位置改变时,背景图像也会随着内容一起滚动。
本地值在桌面浏览器中得到相对较好的支持,但在移动浏览器中却不太可靠:有理由认为一些移动浏览器制造商忽略了这一属性(以及固定值),因为元素滚动是不常见的,并且会对使用触摸滚动的小屏幕产生可用性影响。事实上,该规范还允许实现者忽略背景附件,如果它被认为在设备上不合适的话。移动浏览器专家彼得-保罗·科赫(Peter-Paul Koch)在他的网站 QuirksMode.org(http://www . quirksmode . org/blog/archives/2013/03/new _ CSS _ tests _ c _ 2 . html)上有一篇关于这个主题的文章(以及其他移动浏览器测试的宝库)。
背景尺寸
在上一节的例子中,我们使用了一个更大的图像来覆盖轮廓框。这意味着在较小的浏览器窗口中查看时,它会被剪切。当窗户变得很大时,它的侧面也可能会有缝隙。假设我们希望防止这种情况,并在缩放页面时保持内容的纵横比,我们需要利用 background-size 属性。
通过将 background-size 设置为显式长度度量,您可以将背景图像的大小调整为新的固定度量,或者让它随元素一起缩放。
如果我们仍然有一个大文件,并且出于某种原因想要显示得小一些,我们可以给它新的像素度量:
.profile-box {
background-size: 400px 240px;
}
让图像和框一起缩放意味着我们需要切换到使用百分比。您可以设置宽度和高度的百分比,但是这些百分比与图像的固有大小无关,而是与容器的大小有关:如果容器的高度随内容而变化,这可能会扭曲图像的纵横比。
使用百分比的一个更明智的方法是对一个值使用 percent,对另一个值使用关键字 auto。例如,如果我们希望图像的宽度为 100%(x 轴,第一个值),并保持其纵横比(见图 5-11 ),我们可以使用以下方法:
图 5-11。使用百分比和 auto 关键字设置背景大小允许背景覆盖元素的宽度,而不管视口大小如何
.profile-box {
background-size: **100% auto**;
}
使用百分比给了我们一些灵活性,但并不适用于所有情况。有时,我们可能希望确保背景永远不会被裁剪,在 profile header 示例中,我们可能希望确保背景总是覆盖元素的整个区域。幸运的是,有一些神奇的关键字为我们解决了这个问题。
首先,我们可以使用关键字 contain 作为背景尺寸。这意味着浏览器将试图在不扭曲其纵横比或裁剪图像的情况下尽可能放大图像:这几乎与上一个示例类似,但它会自动确定哪个值应该是 auto,哪个值应该是 100%(见图 5-12 )。
图 5-12。使用 contain 关键字作为背景大小可以防止裁剪
.profile-box {
background-size: contain;
}
在又高又窄的元素中,正方形背景最多 100%宽,但可以留有垂直间隙;在宽元素中,它最多 100%高,但会留下水平间隙。
我们可以使用的第二个关键字值是 cover:这意味着图像的大小可以完全覆盖元素的每个像素,而不会扭曲图像。这就是我们想要在我们的个人资料页面的例子。图 5-13 显示了一个窄而高的元素上的正方形背景如何填充高度但剪裁侧面,一个宽的元素如何在填充元素宽度的同时剪裁顶部和底部,配置如下:
图 5-13。使用 cover 关键字在裁剪背景时完全覆盖元素的表面
.profile-box {
background-size: cover;
}
与 clip 和 origin 的属性一样,background-size 是一个相对较新的背景属性,支持级别也类似。
背景速记
正如我们在本章开始时看到的,有一个后台简写语法,用于同时设置许多与后台相关的属性。一般来说,你可以随意指定不同的值——浏览器会从不同的关键字和语法中判断出你的意思。不过,还是有一些问题。
首先,由于长度对可用于背景位置和背景大小,您需要将它们写在一起,首先是背景位置,然后是背景大小,并用斜杠(/)字符分隔它们。
第二个是用于背景源和背景剪辑的*-box 关键字。以下规则适用:
-
如果只有一个*-box 关键字(border-box、padding-box 或 content-box),则两个值都被设置为声明的值。
-
如果有两个*-box 关键字,第一个设置背景-原点,第二个设置背景-剪辑。
下面是一个将各种背景属性组合在一起的例子:
.profile-box {
background: url(img/cat.jpg) 50% 50% / cover no-repeat padding-box content-box #bada55;
}
正如我们在本章开始时所说的,要小心后台简写:它会自动将您没有提到的所有值设置回它们的默认值。如果您确实要使用它,首先放入速记声明,然后根据需要覆盖特定的属性。尽可能多地使用快捷键来节省一些击键次数可能很诱人,但作为编写代码的一般规则,显式代码通常比隐式代码更不容易出错,也更容易理解。
多重背景
到目前为止,我们已经处理了背景图像,就好像你总是使用一个单独的图像作为背景。过去是这样,但是 3 级背景和边框规范中定义的背景属性现在允许您为单个元素指定多个背景,并为每个属性指定相应的多值语法。多个值用逗号分隔。下面是一个例子,如图 5-14 所示:
图 5-14。一个元素上的多个重叠背景
.multi-bg {
background-image: url(img/spades.png), url(img/hearts.png),
url(img/diamonds.png), url(clubs.png);
background-position: left top, right top, left bottom, right bottom;
background-repeat: no-repeat, no-repeat, no-repeat, no-repeat;
background-color: pink;
}
背景层在声明时从上到下堆叠,第一个在顶部,最后一个在底部。颜色层在它们后面结束(见图 5-15 )。
图 5-15。多个背景层按照声明的顺序从上到下堆叠。颜色层总是在底部
您还可以声明多个后台简写值:
.multi-bg-shorthand {
background: url(img/spades.png) left top no-repeat,
url(img/hearts.png) right top no-repeat,
url(img/diamonds.png) left bottom no-repeat,
url(img/clubs.png) right bottom no-repeat,
pink;
}
使用这种语法,你只允许在最后一个背景层上声明一种颜色,考虑到图 5-15 中的顺序,这是有意义的。
如果任何背景属性的值列表短于背景图像的数量,则值列表会循环。这意味着如果它们的值都相同,你只需要声明一次:如果它在两个值之间交替,你只需要声明两个,以此类推。因此,前一个示例中的循环不重复可以写成如下形式:
.multi-bg-shorthand {
background: url(img/spades.png) left top,
url(img/hearts.png) right top,
url(img/diamonds.png) left bottom,
url(img/clubs.png) right bottom,
pink;
background-repeat: no-repeat; /* goes for all four */
}
因为多重背景的东西来自于 3 级规范,所以它在一些老的浏览器中也不可用。很多时候,通过使用单值后台语法的组合,您可以为较旧的浏览器实现完全可以接受的回退:
.multi-fallback {
background-image: url(simple.jpg);
background-image: url(modern.png), url(snazzy.png), url(wow.png);
}
就像书中的其他例子一样,旧的浏览器将获得更简单的第一个图像并丢弃第二个声明,而新的浏览器将忽略第一个,因为第二个会覆盖它。
边框和圆角
我们在第三章中提到了边界是盒子模型属性的一部分。在现代浏览器中,我们对边框有了进一步的控制,允许我们用图像和圆角给它们增添情趣——所以我们终于可以创建除了尖锐矩形之外的东西了!
首先快速回顾一下旧的边界属性:
-
您可以分别设置边框各边的属性,也可以同时设置所有边的属性。
-
您可以使用 border-width 设置整个边框的宽度,或者使用 border-top-width 设置特定的边。请记住,边框的宽度决定了框的整体大小,除非 box-sizing 属性另有说明。
-
同样,您可以用 border-color 设置整个边框的颜色,或者用 border-left-color 设置特定的边。
-
边框的样式、边框样式(或右边框样式等。)是由关键字设置的:实线、虚线或虚线是非常常用的。还有一些更奇特的,比如 double(在由 border-width 指定的表面上画两条平行线)、groove 和 inset。老实说,这些很少有用:一是因为它们看起来很时髦,二是因为你让浏览器来控制它们的外观——这在标准中并没有明确规定。您也可以通过设置 border-style: none 来完全移除边框。
-
最后,您可以使用 border 速记来设置所有的边框属性。简写将所有边的宽度、样式和颜色设置为相同的值,如下所示:border:2px solid # 000;。
边框半径:圆角
很长一段时间,圆角一直是开发者的首选。我们花了无数的时间,使用可扩展的、跨浏览器工作的图像来想出新的方法。事实上,这本书以前的版本详细描述了它们。今天,幸运的是,我们已经过了那个阶段。目前唯一不支持边框半径属性的浏览器是老版本的 IE 浏览器(8 及以下版本)和 Opera Mini。关于圆角的事情是,它们通常是一个很好的特性,而不是页面可用性的关键,所以我们认为使用标准化的属性是有意义的,而不是让一些最弱的浏览器(就性能而言)负担更多的代码,以模拟所有其他浏览器中存在的东西。
边框半径速记
这一次,我们将从速记属性开始——因为这是最常见的用例——使盒子上的所有角都变圆。
border-radius 属性允许您通过简单地声明一个长度值来一次设置所有的角。让我们添加一个侧面照片框到我们的例子,并使角落变圆。首先,一些标记:
<header class="profile-box" role="banner">
<div class="profile-photo">
<img src="img/profile.jpg" alt="Charles the Cat">
<h1 class="username">@CharlesTheCat</h1>
</div>
</header>
下面是添加的 CSS 来调整我们的个人资料照片框的大小和位置,以突出标题区域的底部,并给它一个边框以突出背景(参见图 5-16 中的结果):
图 5-16。轮廓照片组件上的圆角
.profile-box {
position: relative;
/* other properties omitted for brevity */
}
.profile-photo {
width: 160px;
min-height: 200px;
position: absolute;
bottom: -60px;
left: 5%;
background-color: #fff;
border: 1px solid #777;
**border-radius: 0.5em;**
}
更复杂的边框半径语法
您也可以使用速记属性来单独设置每个值。首先从左上角开始,然后顺时针旋转:
.box {
border-radius: 0.5em 2em 0.5em 2em;
}
这个声明中的每个长度值已经是一个简写,因为它在每个角的水平轴和垂直轴上表示相同的半径。如果需要不同的值(即不对称的角形状),可以将每个轴指定为一个值列表(先水平,再垂直),并用斜杠分隔两个轴:
.box {
border-radius: 2em .5em 1em .5em / .5em 2em .5em 1em;
}
如果值对角地反映在角上,您可以省略右下角和左下角;如果只有两个或三个值,其余的将被填入:
.box {
border-radius: 2em 3em; /* repeated for bottom right and bottom left. */
}
在前面的示例中,第一个值设置左上角和右下角,第二个值设置右上角和左下角。如果我们为右下角包含第三个值,左下角将获得与右上角相同的值。
在单个角上设置边界半径
当然,您可以使用边框左上半径、边框右上半径等来设置单个角的值。
为这些单个拐角属性提供与前面速记示例中相同的半径长度:一个长度值(创建对称拐角)或两个长度值(用斜线分隔,第一个设置水平半径,第二个设置垂直半径)。
下面是应用于我们的个人资料照片框的单个对称圆角的代码,如图 5-17 所示:
图 5-17。我们的个人资料照片框的一个版本,只有左上角是圆形的
.profile-photo {
border-top-left-radius: 1em;
}
创建具有边框半径的圆形和药丸形状
到目前为止,我们一直在谈论使用长度值来设置半径,但您也可以使用百分比。以百分比设置边框半径时,x 半径与元素的宽度相关,y 半径与元素的高度相关。这意味着我们可以很容易地创建一个正方形元素,然后将其边界半径设置为至少 50%的圆形。
为什么是“至少”?嗯,确实没有理由为所有拐角设置高于 50%的值,但是知道当两条拐角曲线开始重叠时,两轴都减小,直到它们不再重叠,这可能是有用的。对于正方形上的对称角,任何高于 50%的值都会产生一个圆(见图 5-18 )。注意,具有相同边界半径的矩形元素将变成椭圆形,因为半径与该方向的尺寸成比例减小:
图 5-18。圆形和椭圆形使用边界半径:50%
<div class="round"></div>
<div class="round oval"></div>
.round {
width: 300px;
height: 300px;
border-radius: 50%;
background-color: #59f;
}
.oval {
width: 600px;
}
圆形通常是理想的,但椭圆形就不那么理想了。有时,我们想要一个“药丸形状”——一个带半圆的长方形元素。这种形状的专业术语(如图 5-19 所示)是一个 obrund 。百分比或精确的长度测量不会帮助我们创建这样的形状,除非我们知道元素的精确测量,这在网页设计中很少出现。
图 5-19。使用大的边界半径来创建药丸形状
然而,我们可以使用一种奇怪的边界半径计算来创建这个形状。我们看到,当半径不再合适时,半径会减小。但是当它被设置为一个长度时(不是一个百分比),半径与元素的大小无关,相反它们最终是对称的。因此,为了创建一个 obrund 的半圆边,我们可以欺骗和使用一个长度,我们知道这个长度比创建一个半圆边所需的半径长*,并且这个形状会自己创建:*
.obrund {
border-radius: 999em; /* arbitrarily very large length */
}
关于边框半径的最后一点,你应该知道它们是如何影响页面上元素的形状的。我们终于找到了一种方法来创建矩形之外的东西,但是唉:就布局而言,它们仍然会表现得好像它们是覆盖盒子原始表面的矩形。在如何解释元素的形状方面,改变了的一点是,元素的可点击(或“可触摸”)表面遵循了角的形状。在创建圆角按钮、链接等时,请记住这一点。,以便可点击的表面不会变得太小。
边框图像
级别 3 背景和边框规范还允许您定义一个图像作为元素的边框。你可能会问,单一图像有什么好处?border-image 属性的美妙之处在于,它允许您根据“切割”位置的规则将图像分割成九个独立的部分,浏览器将自动为相应的边框部分使用正确的部分。这种技术被称为九切片缩放,有助于避免在调整图像大小以覆盖框时通常会出现的失真。这有点难以想象,所以举个例子吧。
使用边框图像的典型例子可能是为元素创建类似图片框的东西。图片框架的来源是一个边长为 120 像素的正方形图像。如果你从盒子的上、右、下、左边缘画 40 像素的线,你将把盒子分成九个部分(见图 5-20 )。
图 5-20。我们的边框图像的源文件,为了便于说明,分割点画在上面
border-image 属性将自动使用每个扇区中的图像作为相应边框部分的背景。左上角的图像切片将用作该角的图像,中上位的切片将用于上边框,右上角的切片用于该角,依此类推。默认情况下,中间的切片会被丢弃,但是您也可以更改这种行为。
您还可以告诉浏览器在覆盖边框时如何处理上、右、下和左位。它们可以拉伸、重复或间隔,四舍五入显示的完整重复次数:它的工作方式很像更新的背景重复关键字。默认情况下,每一侧的中间切片都被拉伸,这很好地满足了我们的目的。
为了显示边界图像,还需要设置边界宽度——测量将根据特定线段的边界宽度拉伸每个切片。
将此图形用作边界图像,我们可以创建类似于我们在图 5-21 中看到的“座右铭”的东西。
图 5-21。拉伸边框图像以适合元素
这个组件的 CSS 如下所示:
.motto {
border-width: 40px solid #f9b256;
border-image: url(picture-frame.png) 40;
/* ...same as border-image: url(picture-frame.png) 40 40 40 40 stretch; */
}
前面的代码将加载图像 picture-frame.png,从四个边中的每个边对其进行 40 像素的切片,并在顶部、右侧、底部和左侧拉伸中间的切片。请注意,切片辅助线的“20 像素”测量值是在没有 px 单位的情况下给出的;这是一个与矢量图像(SVG)和位图图像之间的差异有关的怪癖。
关于前面的例子,另一件值得一提的事情是,您需要在 border-image 属性之前放置 border 速记(如果使用的话)。规范要求速记重置所有的边界属性,而不仅仅是它自己设置的属性。
如您所料,有特定的边框图像属性来分别设置每个值。事实上,有一大堆值可以让你控制边框图像的工作方式。事情是这样的,我们一方面可以计算出在我们的职业生涯中使用边框图像的次数,所以我们不会在这里做更多的细节。
几年前,边框图像支持在许多网页设计师的愿望清单上排在第一位,主要是因为它可以方便地创建圆角而无需黑客攻击。现在我们有了边界半径,这种需求就不那么迫切了。当然,根据项目的设计,边框图像可能是一个很好的选择——例如,很容易看出位图图像作为边框如何有利于古灵精怪的美感。
如果你想更深入地了解边框图像属性的复杂性,可以看看诺拉·布朗关于 CSS 技巧的文章:css-tricks.com/understanding-border-image/
。对边框图像属性的支持相当广泛——主要是 Internet Explorer 10 和更早版本缺少支持。可悲的是,即使在支持浏览器中也存在相当多的错误和怪癖。
箱形阴影
暂时把背景图片和边框放在一边,我们将探索另一种给页面添加视觉效果的方法:阴影。过去,设计师不得不通过使用额外的元素和图像来为他们的设计添加阴影。不再是了!
CSS 允许您使用 box-shadow 属性添加阴影。它得到了很好的支持。事实上,几乎只有老版本的 IE(8 版及更早版本)和 Opera Mini 被遗漏了。为了支持旧的 Android WebKit 浏览器(和其他一些旧的 WebKit 版本),您需要-webkit-前缀。Firefox(和其他基于 Mozilla 的浏览器)已经有了足够长的时间来安全地跳过-moz 前缀。
你已经在前一章看到了文本阴影的语法:框阴影有一个非常相似的语法,但是增加了一些额外的东西。
让我们添加一个阴影到个人资料照片框来说明,使用以下标记和 CSS(图 5-22 显示结果):
图 5-22。添加了细微阴影的轮廓图像框
.profile-photo {
box-shadow: .25em .25em .5em rgba(0, 0, 0, 0.3);
}
本例中的语法与文本阴影版本完全相同:x 和 y 偏移的两个值,然后是模糊半径值(阴影边缘模糊的程度),最后是颜色,使用 rgba()。注意阴影是如何跟随圆形盒子的拐角形状的!
扩散半径:调整阴影的大小
box-shadow 属性比 text-shadow 更灵活一些。例如,您可以在模糊半径后添加一个值来指定扩散半径:阴影应该有多大。默认值为 0,表示与其应用的元素大小相同。增加该值会使阴影变大,负值会使阴影变小(参见图 5-23 )。
图 5-23。显示有不同扩散半径值的框
.larger-shadow {
box-shadow: 1em 1em .5em **.5em** rgba(0, 0, 0, 0.3);
}
.smaller-shadow {
box-shadow: 1em 1em .5em **-.5em** rgba(0, 0, 0, 0.3);
}
嵌入阴影
另一个比文本阴影更灵活的额外的方框阴影功能是 inset 关键字。应用插入阴影意味着元素被假定为阴影投射到的表面,从而产生它被从背景中“剔除”的效果。例如,我们可以使用插入阴影效果,使它看起来像我们的个人资料标题的背景有点沉入页面,在个人资料照片和其余内容的后面。我们将向配置文件框规则集添加以下内容(参见图 5-24 ):
图 5-24。轮廓标题组件的详细信息,显示了大背景底部边缘上的嵌入框阴影
.profile-box {
box-shadow: inset 0 -.5em .5em rgba(0, 0, 0, 0.3);
}
多重阴影
就像文本阴影一样,您可以对单个元素应用多个阴影,用逗号分隔不同的值。我们将看一个例子来说明如何将它与“平面”阴影技术相结合,并完全消除模糊半径。
如果你忽略模糊半径或将其设置为 0,你将得到一个边缘非常清晰的阴影。这可能是有益的,因为它允许你远离伪现实阴影的心理模型,并开始更多地将它们视为在应用它们的元素后面生成的“额外的盒子”,这些盒子不会影响布局——对于各种效果来说非常方便。
一个有用的例子是在一个元素上创建多个“假边框”。border 属性只允许你画一个边框(诡异的 double 关键字除外,但那不算)。使用 0 模糊半径和不同扩散半径的阴影,你可以创建几个类似边界的区域(见图 5-25 )。因为它们不影响布局,所以它们的行为更像 outline 属性。
图 5-25。使用多重阴影和扩散半径绘制假轮廓
.profile-photo {
box-shadow: 0 0 0 10px #1C318D,
0 0 0 20px #3955C7,
0 0 0 30px #546DC7,
0 0 0 40px #7284D8;
}
使用 CSS 渐变
设计中的一个常见用例是使用颜色渐变作为元素的背景,为页面添加微妙的深度感。加载包含渐变的图像文件可以很好地工作,但是 CSS 也有一个为您绘制渐变图像的机制。这是通过各种样式的梯度函数符号,结合任何接受图像(包括背景图像)的属性来实现的。假设我们有一个用户尚未上传背景图片的个人资料页面(见图 5-26 ),我们希望默认显示渐变背景:
图 5-26。应用于轮廓框背景的线性渐变
.profile-box {
background-image: linear-gradient(to bottom, #cfdeee 0%, #8da9cf 100%);
}
由于用 CSS 创建的渐变图像没有特定的大小,这个渐变最初会覆盖整个元素,除非你特别指定使用背景大小来度量它。
浏览器支持和浏览器前缀
大多数现代浏览器都支持渐变。Internet Explorer 9(及更早版本)和 Opera Mini 是最明显的例外。一些稍旧的基于 WebKit 的浏览器只支持线性渐变版本。在接下来的章节中,我们将会看到不止一种类型的渐变。
注意
CSS 渐变的语法在 Safari 中首次作为非标准属性引入以来,已经发生了几次变化。有三种不同的语法,根据您需要的浏览器支持级别,您可能需要同时使用多个版本,并带有不同的供应商前缀。为了使这一部分易于管理并且不会太混乱,我们将使用最新的无前缀语法来浏览它们。你可以仔细阅读本文中的各种语法:www . site point . com/using-un fixed-css3-gradients-in-modern-browsers/
。
线性渐变
前面的示例使用 linear-gradient()函数沿着从元素顶部到底部的假想线绘制渐变。这条线的角度,在本例中是一个关键字对(到底部),是函数的第一个参数,后面是逗号分隔的色标列表。色标定义了渐变线上颜色发生变化的点,在这种情况下,我们从 0%的较亮蓝灰色开始,以 100%的较暗蓝色结束,这意味着元素的底部。
我们可以使用 to 关键字指定方向,后跟一个边(上、右、下、左)或一个角(左上、右下等)。),后者使梯度成对角线。它从对角或对边开始,渐变线总是穿过图像区域的中心。我们也可以使用以度为单位的角度,其中 0 度表示向上/向北,然后顺时针增加到 360 度,就像 HSL 色轮一样。在这种情况下,度数意味着渐变的绘制方向,所以它仍然从我们指向的相反方向开始。这是一个 45 度的梯度:
.profile-box {
background-image: linear-gradient(45deg, #cfdfee, #4164aa);
}
这里,渐变线不是从背景图像区域的边缘开始。相反,它会自动缩放,以便 0%和 100%的任何颜色都与图像的角重合。图 5-27 解释了其工作原理。
图 5-27。对角线渐变中渐变线的位置和比例
默认值和颜色停止位置
由于从上到下(180 度)是默认设置,0%和 100%分别隐含在第一个和最后一个色标中,我们实际上可以将第一个示例缩短如下(参见图 5-26 ):
.profile-box {
background-image: linear-gradient(#cfdfee, #8da9cf);
}
任何没有指定位置的附加色标将以 0%到 100%之间的比例间隔结束,如果有五种颜色,它们将分别为 0%、25%、50%、75%和 100%:
.profile-box {
background-image: linear-gradient(red, green, blue, yellow, purple);
}
我们可以使用百分比之外的其他测量值来表示色标,从而进一步控制渐变的绘制方式:
.profile-box {
background-image: linear-gradient(#cfdfee, #8da9cf **100px**);
}
这将绘制一个渐变,从顶部的浅蓝色开始,然后经过 100 个像素转换到深蓝色,然后保持这种颜色,直到背景图像区域的底部边缘。
径向梯度
您还可以使用径向渐变来创建沿着假想的渐变光线发生的颜色偏移,以圆形或椭圆形的形式从中心点向所有方向延伸。
径向渐变的语法稍微复杂一些。您可以指定以下属性:
-
哪种形状:圆形或椭圆形。
-
渐变光线的半径,决定渐变区域的大小。圆形只接受一个尺寸测量(对于半径),而椭圆分别接受 x 轴和 y 轴上的两个尺寸测量。椭圆可以使用任何长度或百分比,其中百分比是相对于该轴上的背景图像大小。圆只接受长度,不接受百分比。还有表示渐变区域边缘结束位置的关键字,以便渐变可以延伸到距离中心最远或最近的一侧(最近侧和最远侧)的内,或者渐变形状的边缘接触图像区域的最近或最远角(最近角或最远角)。
-
使用与背景位置属性非常相似的位置值来确定形状中心的位置。这些值前面有 at 关键字,以区别于大小。
-
随着形状的扩展,颜色以逗号分隔的方式停止(尽可能多)。
一个例子可能是这样的:
.profile-box {
background-image: radial-gradient(circle closest-corner at 20% 30%, #cfdfee, #2c56a1);
}
这将给我们一个圆形径向梯度,其中心位于 x 轴上的 20%和 y 轴上的 30%,延伸使得圆的圆周接触最近的角。在圆圈之外,最终颜色停止颜色继续覆盖整个背景图像区域(参见图 5-28 )。
图 5-28。我们的个人资料页面标题有一个圆形径向梯度,定位在 20% 30%,大小扩展到最近的角落
考虑到我们的轮廓框示例形状,我们可能需要一个居中的径向渐变,椭圆形状。让我们尝试一些更迷幻的东西(见图 5-29 ):
图 5-29。放射状渐变中的几个重复色标
.profile-box {
background-image: radial-gradient(#cfdfee, #2c56a1, #cfdfee, #2c56a1, #cfdfee, #2c56a1);
}
我们实际上忽略了声明它是一个椭圆的部分,该椭圆居中并覆盖整个元素(通过延伸到最远的角);在这种情况下,所有这些属性都包含在默认值中。但是像那样重复这些颜色停止似乎有点乏味,不是吗?这就是重复渐变的由来。
重复渐变
在沿着线(或光线)的某一点,法线渐变在最终颜色处停止。还有线性和径向的重复渐变函数(见图 5-30 ),只要它们的大小允许(通过背景大小属性或元素大小),就重复颜色停止序列。例如,这里有一个重复的线性渐变:
图 5-30。重复渐变功能会在整个背景图像区域重复颜色停止列表
.linear-repeat {
background-image: repeating-linear-gradient (#cfdfee, #2c56a1 20px);
}
这是一个重复的径向梯度:
.radial-repeat {
background-image: repeating-radial-gradient(#cfdfee, #2c56a1 20px);
}
渐变作为图案
渐变不一定需要在几个像素上平滑过渡。它们也可以从一个像素变化到下一个像素,让我们可以创建更清晰的线条和圆圈。将这一点与在彼此之上层叠多个背景图像的能力相结合,为我们提供了一种工具来声明性地创建简单的背景图像模式,而无需打开图像编辑软件!
创造清晰图案的诀窍是以正确的方式放置色标。例如,要画一条简单的垂直线,我们需要将相邻的色标放在一起,这样就没有颜色逐渐变化的空间(见图 5-31 ):
图 5-31。第二个和第三个色标都位于 50%,在颜色之间产生明显的变化
body {
background-color: #fff;
background-image: linear-gradient(
transparent,
transparent **50%**,
rgba(55, 110, 176, 0.3) **50%**
);
background-size: **40px 40px**;
}
根据浏览器的不同,您可能会发现它并没有完全清晰地显示线条,而是实际上向两边渐变了 1 px。随着浏览器在渲染渐变方面变得更好,这可能会得到改善,但对于更微妙的模式来说应该足够好了。
我们没有在整个元素上使用重复的线性渐变,而是使用了单一渐变,然后使用背景属性调整和重复生成的图像。这让我们可以控制线条的比例,而不会影响颜色停止。通过添加另一个渐变图像,这次是水平运行,我们可以构建一个“桌布”图案(见图 5-32 ):
图 5-32。用两条线性渐变线绘制背景图案
body {
margin: 0;
background-color: #fff;
background-image: linear-gradient(
transparent,
transparent 50%,
rgba(55, 110, 176, 0.3) 50%
),
linear-gradient(
to right,
transparent,
transparent 50%,
rgba(55, 110, 176, 0.3) 50%
);
background-size: 40px 40px;
}
想象一下你可以用线条、三角形(半填充对角线渐变)、圆形和椭圆形的基本形状(重叠)的倍数想象出丰富的形状,这并不是一个很大的进步。
一个很大的灵感来源是 Lea Verou 在lea.verou.me/css3patterns/
的 CSS3 模式画廊(见图 5-33 )。
图 5-33。Lea Verou 的 CSS3 模式库
使用 CSS 绘图
将渐变图案与方框阴影和伪元素结合起来,你就有足够的机会创造出创造性的效果*,而无需加载一张图像*。另一个鼓舞人心的资源是艺术家兼设计师林恩·费希尔的项目“一个单独的格子”(【http://a.singlediv.com】)。这是一个用 CSS 完成的插图集合,其中每幅插图只需要标记中的一个元素,没有图像(见图 5-34 )。
图 5-34。《一个格子》的插图
请记住,在某些时候,这些 CSS 绘图的代码可能会变得比创建一个 SVG(或 PNG)图像文件并使用它更难理解和维护。还值得记住的是,尽管渐变避免了加载外部图像资源,但它们本身也会对性能产生相当大的影响——尤其是在手机等资源受限的设备上。径向梯度尤其值得保持最小。
设计嵌入图像和其他对象的样式
在设计文档流中的图像样式时,您处理的内容不同于构成页面的其他框。这是因为图像可能有固有的宽度和高度(以像素为单位)、需要考虑的设定纵横比,或者两者都有。在灵活的设计中,内容取决于浏览器窗口的宽度,您需要使用 CSS 来驯服图像和其他嵌入对象。
注意
为当前渲染尺寸加载不同的图像——被称为响应图像——对于性能来说是一个非常重要的话题,但我们现在把它放在一边。我们将回到第八章的响应技术。
灵活的图像模式
使用理查德·鲁特(clagnut.com/blog/268/
)发明的一项技术,可以使图像变得灵活,而不会显示得比其固有尺寸大,也不会扭曲纵横比。在其核心,你只需要以下规则:
img {
max-width: 100%;
}
应用于图像的 max-width 属性意味着图像将根据其所在容器的边界缩小,但如果容器更宽,它将不会增长到其固有大小之外*(参见图 5-35 )。*
图 5-35。320 像素宽的位图图像,最大宽度:100%显示,容器宽度为 100 像素,而容器宽度为 500 像素
我们可以通过将此规则扩展到以下内容来扩展它,以涵盖更多的基础:
img {
width: auto;
max-width: 100%;
height: auto;
}
为什么有额外的规则?嗯,有时标记作者或内容管理系统在 HTML 源代码中把宽度和高度属性与图像尺寸放在一起。
将宽度和高度设置为 auto 部分是为了覆盖这些属性,但也是为了解决 IE8 中的一个错误,即没有声明宽度属性的图像有时无法正确缩放。
新的对象大小调整方法
有时,您最终希望将大小应用于 img 元素和其他嵌入对象(如视频或对象元素),这些对象与它们内部显示的媒体具有不同的纵横比。例如,您可能有一个矩形图像文件作为用户头像占位符(见图 5-36 ),但您希望使用 CSS 将其显示为正方形。
图 5-36。矩形用户化身图像
一些新的神奇属性和关键字最近已经被标准化,并正在进入浏览器,允许您以更灵活的方式调整这些类型元素的内容的大小和位置。使用 object-fit 属性,我们可以调整图像内容的大小,就像使用较新的背景大小关键字一样,同时保持纵横比:
img {
width: 200px;
height: 200px;
}
img.contain {
object-fit: contain;
}
img.cover {
object-fit: cover;
}
img.none {
object-fit: none;
}
img.scaledown {
object-fit: scale-down;
}
图 5-37 说明了当以与固有尺寸不匹配的设定尺寸显示图像时,这些关键字之间的区别。
图 5-37。固定大小图像的示例,其内容使用对象适合属性的不同关键字来调整大小
对象适配的默认行为是填充,这意味着图像的内容将随着元素尺寸而拉伸,这可能会导致纵横比失真。
cover 和 contain 关键字的作用与它们在 background-size 属性中的作用相同。当使用 none 时,使用原始图像的精确尺寸,而不管元素的大小。最后,还有缩小,它会在无和包含之间自动选择,选取最小的结果尺寸。生成的图像居中,但可以使用对象位置进行定位,就像定位背景图像一样。
到目前为止,支持仅限于 Chrome、Opera、Safari 和 Firefox 的最新版本,尽管在编写本文时 Safari 还不支持对象位置。没有任何版本的 IE 或 Edge 支持这种行为,尽管 Edge 很可能会紧随其后,很快支持这些属性。
具有长宽比意识的柔性容器
对于位图图像,正如我们在前面几节中看到的,纵横比是内置的:它们有固定的宽度和高度,只要您将高度设置为自动,并且只更改宽度(反之亦然),事情看起来仍然正确。
但是,如果您正在设计的元素没有固有的纵横比,而您想给它一个纵横比,同时保持它的灵活性和可调整性,会发生什么情况呢?
iframe 和 object 元素就是这种情况,在某种程度上,SVG 内容也是如此。一个常见的例子是将来自 YouTube 或 Vimeo 等网站的视频嵌入页面时得到的标记:
<iframe width="420" height="315" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allowfullscreen></iframe>
如果我们像这样设置一个灵活的宽度:
iframe {
width: 100%; /* or any other percentage, really…*/
}
…这将产生一个 100%宽的 iframe,但由于 height 属性,它仍然是 315 像素高。由于视频有一个设定的长宽比,我们希望高度自动调整。
设置自动高度或删除该属性不起作用,因为 iframe 没有固有的高度,它很可能变成 150 像素高。为什么是 150 像素?CSS 规范规定被替换的内容(比如 iframes、images、object 元素等。)没有指定的或固有的尺寸,退回到 300 像素宽和/或 150 像素高的尺寸。奇怪但真实。
为了解决这个问题,我们需要应用一些聪明的 CSS 技巧。首先,我们将 iframe 放在包装元素中:
**<div class="object-wrapper">**
<iframe width="420" height="315" src="https:www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allowfullscreen></iframe>
**</div>**
然后,我们让包装框的大小与我们想要嵌入的对象的长宽比相同。为了弄清楚这一点,我们用原始高度(315 像素)除以原始宽度(420 像素)得到一个结果比率:315/420 = 0.75。所以高度是宽度的 75%。
接下来,我们将包装器的高度设置为 0,但是将底部填充设置为我们得到的数字—75%:
.object-wrapper {
width: 100%;
height: 0;
padding-bottom: 75%;
}
你可能从第三章中记得,当垂直填充和边距以百分比设置时,它们实际上指的是包含块的宽度——在这种情况下,宽度是 100%(与包含块相同),所以填充是 75%。我们现在已经创建了一个具有设定纵横比的块。
最后,我们将嵌入的对象放在包装器中。即使包装器的高度为 0,我们也可以使用绝对定位将元素放置在“支持纵横比”的填充框中:
.object-wrapper {
width: 100%;
height: 0;
**position: relative;**
padding-bottom: 75%;
}
.object-wrapper iframe {
**position: absolute;**
top: 0;
right: 0;
bottom: 0;
left: 0;
}
就是这样!现在,我们有办法将灵活的对象嵌入到页面中,并创建其他保持纵横比的元素。图 5-38 显示了该过程。
图 5-38。创建支持纵横比的容器
存在一个警告:如果我们希望包装器不是 100%宽,我们将不得不重新计算填充底部的尺寸。因此,使用另一个包装器来实现进一步的灵活性可能是一个好主意;然后,我们可以将外层包装的宽度设置为我们喜欢的宽度,将内层包装的宽度设置为 100%,这样就完成了。
这项技术是由开发者 Thierry Koblentz 率先提出的,你可以在alistapt . com/article/creating-intrinsic-ratios-for-video
阅读对它的深入解释。
减小图像文件大小
当你使用图片作为设计的一部分时,你需要确保你不会向你的用户发送不必要的大图片。当然,您可以使用 CSS 来缩放和裁剪它们,但是每个不必要的像素都会导致性能损失。下载时间过长,电池耗尽,处理器浪费时间调整图像大小,这些都是良好用户体验的敌人。
减少不必要的文件大小的第一步是优化您的图像。图像文件通常包含大量的元数据,浏览器并不真正需要这些元数据来正确显示图像,有一些程序和服务可以帮助你从文件中剥离这些内容。艾迪·奥斯马尼在 https://addyosmani.com/blog/image-optimization-tools/做了一次精彩的综述。他提到的许多工具都是自动化任务运行器的一部分——我们将在第十二章中回头来看看这类工作流。
如果您正在处理 PNG 图像以获得更简单的图形,那么通过减少图像中的颜色数量,您也可以极大地减小文件大小。如果您在图像中使用 alpha 透明度,大多数图像编辑软件只会让您以 PNG24 格式导出它。事实是,即使是更简单(也小得多)的 PNG8 格式也可以包含 alpha 透明度,因此通过将图形转换为这种格式,您可以获得更多的收益。有基于网络的服务,比如帮助你在线转换 PNG 文件的tinypng.com
,还有几个适用于所有操作系统的独立应用。一些专业图像编辑程序,如 Photoshop,在最新版本中内置了这一功能。
如果您正在使用 SVG 图形,您应该知道大多数处理 SVG 的图像编辑器导出的文件中有许多不必要的数据。一个非常有用的优化 SVG 的工具是杰克·阿奇博尔德的 OMG SVG(jakearchibald.github.io/svgomg/
)——一个在线工具,让你调整一系列参数,使你的文件更精简,它甚至可以离线工作!
我们将在第十二章中深入分析和调试性能的技术。
摘要
在这一章中,我们已经学习了很多设计页面盒子样式的技巧。我们探讨了如何使用各种颜色语法,以及如何使用透明度。我们已经了解了如何控制背景图像,以及如何相对于元素框定位、调整大小、重复和裁剪它们。
我们还向您展示了如何使用边框,以及如何通过使用边框半径来创建圆角,甚至是圆形,从而打破默认的四方形。
我们尝试使用阴影,既作为一种在页面中创造深度的手段(作为盒子上的插入或开始阴影),也作为一种绘制“额外矩形”来创造其他视觉效果的手段。此外,我们还研究了如何使用线性和径向渐变,既作为微妙的效果,也作为让浏览器为您绘制图像模式的一种方式。
我们讨论了内容图像和背景图像之间的差异,以及如何灵活地设计内容图像的样式,还有其他嵌入的内容,包括支持纵横比的容器。
我们将在第十一章回到一些更高级(但不太受支持)的视觉效果。同时,在下一章中,我们将最终结合我们的尺寸、样式、定位框和文本的知识,使用新旧技术和属性为 Web 做适当的布局。
六、内容布局
一个网页,在最基本的层面上,是由不同的内容块组成的:标题、段落、链接、列表、图像、视频等等。这些要素可以按主题分组;构成新闻故事的标题、一些文本和图像。通过控制每个组件中项目的位置、大小、顺序和间距,我们可以更好地传达它们的功能和意义。
这些内容通常会进一步组合到整个页面的布局中。我们将在下一章中探讨如何系统地编排整个页面。在本章中,我们将继续关注各个内容块以及如何对它们进行布局。
我们已经简单地提到了使用定位和浮动来布局,它们都有优点和缺点。您还可以引导其他属性,如表格显示模式和内联块,在布局中发挥它们的作用,各有利弊。新的柔性盒布局模块——或简称为flexbox——提供了一整套属性来控制排序、方向、对齐和大小。Flexbox 是一个强大的工具,我们将详细介绍它。
在本章中,我们将了解以下内容:
-
绝对与相对定位以及 z 索引的常见使用案例
-
使用浮动、内联块和表格显示进行布局
-
掌握垂直对齐和垂直居中
-
使用 flexbox 进行定位、调整、订购和调整
使用定位
在第三章中,我们提到定位并不是高级布局的最佳工具,因为它将元素从页面流中剔除。另一方面,这使得定位成为 CSS 的一个重要部分。在这一节中,我们将简要分析一些定位是有用工具的场景。
从第章到第章,快速回顾一下:
-
元素最初被定位为静态,这意味着块级元素垂直堆叠。
-
我们可以给元素相对定位,允许我们相对于它们的原始位置轻推它们,而不改变它们周围的元素流。这样做也为后代元素创建了一个新的定位上下文。最后一个事实使得相对定位非常有用。从历史上看,移动元素的能力是许多老派布局技巧的重要组成部分,但现在我们没有它们也能应付过去。
-
绝对定位允许我们给一个元素一个关于最近的定位上下文的精确位置,它或者是一个定位非静态的祖先,或者是 html 元素。在这个模型中,元素被从页面流中提取出来,并相对于它们的定位上下文放回原处。默认情况下,它们会停留在原本应该停留的位置,但不会影响周围的元素。然后,我们可以选择更改它们相对于定位上下文的位置。
-
固定定位基本与绝对相同,但定位上下文自动设置为浏览器视口。
绝对定位用例
绝对定位的本质使它成为创建覆盖、工具提示和对话框等位于其他内容之上的东西的理想选择。它们的位置可以用 top、right、bottom 和 left 属性给出。关于绝对定位,有几件事是值得了解的,可以帮助您编写更高效的代码。
使用初始位置
对于这个例子,我们使用的是一篇关于宇宙飞船的文章,我们想引入一些行内注释。我们希望将它们显示为空白处的小注释气泡,如图 6-1 所示。
图 6-1。在文章旁边显示页面内评论
每个注释都是位于注释所指段落之后的旁注元素:
<p>This is a fake article[...]</p>
<aside class="comment"> I've never done this. Is that really true?</aside>
<p>You may think[...]</p>
为了让评论显示在它所指的段落的末尾,我们需要绝对定位它。诀窍是我们不需要给它一个从物品容器顶部的精确的偏移量来在垂直方向上正确定位它。
当定位上下文的偏移量未定义时,绝对定位的元素将保留它们作为静态元素的位置,因此第一步是将注释留在原处(参见图 6-2 ):
.comment {
position: absolute;
}
图 6-2。对注释应用绝对定位会将它从流中提升出来,但默认情况下会将它留在原本会以静态位置结束的地方
现在,我们需要将注释向左上方移动,使其位于上一段末尾的空白处。这种轻推听起来像是相对定位的工作,但是我们不能让一个元素同时被绝对定位和相对定位。如果我们使用方向偏移(顶部、右侧、左侧和底部)来定位它,我们将依赖于父定位上下文和周围元素的确切大小。幸运的是,我们不需要!相反,我们可以使用负边距来微调元素:
.comment {
position: absolute;
**width: 7em;**
**margin-left: -9.5em;**
**margin-top: -2.5em;**
}
负边距在 CSS 中完全有效,并且有一些有趣的行为:
-
左边距或上边距为负会将元素拉向那个方向,与它旁边的任何元素重叠。
-
负的右边距或下边距会拉近任何相邻的元素,使它们与具有负边距的元素重叠。
-
在浮动元素上,与浮动方向相反的负边距将减小浮动区域,导致相邻元素与浮动元素重叠。浮动方向上的负边距会将被浮动的元素拉向那个方向。
-
最后,当在没有定义宽度的非浮动元素上使用时,侧面的负边距行为会稍微缓和一些。在这种情况下,左边和右边的负边距都将元素拉向那个方向。这个扩展了元素,潜在地覆盖了任何相邻的元素。
在我们的注释气泡的例子中,我们使用负的左边距和上边距来将元素拉到适当的位置,就像我们使用相对定位一样。
额外收获:在 CSS 中创建三角形
在图 6-1 所示的评论气泡中,指向前一段的小三角形状依次相对于评论气泡绝对定位。它被创建为一个伪元素,并使用一个带有边框的老技巧赋予了一个三角形。(这至少可以追溯到 2001 年——请看坦泰克·切利克的这篇文章:【http://tantek.com/CSS/Examples/polygons.html。)图 6-3 显示了它是如何工作的。
.comment:after {
position: absolute;
content: '';
display: block;
width: 0;
height: 0;
border: .5em solid #dcf0ff;
border-bottom-color: transparent;
border-right-color: transparent;
position: absolute;
right: -1em;
top: .5em;
}
图 6-3。创建具有零尺寸元素和边框的箭头。由于右边缘和下边缘是透明的,所以留下了一个三角形
在这里,我们创建一个 0 × 0 像素的块,它有一个 5 em 的边界,但是只有顶部和右侧的边界边缘有颜色,所以我们最终得到一个三角形,因为角的边界边缘是倾斜的。一种不用图像就能生成三角形的简便方法!然后我们定位三角形,使其突出于评论框的右上角(见图 6-4 )。
图 6-4。相对于注释内容定位三角形
使用偏移量自动调整大小
在标尺的另一端,它有助于了解当元素被绝对定位并声明了许多或所有的偏移量时,元素是如何反应的。没有任何声明的大小,绝对定位的元素将回落到包含其内容所需的大小。当我们从定位上下文的相对两侧声明偏移量时,元素将拉伸以适应满足这些规则所需的大小。
例如,我们可能会遇到这样的情况,我们希望在距离另一个元素的边缘一定距离处调整某个元素的大小,但不在任何一个元素上使用特定的大小。例如,我们可能在一幅图像的上面有一个包含文本的框,如图 6-5 所示。
图 6-5。图像顶部的半透明框相对于右侧、底部和左侧绝对定位。到顶端的距离由内容决定
<header class="photo-header">
<img src="img/big_spaceship.jpg" alt="An artist's mockup of the "Dragon" spaceship">
<div class="photo-header-plate">
<h1>SpaceX unveil the Crew Dragon</h1>
<p>Photo from SpaceX on <a href="https://www.flickr.com/photos/spacexphotos/16787988882/">Flickr</a></p>
</div>
</header>
假设我们不希望容纳标题的半透明“板”占据特定的宽度,我们可以从右侧、底部和左侧放置它,让它自己计算出它的尺寸和顶部边缘位置:
.photo-header {
position: relative;
}
.photo-header-plate {
position: absolute;
**right: 4em;**
**bottom: 4em;**
**left: 4em;**
background-color: #fff;
background-color: rgba(255,255,255,0.7);
padding: 2em;
}
无论图像的尺寸如何,板现在将位于图像的底部,距离底部和侧面 4 ems。这为我们提供了一些在不同屏幕尺寸下都能很好工作的东西——如果有换行符,板的上边缘会根据内容高度进行调整(见图 6-6 )。
图 6-6。在较小的屏幕尺寸下,文字会随着框的向上增长而换行
定位和 z 索引:堆叠上下文陷阱
聪明地使用定位的最后一个要素是很好地掌握 z 索引:元素的堆叠顺序。我们在第三章中提到了基础知识:位置不是静态的元素根据它们在源代码树中的深度排列成堆栈,就像扑克牌叠放在一起一样。更改 z 索引会更改它们在堆栈中的顺序。
任何具有设置为正值的显式 z-index 声明的元素在堆栈中的位置都比没有 z-index 声明的元素高。具有负值的元素显示在没有 z 索引的元素后面。
但是 z 索引并不是唯一控制元素堆叠方式的东西。我们还有一个堆栈上下文的概念。将卡片组的类比延伸一点,每张卡片也可以是它自己的卡片组,卡片只能根据当前的卡片组级别进行排序。总是有一个根堆栈上下文开始,z 索引不是 auto 的定位元素在其中排序。随着其他上下文的形成,它们创建了堆栈的层次结构。
特定的属性和值创建了这些新的堆叠上下文。例如,position: absolute 和 z-index 声明设置为除 auto 之外的任何值的元素将为其中的后代元素形成堆栈上下文。
在堆栈上下文中,z-index 值的大小无关紧要:你不能相对于另一个堆栈上下文重新排序(见图 6-7 )。
图 6-7。容器 A、B、C 和 D 都是绝对定位的,其中 C 是 B 的子元素。容器 C 和 D 应用了 z-index,但是由于容器 B 的不透明度小于 1,它创建了一个新的堆叠上下文,与其他上下文分开。z-index 不会将 C 放在 D 的前面,不管数字有多高
这些触发规则之一是将不透明度设置为小于 1 的值。不透明度较低的元素在放置到页面上之前需要单独呈现(与其后代元素一起),因此这些规则是为了确保在这种情况下没有外部元素可以插入半透明元素之间。本书附带的文件中有一个代码示例,可以让您处理这种情况。
在本书的前面,我们将遇到其他的例子,比如 transform 和 filter 属性,它们也可以触发新的堆栈上下文的创建。在本章的最后,我们将了解使用 z-index 和 flexbox 的一些特性。
水平布局
一般来说,随着内容的增加,网页在垂直方向上增长。您添加的任何块容器(div、article、h1–h6 等。)将垂直堆叠,因为它们显示为具有自动宽度的块。正因为如此,当你想给内容块一个宽度,并让它们在水平方向彼此相邻时,最基本的布局挑战之一就出现了。
我们已经在第三章看到了一个使用浮动设计小型“媒体组件”的例子。这种一边是图像(或其他类型的媒体),另一边是一段文本的模式是布局的原子模式的一个很好的例子:“这个东西紧挨着另一个东西,它们属于一起。”如果你查看任何网站,你肯定会看到这种模式一次又一次地重复(见图 6-8 )。
图 6-8。Wired.com 一部分的截图。你能发现多少“媒体对象”?
还有许多其他常见的模式出现在各种各样的网站上。很多都和横向布局有关。像 flexbox 这样的新标准是为了满足水平布局(以及更多)而创建的,但是在 flexbox 得到普遍支持之前,您可能需要采用浮动、内嵌块显示或表格显示模式来创建水平布局模式。
使用浮动
在飞船文章中,我们有一个关于浮动最基本用法的例子。该图浮动到右侧,允许文本的行框围绕其周围和下方流动(参见图 6-9 )。我们还使用了负的右边距来将图像从文本中拉出一段距离。
图 6-9。使用一个浮动的数字,使用负边距-右拉出
<p>You may think[...]</p>
<figure>
<img src="img/spaceship.jpg" alt="The Dragon spaceship in orbit around Earth.">
<figcaption>The "Dragon" spaceship, created by SpaceX. Image from <a href="https://www.flickr.com/photos/spacexphotos/16787988882/">Flickr.com</a></figcaption>
</figure>
<p>There's various [...]</p>
figure {
background-color: #eee;
margin: 0;
padding: 1em;
float: right;
max-width: 17em;
margin-right: -8em; /* pull to the right */
margin-left: 1em;
}
在图 6-10 中,我们移除了负边距,并限制该图占据宽度的 50%。我们还在第一个数字之后添加了第二个数字。这两个图形现在将水平并排放置。
图 6-10。两个宽度为 50%的浮动图形,并排放置
figure {
float: right;
width: 50%;
}
这种效果——浮动项目充当“行”中的“列”——形成了无数 CSS 布局技术的基础。正如在第三章中所讨论的,浮动有一些奇怪的地方会让你犯错。请记住,浮动实际上并不在页面流中,所以您可能需要一个包含浮动的元素。通常,这是通过对容器内的(伪)元素应用 clear,或者通过一个规则使容器成为一个新的块格式上下文来实现的。如果需要的话,浮动也可以包装成多行,但是会卡在从上面的行伸出的前面的浮动上。
浮动还可以提供一些有限的水平内容的重新排序,与源顺序无关。例如,我们可以通过向左浮动而不是向右浮动来切换图形的位置(参见图 6-11 )。
图 6-11。通过向另一个方向浮动来交换图形的位置
由于无处不在的浏览器支持和浮动的相对多功能性,它们已经成为许多水平布局变化的首选解决方案。当我们为高级页面布局构建一个小的网格系统时,我们将在第七章回到使用它们。但是还有其他 CSS 属性允许我们创建水平布局模式,它们有各自不同的优缺点,我们将在接下来的章节中看到。
作为布局工具的内嵌块
文本行本身就是一种水平布局,至少在从左到右或从右到左书写的语言中是这样。当我们使用行内元素(如 span、time 或 a)时,它们与文本在同一方向水平排列。我们还可以将内联块放入该流中,创建水平排列的元素,但在视觉格式方面充当块,并且可以在其中包含其他块。
例如,让我们在飞船文章的底部添加一些元数据,由一个作者姓名、一张照片和一个电子邮件地址组成。我们还增加了几个额外的跨度作为造型挂钩:
<p class="author-meta">
<!-- image from Jeremy Keith on Flickr: https://flic.kr/p/dwFRgH -->
<img class="author-image" src="img/author.jpg" alt="Arthur C. Lark">
<span class="author-info">
<span class="author-name">Written by Arthur C. Lark</span>
<a class="author-email" href="mailto:arthur.c.lark@example.com">arthur.c.lark@example.com</a>
</span>
</p>
的内容。作者-元段落现在将对齐,图像的底部边缘位于文本的基线上。任何空白字符,包括例如图像和作者信息开始的行之间的换行符,都将呈现为空白。该空间的宽度取决于字体系列和字体大小(参见图 6-12 )。
图 6-12。我们的作者元数据。注意图像和文本之间的空白。
接下来,我们将把图像和作者信息转换成内嵌块:
.author-image,
.author-info {
display: inline-block;
}
在渲染方面,组件在这个阶段看起来是一样的。不同之处在于,我们可以开始将图像和信息视为块。例如,我们可以将作者信息中的姓名和电子邮件地址放在图像旁边的单独行中,方法是将它们更改为块显示:
.author-name,
.author-email {
display: block;
}
我们现在已经非常接近视觉效果了,例如,一个文本块旁边的浮动图像(如第三章中的“媒体块”示例)。一个区别是作者信息块的最后一条基线与图像的底部对齐。我们在图 6-13 中看到了结果,我们在图像和作者信息周围添加了一个虚线轮廓,以可视化这两个元素之间的关系。
图 6-13。作者信息的基线现在与图像的底部对齐
我们现在可以通过改变 vertical-align 属性来相对于图像移动作者信息。当对齐设置为顶部时,作者信息块的顶部将与图像的顶部对齐(参见图 6-14 )。
图 6-14。使用 vertical-align: top 将作者信息对齐到图像顶部
带内嵌块的垂直居中
现在,假设我们想要的设计是作者信息块相对于图像垂直居中。尝试这样的事情可能很有诱惑力:
.author-info {
vertical-align: middle;
}
…但这可能不会达到您预期的效果!图 6-15 显示了结果。
图 6-15。使用垂直对齐时作者信息的位置:中间
这就是事情变得有些棘手的地方。关键字 middle 应用于内联块时,意味着“将该内联块的垂直中心与文本行的 x 高度的中间对齐”在这种情况下,有就有没有内联文本。因此,图像(是行中最高的元素)决定了行框的高度和基线的结束位置。因此,x 高度的中心正好位于图像底部的上方。为了使作者信息在图像的垂直中心居中,我们需要使两个元素指向同一个“中间”:
.author-image,
.author-info {
vertical-align: middle;
}
当图像是一个内嵌块时,它也在作者信息的同一个垂直点上垂直居中,产生了我们想要的布局,如图 6-16 所示。
图 6-16。将 vertical-align: middle 应用于图像和作者信息,使它们在同一点上垂直居中
如何决定行框基线的规则,以及它如何影响行内和行内块元素,是相当复杂的。如果你想深潜,我们推荐克里斯托弗·奥伊的文章《垂直对齐:你需要知道的一切》(christopheraue.net/2014/03/05/vertical-align/
)。为了将内嵌块显示用作布局工具,在垂直对齐方面有两个重要的要点:
-
要使内联块顶端对齐(很像浮动),设置 vertical-align: top。
-
要使内容彼此垂直居中,请确保它们都是内联块,然后使用 vertical-align: middle。
容器元素内的垂直居中
上一个列表中的最后一点使我们能够在任意高度的容器中垂直居中内容,这需要一点技巧。唯一的先决条件是容器的高度设置为一个确定的长度。
例如,让我们假设我们想要使作者信息块 10 米高,并在其中垂直和水平地居中放置作者图像和信息。首先,我们将高度应用于。作者-元块。我们还将添加一个边框,使变化更容易被发现(见图 6-17 )。
.author-meta {
height: 10em;
border: 1px solid #ccc;
}
图 6-17。那个。添加了高度和边框的作者元块
照片和作者信息的垂直对齐与容器块无关,但与它们所在的假想文本行有关。为了垂直对齐它们,我们需要添加另一个内嵌块元素*,它占据了 100%的高度。该元素将强制 middle 关键字的对齐点位于容器的中间。为此,我们将使用伪元素。图 6-18 显示了当添加这个“虚元素”时,假设基线是如何计算的。*
图 6-18。使用 100%高的伪元素来强制 middle 关键字以表示容器的垂直中心结束
.author-meta:before {
content: '';
display: inline-block;
vertical-align: middle;
height: 100%;
}
至此,整个。author-meta 容器实际上将有一个占据整个高度的单行框。由于伪元素是垂直对齐设置为中间的内联块,所以其他内联块将垂直对齐容器的中心。我们现在需要做的就是将内容水平居中。由于内联块响应文本对齐,我们需要使用文本对齐:
.author-meta {
height: 10em;
**text-align: center;**
border: 1px solid #ccc;
}
.author-info {
**text-align: left;**
}
这将导致。author-meta 水平和垂直居中,如图 6-19 所示。
图 6-19。内容现在水平和垂直居中
实际上,水平居中并不完全正确。请记住,行框中的任何空白字符都将显示为一个空格。伪元素将创建一个这样的空间,将内容向右推几个像素。我们可以通过对伪元素应用负边距来取消空白空间的宽度:
.author-info:before {
**margin-right: -.25em;**
}
为什么是 0.25 毫米?在这种情况下,它恰好是当前字体中空白字符的宽度。这是一个“神奇的数字”,会随着使用的字体而变化。因此,它不是很健壮,我们不推荐任何系统化的布局工作。在我们的下一个水平布局例子中,我们将关注内联块作为布局工具的更详细的应用。
获取正确的细节:对抗空白
当处理水平布局时,每个块占用一个精确的宽度,空白问题变得更加明显。我们将通过构建另一个通用组件来强调如何在使用内联块时解决这个问题,使用更少的幻数。
这一次我们创建了一个导航栏,由四个链接项组成,每个链接项正好占宽度的四分之一。我们从标记开始:
<nav class="navbar">
<ul>
<li><a href="/home">Home</a></li>
<li><a href="/spaceships">Spaceships</a></li>
<li><a href="/planets">Planets</a></li>
<li><a href="/stars">Stars</a></li>
</ul>
</nav>
CSS 在颜色和字体方面为我们提供了一些基本的样式,以及突出项目之间边缘的轮廓。每个项目都设置为 25%的宽度,因此四个项目应该作为一个整体适合导航栏:
.navbar ul {
font-family: Avenir Next, Avenir, Century Gothic, sans-serif;
list-style: none;
padding: 0;
background-color: #486a8e;
}
.navbar li {
text-transform: uppercase;
display: inline-block;
text-align: center;
box-sizing: border-box;
width: 25%;
background-color: #12459e;
outline: 1px solid #fff;
}
.navbar li a {
display: block;
text-decoration: none;
line-height: 1.75em;
padding: 1em;
color: #fff;
}
我们使用 box-sizing: border-box 来确保每个项目的任何边框或填充都包含在每个项目的 25%宽度中。导航栏本身的背景是蓝灰色的,而项目的背景是略暗的蓝色,带有白色的链接文本。
现在来看结果,如图 6-20 所示。
图 6-20。遗憾的是,这个列表不能放在一行中,而且项目是分开的
HTML 源代码中的换行符呈现为空格字符,增加到每个项目的 25%宽度,并导致换行。例如,我们可以通过将所有的
- Tags are placed in a line to eliminate these blank characters, but this requirement for tag format is fragile.
我们解决这个问题的首选方法有点残忍。它的工作原理是将容器本身的字体大小设置为 0(从而导致空格字符的宽度为零),然后重新设置项目的大小:
.navbar ul {
**font-size: 0;**
}
.navbar li {
**font-size: 16px;**
**font-size: 1rem;**
}
这以一种可预测的方式消除了空白,使项目很好地适应容器,如图 6-21 所示。
图 6-21。具有四个等宽项目的导航栏
这种技术有几个缺点。第一个与继承的字体大小有关。假设我们在导航条上使用 16 像素的字体大小,我们不能再使用 em 单位或百分比来继承列表项的灵活字体大小——它只会变成 0 的倍数。相反,我们可以通过使用 rem 单位将大小基于根字体大小来保留灵活的大小调整。对于不支持 rem 单位的浏览器(大多数是 Internet Explorer 8 和更早版本),基于像素的测量作为一种后备措施。
第二个缺点与稍旧的基于 WebKit 的浏览器有关,在这种浏览器中,字体大小为 0 并不总是受欢迎的——例如,早期版本的 Android 4 上基于 WebKit 的浏览器。正如我们将在本章前面看到的,我们通常只使用内嵌块显示作为旧浏览器的后备,然后在 flexbox 等更现代的技术上再加一层。因为即使是这些旧的 Android 浏览器也支持 flexbox——尽管是旧版本——空白问题可能会成为一个问题。
小费
如果你出于某种原因需要在这些旧的 Android 浏览器中使用内嵌块技术,还有一个关于字体的技巧。它的工作原理是在父元素上使用一个很小的自定义字体,只包含一个宽度为零的空格字符。然后在子元素上重置原始字体系列。详见开发者 Matthew Lein 的这个演示:Matthew Lein . com/articles/inline-block-no-space-font/
。
将表格显示属性用于布局
表格中的行具有我们在 navbar 示例中寻找的精确质量:许多“单元格”划分它们之间的空间,从不在多行上滑落。这也是为什么在网络早期,实际的 HTML 表格被用于布局的原因之一。现在,我们可以通过 CSS 借用表格的显示模式,而不需要借助基于表格的标记。
如果我们将导航栏示例更改为使用 ul 元素的表格显示模式,并将每个项目设置为显示为表格单元格,我们将获得与使用内联块时相同的外观:
.navbar ul {
/* some properties omitted for brevity. */
**width: 100%;**
**display: table;**
**table-layout: fixed;**
}
.navbar li {
width: 25%;
**display: table-cell;**
}
这给了我们与前面的内嵌块示例完全相同的外观(如图 6-21 所示)。
请注意,我们已经将 ul 元素设置为 100%宽。这是为了确保 navbar 扩展以填充其父级。与常规块不同,没有设置宽度的表格具有“收缩以适应”宽度,除非单元格的内容将它们推出以填充其父容器。
对于如何计算表格行中每列的宽度,有两种算法。默认情况下,浏览器将使用“自动”算法。从标准的角度来看,它有些不明确,但是它基本上允许表格根据整个表格的单元格内容来调整列宽。
另一种算法是“固定的”表格布局。使用表格布局:固定,列宽根据表格的第一行确定。第一行上声明的任何宽度都会遇到“win”,如果后续行有更宽的内容,该内容将在单元格内换行,或者溢出。
虽然在这个示例中,将表格布局设置为 fixed 在技术上不是必需的,但是在使用表格显示模式作为布局工具时,通常会使用它,以避免自动模式带来的任何意外。
当使用表格显示模式进行布局时,您应该知道表格呈现的其他特点也适用。例如,不可能对呈现为表格单元格的元素应用边距,应用于表格单元格的定位行为充其量也是不可靠的。我们将在第九章回到 HTML 表格和 CSS 表格显示模式。
表格单元格中的垂直对齐
表格显示模式的另一个有用的方面是,在这种情况下,垂直对齐的工作方式略有不同。在显示为表格单元格的元素上设置 vertical-align: middle 将使单元格的内容垂直居中对齐,没有任何额外的技巧。图 6-22 显示了如果我们给以表格形式显示的列表添加一个设定的高度,并将列表项垂直居中对齐会发生什么。
.navbar ul {
display: table;
height: 100px;
}
.navbar li {
display: table-cell;
vertical-align: middle;
}
图 6-22。为显示为表格单元格的列表项添加高度和垂直居中
不同技术的优缺点
当考虑将浮动、内联块和表格显示模式作为水平布局和垂直对齐的工具时,我们如何确定使用哪一种呢?每种方法都有利弊:
-
与内联块一样,float能够换行。浮动还根据其内容“收缩包装”到一个大小,这是一个有用的行为。消极的一面是,当包含或清除浮动时,浮动可能会给你带来痛苦,当浮动的项目卡在更高的浮动上时。另一方面,浮动在某种程度上是独立于源代码顺序的,因为您可以将一些元素浮动在一行的右边,而将其他元素浮动在左边。
-
内联块有空白问题,但是这些问题是可以解决的,尽管有一些蹩脚的解决方案。从积极的一面来看,内联块也可以换行到多行上,它们给你一些对垂直对齐的控制,并且它们有和浮动一样的“收缩换行”大小行为。
-
使用表格显示模式进行水平布局也很有效,但是只适用于非换行的内容行。它们和表格有相同的怪癖,也就是说,例如,它们不受边距的影响,并且里面的项目不能被重新排序。它们还允许其内容简单地垂直居中。
flex box(flex box)的缩写形式
灵活框布局模块,称为 flexbox,是我们可以用来创建布局的一组较新的 CSS 属性。它由许多与容器元素( flex 容器)及其直接子元素( flex 项目)以及这些子元素的行为相关的属性组成。Flexbox 可以控制 flex 项目的几个方面:
-
大小,基于内容和可用空间
-
流向:水平或垂直,向前或向后
-
在两个轴上对齐和分布
-
订购,不考虑来源订单
如果对布局使用内联块、浮动和表格属性让你感到不舒服,flexbox 可能是你想要的解决方案。它是作为对我们在本章中已经看到的各种常见场景的直接响应而开发的。
浏览器支持和语法
所有主流浏览器的最新版本都支持 Flexbox。通过对语法和供应商前缀的一些调整,您也可以让它适用于各种稍旧的浏览器。
为了在 IE10 和更老的 WebKit 浏览器中实现支持,您需要用厂商前缀和稍微不同的属性来补充我们在本章中使用的标准语法,因为 flexbox 的语法在规范的各种迭代中已经改变了很多。有许多工具和文章描述了如何做到这一点,比如“Flexy Boxes”代码生成器(the-echoplex.net/flexyboxes/
)。
请注意,Internet Explorer 9 和更早版本根本不支持 flexbox。我们将在本章后面讨论这些浏览器的一些回退策略。
了解挠曲方向:主轴和横轴
Flexbox 允许您定义页面的一个区域,在这个区域中,可以根据顺序、大小、分布和对齐来控制一组元素。该空间内的框按两个方向排列:默认情况下,水平(作为一行)或垂直(作为一列)。该方向被称为主轴。
内部的方框也可以在垂直于主轴的方向上移动和调整大小:这被称为横轴(见图 6-23 )。通常,使用 flexbox 创建布局的最重要的度量是沿着主轴的尺寸:水平布局的宽度和垂直布局的高度。我们称这个尺寸为物品的主尺寸。
图 6-23。定义行与列模式下的主轴和横轴,以及它们各自的主要大小属性
回到我们第一次在图 6-20 中看到的导航栏例子(一个包含链接的无序列表的包装器),我们可以很容易地将其转换成一个水平的 flex 容器。假设其余的样式(颜色,字体,链接样式,边框)是相同的,我们需要最少的 CSS。我们还不需要列表项目本身的任何特定属性,并且项目上没有声明宽度(见图 6-24 ):
.navbar ul {
display: flex;
/* this also implies flex-direction: row; unless told otherwise */
}
图 6-24。使用 flexbox 创建的导航条
如图 6-24 所示,项目水平排列,并根据其中的内容缩小到最小尺寸。看待它的一种方式是,好像我们把块流旋转了 90 度。
项目也聚集在左侧,这是语言方向从左向右时的默认行为。如果我们将 flex-direction 属性更改为 row-reverse,项目将从右边缘开始,从右向左流动(参见图 6-25 )。注意顺序也是颠倒的!
.navbar ul {
display: flex;
**flex-direction: row-reverse;**
}
图 6-25。行中的流动项目-反向
如果没有其他尺寸,flex 容器中的项目会缩小到这个尺寸。这意味着行中的项自动获得最小宽度,列中的项获得最小高度,这两者都基于每个项中内容的最小大小。
对齐和间距
我们可以使用 flexbox 以各种方式沿着行分布项目。Flexbox 称沿主轴分布对齐,横轴分布称对齐。(为了便于记忆,请记住水平方向是默认的,在水平书写系统中,文本对齐也在水平方向上进行。诀窍是当方向改变时要记住哪个是哪个。)
现在我们可以用各种关键字和 justify-content 属性在主轴上分布项目。使项目按当前文本方向(在本例中是从左到右)对齐的默认值称为 flex-start。使用弯曲端使它们移动到另一侧(见图 6-26 ),但这次保持相同的顺序。图 6-27 、 6-28 和 6-29 分别显示了其他关键字:中心、间隔和环绕。
图 6-26。使用 justify-content: flex-end 将项目向右移动
图 6-27。使用 justify-content: center 使 flex 项目居中。额外的空间放置在边缘项目的外侧
图 6-28。使用对齐内容:空格。在项目之间放置额外的空间
图 6-29。使用对齐内容:空格。空间被平均划分并放置在每个项目的两侧。请注意,项目之间的空间不会折叠
Flexbox 不允许您使用这些关键字来调整单个项目。然而,当应用于 flexbox 项目时,用关键字 auto 设置边距值有稍微不同的含义,我们可以在这里使用它。如果某个元素的一侧的 margin 设置为 auto,并且容器中还有剩余空间,则该边距会扩展以填充可用空间。这可用于除一件物品外所有物品都需要放在一边的模式。例如,我们可以将所有项目放在右边,但将“Home”项目放在左边(见图 6-30 ):
.navbar li:first-child {
margin-right: auto;
}
图 6-30。在第一个项目上使用 margin-right: auto 会耗尽所有剩余的空间,将其余的项目推到右边
请注意,像这样使用自动边距会抵消其他项目上任何对齐的影响,因为没有剩余的空间来分配。不过,您仍然可以在其他元素上设置单独的边距。
横轴对齐
到目前为止,我们只处理了水平布局的基本问题,这对于 flexbox 来说轻而易举。Flexbox 还允许我们控制另一个轴的工作方式。如果我们增加 flex 容器本身或其中一个项目的高度,我们会发现默认属性会产生有趣的效果(参见图 6-31 ):
.navbar ul {
**min-height: 100px;**
}
图 6-31。默认情况下,项目将拉伸以填充跨轴维度中的伸缩容器
似乎我们自动拥有了等高的物品!控制跨轴对齐的 align-items 属性的默认值是 stretch。这意味着所有 flex 项目将填满可用空间。我们还可以设置 flex-start、center 或 flex-end 的值(分别参见图 6-32 到 6-34 ),以使项目收缩回其原始大小,并与导航栏的顶部、中部或底部对齐。
图 6-32。使用对齐项目:弹性开始
图 6-33。使用对齐-项目:居中
图 6-34。使用对齐-项目:柔性端
最后,可以使用 baseline 关键字将项目内文本的基线与容器的基线对齐,类似于内联块的默认工作方式。如果您有不同大小的盒子,您希望它们在横轴上放置不同的位置,但它们之间是对齐的,这将非常有用。
在图 6-35 中,我们添加了一个代表当前活动项目的类名:
图 6-35。使用一个 navbar-active 类来显示选中的状态
<ul>
<li><a href="/home">Home</a></li>
<**li class="navbar-active"**><a href="/spaceships">Spaceships</a></li>
<li><a href="/planets">Planets</a></li>
<li><a href="/stars">Stars</a></li>
</ul>
活动项目被赋予了更大的字体大小和 1:
.navbar .navbar-active {
font-size: 1.25em;
}
较大的活动项目现在决定了基线,其他项目也相应地自行对齐。
对齐单个项目
除了将所有项目作为一组对齐之外,您还可以在横轴上为每个项目设置单独的对齐方式。例如,我们可以让“Home”项与左上方对齐,其余项与右下方对齐(参见图 6-36 ):
.navbar ul {
min-height: 100px;
align-items: flex-end;
}
.navbar li:first-child {
**align-self: flex-start;**
margin-right: auto;
}
图 6-36。使用自对齐进行单独对齐
与 Flexbox 垂直对齐
最后,flexbox 对齐用很少的代码解决了垂直对齐问题。当容器中有单个项目时,我们只需要将父容器设置为 flex 容器,然后将我们希望居中的项目的边距声明设置为自动。请记住,在 flex 项目上设置为自动的边距将向所有方向扩展以“填充”。
<div class="flex-container">
<div class="flex-item">
<h2>Not so lost in space</h2>
<p>This item sits right in the middle of its container...<p>
</div>
</div>
我们现在可以将。使用下面的 CSS 水平伸缩项目和垂直伸缩项目,不管容器或项目的大小。在这个例子中,我们让容器和视窗一样高(在 html、body 和。flex-container 元素),只是为了可视化结果,如图 6-37 所示。
html, body {
height: 100%;
}
.flex-container {
height: 100%;
display: flex;
}
.flex-item {
margin: auto;
}
图 6-37。带 flexbox 和自动边距的垂直和水平居中
当 flex 容器中有几个项目时——就像我们的作者元数据示例中一样——我们可以使用对齐属性将它们聚集到水平和垂直中心(参见图 6-38 )。为此,我们将对齐和对齐都设置为居中。(顺便说一下,这也适用于单个项目,但是 margin: auto 方法需要的代码要少一些。)
.author-meta {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
图 6-38。使用 flexbox 轻松实现多个元素的垂直居中
灵活的尺寸
Flexbox 让我们能够更好地控制规模。这是 flexbox 在详细内容布局方面如此出色的部分原因,但也是迄今为止 flexbox 最复杂的部分。不要担心这一部分一开始会让人不知所措——灵活的规模调整是您在“点击”之前需要做的事情之一
灵活的上浆性能
这就是 flexbox 中“flex”的用武之地,正如三个属性 flex-basis、flex-grow 和 flex-shrink 中定义的那样。这些属性是在每个 flex 项目上设置的,而不是在容器上。
- flex-basis 在根据可用空间进行修正之前,规定项目在主轴上的“首选”大小(宽度或高度)。它可以设置为长度(例如 18em)、百分比(基于容器的主轴大小)或关键字 auto(默认值)。
auto 关键字听起来像是将 width 或 height 设置为 auto,但事实并非如此。相反,这意味着项目将从相应的属性(宽度或高度)中获取其主要大小(如果设置了该属性的话)。如果没有设置主大小,元素将根据其内容调整大小,有点像浮点或内联块,
您还可以将值设置为 content,这也将根据项目的内容设置大小,但是将忽略任何设置了宽度或高度的主轴大小(与 auto 不同)。请注意,content 关键字是 flexbox 的新增功能,在撰写本文时对它的支持还不稳定。
-
flex-grow 规定当每个元素通过 flex-basis 被赋予其首选大小时,如果还有剩余空间会发生什么:你向它提供一个数字,称为伸缩因子,它作为额外空间的一部分计算出来。我们马上会解释分数是如何工作的。flex-grow 的默认值为 0,这意味着项目的增长不会超过它们从 flex-basis 获得的大小。
-
flex-shrink 的工作方式与 flex-grow 类似,但方向相反:如果没有足够的空间,元素将如何收缩?当伸缩开始起作用时,计算就有点复杂了——我们将在后面进一步讨论。默认值为 1,这意味着如果没有足够的空间,所有项目都将与其首选大小成比例缩小。
理解 flex-basis 如何处理 flex-grow 和 flex-shrink 是棘手的部分。Flexbox 使用一种相当复杂的算法来计算大小,但是如果我们将其简化为两个步骤,就会更容易处理:
-
通过查看弹性基准来确定假设的主要尺寸。
-
确定实际主要尺寸。如果在将项目放入具有假设主要大小的容器后,容器中还有任何剩余空间,它们可以增长。这一增长基于弹性增长因素。出于同样的原因,如果空间太小无法容纳它们,这些项目可以根据伸缩系数缩小。
我们可以通过一个例子把这些属性拼凑起来。在这个例子中,我们想象一个 1000 像素宽的容器。标记中的容器内有两个子项。其中一个包含一个短单词,导致这个特定的元素占据 200 像素的宽度。另一个包含一个长单词,宽度为 400 像素(见图 6-39 )。这些物品还没有放入容器中。
图 6-39。一个 1000 像素宽的 flex 容器,以及两个尚未放入容器的 flex 项目
如果这些项目的 flex-basis 设置为 auto,并且没有声明显式宽度值,如下所示,则当它们被放入容器时,它们将保留基于内容的大小(参见图 6-40 ),总共占用 600 个像素的可用宽度。这是 flex-basis 的默认行为,与我们到目前为止在导航栏示例中看到的一样。
.navbar li {
**flex-basis: auto;** /* default value. */
}
图 6-40。这些项目总共占用了可用的 1000 个像素中的 600 个像素,留下了 400 个像素的未使用空间
由于还有空间可供分配,灵活增长开始发挥作用。默认情况下,flex-grow 设置为 0,这不会改变项目的大小。但是当我们将两个项目的 flex-grow 都设置为 1 时会发生什么呢?
.navbar li {
flex-basis: auto;
**flex-grow: 1;**
}
1 和 0 代表什么?嗯,这有点像鸡尾酒配方:1 份这个,2 份这个,3 份苏打水。它并不代表一个特定的度量,只是整体的一部分。
在这种情况下,有两个项目。两者现在将相等地增加可用空间的1%,这意味着它们都将增加剩余空间的一半,即 200 个像素。这意味着第一个项目将被调整到 400 像素的最终大小,第二个项目将是 600 像素,加起来正好填满容器,如图 6-41 所示。
图 6-41。两个项目都增加了剩余 400 像素的一部分,即每个项目增加 200 像素
我们还可以为项目设置单独的弹性增长系数:
.navbar li:first-child {
**flex-grow: 3;**
}
.navbar li:last-child {
**flex-grow: 1;**
}
这将导致第一个项目获得可用空间的四分之三,第二个项目获得四分之一。结果,这两个项目最终都是 500 像素宽!图 6-42 显示了在这种情况下,布局算法如何调整尺寸。
图 6-42。第一项将增加可用空间的四分之三,而第二项仅增加四分之一
本例中的项目碰巧以同样的宽度结束。如果我们希望项目之间按比例划分整个空间,而不考虑内容,有更好的 flexbox 技术,我们接下来会发现。
纯粹根据灵活因素进行规模调整
在我们在上一节中使用的简化 flexbox 布局算法的第一步中,项目的大小基于其内容的宽度,使用 flex-basis of auto,没有显式的宽度声明。如果我们假设弹性基数为 0,那么结果将是在第一步中没有空间被分配。容器内的所有空间将保留在步骤 2 中,根据伸缩系数进行划分,并设置项目的最终大小。
在图 6-43 中,项目的弹性基数为 0,弹性增长设置为 1。这意味着要划分的总空间由两部分组成,因此每个项目将正好占据分配空间的一半。这种效果接近于为布局计算和使用百分比,额外的好处是 flexbox 不在乎有多少个项目,它们会自动调整大小以适应总宽度。
图 6-43。弹性基础设置为 0 的两个项目在算法的第一步中将占用零空间。然后,将完全根据它们的伸缩系数来确定它们的大小
这一次,我们将使用 flex 简写来同时设置 flex-grow、flex-shrink 和 flex-basis,按顺序声明并用空格分隔:
.navbar li {
**flex: 1 0 0%;**
}
注意值中 flex-basis 最后一个后面的百分号:速记中的 flex-basis 不能是无单位的,所以在这个实例中必须使用 0%或另一个单位,如 0px。
如果我们希望第一个项目占用的空间是任何其他项目的两倍,我们可以给它一个伸缩因子 2:
.navbar li {
flex: 1 0 0%;
}
**.navbar li:first-child {**
**flex-grow: 2;**
**}**
将这一点应用到前面的带有四个项目的 navbar 标记中,我们得到一个 navbar,其中第一个项目占宽度的五分之二(或 40%),接下来三个项目各占五分之一(或 20%)(见图 6-44 )。
图 6-44。navbar 示例,其中第一个项目设置为增长 2 个单位,其余项目设置为增长 1 个单位
收缩弹性项目
当要放入 flex 容器中的项目加起来超过可用空间时,我们可以允许它们基于 flex-shrink 属性进行收缩。机制比 flex-grow 更复杂一些。缩小项目的更复杂规则背后的想法是防止小项目缩小到零,只是因为较大的项目导致总宽度过大。允许一个项目有更多的空间是非常简单的(正如我们在 flex-grow 中看到的),并且与可用空间成比例。当收缩发生时,它的方式略有不同。
回到我们假设的 1000 像素宽的导航栏,让我们假设有两个子项目,每个项目都有一个通过 flex-basis 设置的首选大小。它们一起超出容器宽度 300 个像素,如图 6-45 所示。
.navbar li:first-child {
flex: 1 1 800px;
}
.navbar li:last-child {
flex: 1 1 500px;
}
图 6-45。两个弹性物料,其组合弹性基础超出了容器宽度
组合的首选宽度(800 + 500 = 1300)超出了容器的大小 300 个像素,并且两项的 flex-shrink 值都为 1。你可能会认为这两个项目都缩小了 150 像素以腾出空间,但这不会发生。取而代之的是,每一项都将按伸缩系数和伸缩基数的比例收缩。从技术上讲,每个项目的伸缩系数都要乘以伸缩基数。接下来,该值除以每个项目的伸缩因子与其伸缩基准的乘积之和。最后,除法的结果乘以负的空间,给出我们要缩小的空间量。
这是你要记住的很多东西,但要点是:首选尺寸较大的项目比首选尺寸较小的项目收缩更多(相对于伸缩系数)。即使我们两个项目的伸缩系数都是 1,它们也会收缩不同的量。如果我们完成第一项的计算,结果如下:
((800 × 1) / ((800 × 1) + (500 × 1))) * 300 = 184.6
第一项会缩小 184.6 像素。对第二个项目进行同样的计算,我们应该得到余数:
((500 × 1) / ((800 × 1) + (500 × 1))) * 300 = 115.4
…这意味着第二个项目将缩小 115.4 个像素,总共减少了 300 个像素,以适应 flex 容器中的两个项目(参见图 6-46 )。
图 6-46。更复杂的伸缩计算
在使用 flexbox 时,您需要牢记这一点吗?最有可能的答案是“不”。但是如果你正在努力使一个布局工作,意识到伸缩与伸缩的工作方式不同可能会防止你抓狂。
包装 Flexbox 布局
在导航栏和作者元数据示例中,我们只处理了一行内容。就像内联块或浮动一样,flexbox 允许我们将内容分成几行(或几列),但是增加了控制。
警告
包装成多行或多列的属性来自较新版本的规范。支持老的 flexbox 规范的浏览器,比如 Safari 的老版本,4.4 版之前的存量 Android 浏览器,28 版之前的 Firefox,都不支持换行。
这一次,我们将使用一个标签列表,代表行星的类别。这是一个带有链接的无序列表,很像导航栏,但是条目的数量可能会大得多,因此无法将它们都放在一行中。我们给每个项目一个背景颜色,和一个物理行李标签的形状外观,使用我们用于评论气泡的相同类型的伪元素技巧(见图 6-47 )。
图 6-47。我们的标签列表
标记非常简单:
<ul class="tags">
<li><a href="/Binary_planet">Binary planet</a></li>
<li><a href="/Carbon_planet">Carbon planet</a></li>
<!-- …and so on… -->
</ul>
标签的样式稍微复杂一点,但是没有我们以前没见过的:
.tags {
border: 1px solid #C9E1F4;
margin: 0;
padding: 1em;
list-style: none;
}
.tags li {
display: inline-block;
margin: .5em;
}
.tags a {
position: relative;
display: block;
padding: .25em .5em .25em .25em;
background-color: #C9E1F4;
color: #28448F;
border-radius: 0 .25em .25em 0;
line-height: 1.5;
text-decoration: none;
text-align: center;
}
.tags a:before {
position: absolute;
content: '';
width: 0;
height: 0;
border: 1em solid transparent;
border-right-width: .5em;
border-right-color: #C9E1F4;
left: -1.5em;
top: 0;
}
使用前面的样式,标记被声明为内联块,并且将很好地换行。现在是时候对 flexbox 进行增强了。首先,我们将列表转换成一个 flex 容器,并告诉它允许使用设置为 wrap 的 flex-wrap 属性换行:
.tags {
**display: flex;**
**flex-wrap: wrap;**
margin: 0;
padding: 0;
list-style: none;
}
在这一点上,这个列表看起来和它最初的样子非常相似。但是现在我们拥有 flexbox 的所有功能来控制行的方向、大小和对齐。
包装和方向
首先,我们可以反转行的方向,就像我们最初对导航栏所做的那样。当 flex-direction 更改为 row-reverse 时,项目将从右上方开始,从右向左流动,绕成右对齐的行,如图 6-48 所示。
图 6-48。用弯曲方向反转流动:行反转
我们也可以反转垂直流,使行从底部开始,向上缠绕!在图 6-49 中,flex-direction 设置为 row-reverse,flex-wrap 设置为 wrap-reverse。
图 6-49。使用 wrap-reverse 关键字从下到上排列内容
注意
Flexbox 方向是逻辑方向,这意味着它们依赖于文本方向作为开始和结束边缘。例如,如果您正在构建一个具有从右向左文本的阿拉伯语网站,水平方向将被反转(假设您在标记中设置了正确的 dir 属性),而垂直方向保持不变。
包装布局中的灵活大小调整
多行 flexbox 布局的另一个好处是灵活的尺寸允许我们均匀地填充行(见图 6-50 )。flex-grow 计算是基于每一行进行的,因此项目将仅增长到填充当前行所需的量。
.tags li {
**flex: 1 0 auto;**
}
图 6-50。应用伸缩因子创建完美填充的行
当以稍微不同的尺寸观看时,最后一个项目包裹在最后一行上,变得不舒服地宽(参见图 6-51 )。不幸的是,在包装 flexbox 布局中没有处理特定行的机制。例如,如果项目在最后一行,我们不能让它们变得不灵活。
图 6-51。当最后一个标签自己换行到最后一行时,它会变得非常宽,并不断增长以填充空间
我们将通过在标签上设置 max-width 属性来解决眼前的问题,以便它们在一定的限制内保持灵活性(参见图 6-52 ):
.tags li {
display: inline-block;
margin: .5em;
flex: 1 0 auto;
**max-width: 14em;**
}
图 6-52。通过在标签项目上设置合理的最大宽度,我们可以防止项目增长到令人不舒服的长度
通常,填充可用空间的能力是 flexbox 的核心优势。将 flex-grow 与最小和最大宽度相结合,我们可以构建非常智能的包装 flexbox 布局,无论屏幕大小或物品数量如何,物品都保持在合理的尺寸范围内。我们将在第八章中深入探讨这方面的技术,在那里我们将讨论响应式网页设计,以及如何使布局适应它们的环境。
对齐所有行
在我们之前对跨轴对齐属性(align-items 和 align-self)的回顾中,我们看到了 flexbox 如何允许我们根据单行的 flex-start、center、baseline 和 flex-end 点来对齐项。在换行布局中,我们可以根据容器对齐行或列本身。
如果我们将标记列表容器的最小高度设置为 300px,那么 align-content 属性的效果就很明显了。默认情况下,它被设置为 stretch,这意味着每一行都将被拉伸以填充其在容器高度中的份额。如果我们检查物品,我们可以看到每个 li 元素将伸展以填充三分之一的高度,如图 6-53 所示。
.tags {
display: flex;
flex-wrap: wrap;
**min-height: 300px;**
**/* align-content: stretch; is implied here */**
}
图 6-53。拉伸每一行,以便所有行的组合填充容器
align-content 的效果与使用 justify-content 在主轴上分布内容的方式非常相似。我们现在可以将内容分布到 flex-start(容器的顶部)、flex-end(底部)、center(聚集到中间),或者使用 space-between 或 space-around 分隔。
列布局和单独排序
使用 flexbox 订单属性,您可以完全摆脱来源订单。你可以简单地告诉浏览器你想要的盒子的顺序。默认情况下,所有项的顺序值为 0,具有相同顺序值的项按照它们在源中出现的顺序排序。
Flexbox 让我们可以完全控制订单。在我们的下一个 flexbox 示例中,我们将离开水平布局技术,并创建一个小的“文章预告”组件,其中显示了我们的飞船文章的摘录,以及标题、图像和继续阅读全文的链接。我们将把它显示为单列。
从标记开始,我们将按重要性顺序排列每个组成部分:
-
带有文章标题的标题
-
摘要文本
-
文章题目的一幅图像插图
-
文章的链接
<div class="article-teaser">
<h2>The Dragon and other spaceships</h2>
<div class="article-teaser-text">
<p>There are actual spaceships…</p>
</div>
<img src="img/medium_spaceship.jpg" alt="The Dragon spaceship in orbit around Earth.">
<p class="article-teaser-more">
<a href="/spaceships">Read the whole Spaceship article</a>
</p>
</div>
文章预告如图 6-54 所示。我们为这个组件添加了一些基本的样式,主要是处理边距、颜色和排版。这种特殊的样式对于这个例子来说并不重要,所以我们暂时不考虑它。
图 6-54。我们的文章摘要组件的第一次迭代
视觉上,把图像放在第一位,抓住潜在读者的眼球,也许对设计有益。但是就标记而言,把图像放在前面不太合理。例如,我们可能希望屏幕阅读器的用户将文章标题作为摘要中的第一个元素。
为了实现这种重新排序,我们需要将。文章摘要容器到 flexbox 列:
.article-teaser {
display: flex;
flex-direction: column;
}
接下来,我们给图像一个低于缺省值 0 的顺序值,这样它首先出现(见图 6-55 ):
.article-teaser img {
order: -1;
}
图 6-55。我们重新排序的文章预告
例如,如果我们首先需要标题,我们可以在标题和图像上设置顺序值:
.article-teaser h2 {
order: -2;
}
.article-teaser img {
order: -1;
}
…其余项目将保持不变,因为它们保留订单值 0。您设置的顺序值不必是连续的(我们可以分别为 header 和 image 使用-99 和-6),它们可以是正数也可以是负数。只要它们是可以比较的数字,这些项目就会相应地重新排序。请记住 0 是默认值。
警告
值得强调的是,使用 flexbox 重新排序项目只是一种视觉上的转变。像 tab 键顺序和屏幕阅读器朗读内容的顺序这样的事情不会被 order 属性改变。由于这个原因,确保 HTML 源代码仍然是合乎逻辑的,而不是将 flexbox 作为草率标记实践的借口是很重要的。
嵌套 Flexbox 布局
作为我们的最后一个例子,我们将展示 flexbox 布局可以嵌套,并带来一些真正有用的结果。
我们将重复使用文章预告的例子,但是这一次有两个预告,我们想把它们放在一起展示。为此,我们将添加一个包装元素,设置为显示为 flexbox 行:
<div class="article-teaser-group">
<div class="article-teaser">
<!-- first article teaser contents… -->
</div>
<div class="article-teaser">
<!-- second article teaser contents… -->
</div>
</div>
包装元素设置为显示为 flexbox 行:
.article-teaser-group {
display: flex;
}
在图 6-56 中,我们可以看到现在熟悉的效果,默认情况下,flex 项目将在横轴方向拉伸,创建两个等高的文章预告。
图 6-56。我们的两篇文章摘要现在是嵌套的 flexbox 列,也作为 flexbox 行中的项目
我们以前见过使用 flexbox 的等高物品。但是当物品也是像这样的 flexbox 容器时,我们可以玩最后一个把戏。我们可以看到,第二个预告中的内容比第一个短得多,这在两个预告中的高对比度“阅读更多”链接组件之间造成了不平衡的印象。在这种情况下,Flexbox 也可以帮助我们。
还记得在伸缩项上设定为自动的边距会在每个方向上耗尽所有剩余空间吗?在“read more”元素上设置 margin-top: auto 会将其推到列的底部,使其与其旁边的组件对齐(参见图 6-57 ):
.article-teaser-more {
margin-top: auto;
}
图 6-57。在链接上使用 margin-top: auto 会将它推到列的底部,创建一个更整洁的印象
这是一种动态内容的布局,如果使用浮动、内联块和定位等旧技术,这种布局会很麻烦。当 flexbox 不受支持时,它会退回到更简单但完全可行的设计——这很好地引导我们进入下一个主题。
Flexbox 倒回
虽然 flexbox 在理论上得到广泛支持,但是仍然会有一些情况,您可能希望依靠像 floats 或 inline blocks 这样的技术。您可能需要支持旧版本的 Internet Explorer(IE10 之前)。可能会有一些浏览器错误阻止您实现 flexbox 布局,即使是在声称支持的浏览器之间。或者,您可能想要一种与旧的 Android 手机一致的包装行为。你明白了。
幸运的是,在 flexbox 的设计中有一些智慧,可以让你实现这些回退。
首先,由于 flexbox 是容器上的一种显示模式,不理解 flex 关键字的浏览器会忽略它。这意味着您可以让不支持的浏览器将容器本身显示为普通块。
其次,您可以向 flex 项目添加浮点声明,或者将它们设置为显示为内嵌块,而不会影响 flexbox 布局。float 和 clear 属性对 flex 项目没有影响,设置不同的显示值不会影响框的布局。这给了你一个很好的机会开始使用 flexbox 进行水平布局。首先,您将创建一个适用于任何地方的简单布局,然后使用 flexbox 增强它——例如,利用自动边距、垂直对齐或其他漂亮的修饰。
在某些情况下,您可能想单独区分理解 flexbox 的浏览器和不理解 flexbox 的浏览器。在这种情况下,我们建议您使用像 Modernizr(modernizr.com
)这样的 JavaScript 库来检测浏览器的功能,为您提供作为样式基础的类名挂钩。我们将在第七章中更仔细地看看 Modernizr 技术。
如果您只关心该规范在最新浏览器中的最新实现,您也可以使用@supports 符号,这是专门为区分基于浏览器支持的样式而设计的:
@supports (flex-wrap: wrap) {
/* flexbox rules here */
}
在这种情况下,我们将@supports 块限制在既理解条件规则语法又理解 flex-wrap: wrap 声明的浏览器中,该声明只存在于实现最新语法的浏览器中。有很多浏览器理解 flexbox 的变体,但不支持@supports,反之亦然。当您只想应用 flexbox 的一些非常新的方面,或者解决旧实现中的错误时,这种构造会非常方便。
这种技术的重要方面是将更简单的后备解决方案作为基线,然后在此基础上构建 flexbox 增强功能。
Flexbox 错误和陷阱
由于 flexbox 相当新,并且已经经历了不同语法的多次迭代,所以有相当多的错误和不一致需要考虑。
为了跟踪稍老的浏览器中的 flexbox bugs,请查看 Philip Walton 的社区管理的“Flexbugs”库(github.com/philipwalton/flexbugs
),其中列出了 bug 和解决方法。
除了纯粹的 bug,还有一些其他的东西可能会让你犯错:
-
当图像、视频和其他具有固有长宽比的对象成为 flex 项目时,调整它们的大小是很棘手的。随着时间的推移,规范在这方面已经发生了变化,所以最好的办法可能是在这些对象周围添加一个包装器元素,让包装器充当 flex 项目。
-
Flex 项目也有所谓的隐含最小宽度。实际上,这意味着 flex 项目可能会拒绝缩小到基于内容的大小以下,尽管它被告知可以通过灵活调整大小来这样做。重写 min-width 属性或设置显式主尺寸会重写此行为。
-
flex 项目的绘制顺序由 order 属性决定(如果存在)。这可能会影响项目的重叠,就像 z 索引一样。
-
此外,与普通块不同,flex 项可以被赋予一个 z 索引,而不必赋予它们一个非静态的位置。如果给定 z 索引,该索引将覆盖堆叠顺序。设置了 z 索引的伸缩项还会创建新的堆叠上下文。
-
有些元素的渲染模型有点不正常。例如,button 和 fieldset 元素的默认呈现并不完全遵循 CSS 样式的一般规则。试图让这些元素充当 flex 容器可能会严重失败。
摘要
在这一章中,我们已经研究了几种常见的内容布局模式和它们的各种用例。我们已经看到了如何将内联块、表格显示模式和浮动用于布局目的,以及它们各自的利弊。
我们还研究了一些使用绝对或相对定位的有用模式,并结合边距来实现一些有效的模式。
最后,我们深入研究了 flexbox 标准,找到了更有效的方法来横向和纵向分发、调整、对齐和排序项目。
在接下来的两章中,我们将在布局方面加大努力。首先,我们将看看如何将布局技术应用到整个页面的布局系统中,并了解专门为该场景创建的新网格模块。然后,我们将会看到如何使用响应式网页设计的技术,使我们的设计适应不同的屏幕尺寸。