第 2 章 相对单位

4 篇文章 0 订阅

1 相对值的好处

CSS为网页带来了后期绑定(late-binding)的样式:直到内容和样式都完成了,二者才会结合起来、

1.1 那些年追求的像素级完美

在很长时间里,网页设计者通过聚焦到“像素级完美”的设计来降低这种复杂性。他们会创建一个紧凑的容器,通常是居中的一栏,大约800px宽。然后再像之前的本地应用程序或者印刷出版物那样,在这些限制里面进行设计。

1.2 像素级完美的时代终结了

等到智能手机出现后,开发人员再也无法假装每个用户访问网站的体验都能一样。不管我们喜欢与否,都得抛弃以前那种固定宽度的栏目设计,开始考虑响应式设计。我们无法逃避CSS带来的抽象性。我们得拥抱它。

响应式——在CSS中指的是样式能够根据浏览器窗口的大小有不同的“响应”。这要求有意地考虑任何尺寸的手机、平板设备,或者桌面屏幕

像素、点、派卡
CSS支持几种绝对长度单位,最常用、最基础的是像素(px)。不常用的绝对单位是mm(毫米)、cm(厘米)、in(英寸)、pt(点,印刷术语,1/72英寸)、pc(派卡,印刷术语,12点)。这些单位都可以通过公式互相换算:1in = 25.4mm = 2.54cm = 6pc = 72pt = 96px。因此,16px等于12pt(16/96×72)。设计师经常用点作为单位,开发人员则习惯用像素。因此跟设计师沟通的时候需要做一些换算。

像素是一个具有误导性的名称,CSS像素并不严格等于显示器的像素,尤其在高清屏(视网膜屏)下。尽管CSS单位会根据浏览器、操作系统或者硬件适当缩放,在某些设备或者用户的分辨率设置下也会发生变化,但是96px通常等于一个物理英寸的大小。

2 em和rem

2.1 em

2.1.1 其它属性

em是最常见的相对长度单位,适合基于特定的字号进行排版。

在CSS中,1em等于当前元素的字号

,其准确值取决于作用的元素。图2-1是一个内边距为1em的div元素。
在这里插入图片描述
图2-1 内边距为1em的元素(虚线用于展示内边距)
它的代码如代码清单2-1所示。规则集指定了字号为16px,也就是元素局部定义的1em。然后使用em指定了元素的内边距。将代码清单2-1加入一个新的样式表,在<div class="padded">中写一些文字,在浏览器中看看会是什么效果。
代码清单2-1 用em单位设置内边距

.padded {
  font-size: 16px;         
  padding: 1em;    ←---- 设置四个内边距为font-size
}

这里设置内边距的值为1em。浏览器将其乘以字号,最终渲染为16px。这一点很重要:浏览器会根据相对单位的值计算出绝对值,称作计算值(computed value)。
在本例中,设置内边距为2em,会产生一个32px的计算值。如果另一个选择器也命中了相同的元素,并修改了字号,那么就会改变em的局部含义,计算出来的内边距也会随之变化。

当设置padding、height、width、border-radius等属性时,使用em会很方便。这是因为当元素继承了不同的字号,或者用户改变了字体设置时,这些属性会跟着元素均匀地缩放。

图2-2展示了两个不同大小的盒子,它们的字号、内边距和圆角都会不一样。
在这里插入图片描述
元素的内边距和圆角都是相对值
在定义这些盒子的样式时,可以用em指定内边距和圆角。给每个元素设置1em的内边距和圆角,再分别指定不同的字号,那么这些属性会随着字体一起缩放。

如代码清单2-2所示,在HTML中创建两个盒子。给元素分别添加box-small和box-large类名,作为大小修饰符。
代码清单2-2 给不同的元素加上em(HTML)

<span class="box box-small">Small</span>
<span class="box box-large">Large</span>

现在将代码清单2-3加到样式表中。这段代码用em定义了一个盒子,同时定义了一个small和一个large的修饰符,分别指定不同的字号。
代码清单2-3 将em应用于不同的元素(CSS)

在这里插入代码片.box {
  padding: 1em;
  border-radius: 1em;
  background-color: lightgray;
}

.box-small {
  font-size: 12px;     (以下5行)不同的字号,可以决定元素的em值
}                           

.box-large {                
  font-size: 18px;       
}

这就是em的好处。可以定义一个元素的大小,然后只需要改变字号就能整体缩放元素。稍后会再举一个例子,在此之前,我们先说说em和字号。

2.1.2 使用em定义字号

谈到font-size属性时,em表现得不太一样。之前提到过,当前元素的字号决定了em。但是,如果声明font-size: 1.2em,会发生什么呢?一个字号当然不能等于自己的1.2倍。实际上,这个font-size是根据继承的字号来计算的。

举个简单的例子。如图2-3所示,有两段文字,分别有不同的字号。可以像代码清单2-4那样定义元素,然后使用em定义字号。
在这里插入图片描述
图2-3 使用em定义两种不同的字号
按照代码清单2-4修改网页。第一行文字在标签中,因此它会按照body的字号来渲染。第二段的slogan继承了这个字号。
代码清单2-4 使用相对font-size的标记

<body>
  We love coffee                             
  <p class="slogan">We love coffee</p>   ←---- slogan继承了<body>的字号
</body>

代码清单2-5指定了元素的字号。简单起见,这里用像素单位。接下来使用em来放大slogan的字号。
代码清单2-5 使用em定义font-size

body {
  font-size: 16px;
}

.slogan {       (以下3行)计算结果为元素继承的字号的1.2倍
  font-size: 1.2em;    
}

slogan的指定字号是1.2em。为了得到计算的像素值,需要参考继承的字号,即16px。因为16×1.2 = 19.2,所以计算值为19.2px。

提示 如果知道字号的像素值,但是想用em声明,可以用一个简单的公式换算:用想要的像素大小除以父级(继承)的像素字号。比如,想要一个10px的字体,元素继承的字体是12px,则计算结果是10/12 = 0.8333em。如果想要一个16px的字体,父级字号为12px,则计算结果是16/12 = 1.3333em。在本章我们还会进行几次这样的计算。
了解这些非常有用。对大多数浏览器来说,默认的字号为16px。准确地说,medium关键字的值是16px。

01 em同时用于字号和其他属性
现在你已经用em定义了字号(基于继承的字号),而且也用em定义了其他属性,比如padding和border-radius(基于当前元素的字号)。em的复杂之处在于同时用它指定一个元素的字号和其他属性。这时,浏览器必须先计算字号,然后使用这个计算值去算出其余的属性值。这两类属性可以拥有一样的声明值,但是计算值不一样。

在前面的例子里,字号的计算值为19.2px(继承值16px乘以1.2em)。图2-4展示了相同的slogan元素,但是内边距为1.2em,背景为灰色,这样能明显地看到内边距的大小。内边距比字号稍微大一些,尽管它们的声明值相同。
在这里插入图片描述
图2-4 字号为1.2em和内边距为1.2em的元素
这是因为该段落从body继承了16px的字号,最终字号的计算值为19.2px。因此19.2px是em的局部值,用于计算内边距。按照代码清单2-6更新测试页面的样式表。
代码清单2-6 使用em定义font-size和padding

body {
  font-size: 16px;
}

.slogan {                   
  font-size: 1.2em;      ←---- 计算值为19.2px       
  padding: 1.2em;        ←---- 计算值为23.04px
  background-color: #ccc;
}

在这个例子里,padding的声明值为1.2em,乘以19.2px(当前元素的字号),得到计算值为23.04px。尽管font-size和padding的声明值相同,计算值却不一样。

02 字体缩小的问题
当用em来指定多重嵌套的元素的字号时,就会产生意外的结果。为了算出每个元素的准确值,就需要知道继承的字号,如果这个值是在父元素上用em定义的,就需要知道父元素的继承值,以此类推,就会沿着DOM树一直往上查找。

当使用em给列表元素定义字号并且多级嵌套时,这个问题就显现出来了。绝大部分Web开发人员曾遇到过类似于图2-5的现象。文字缩小了!正是这种问题让开发人员惧怕使用em。
在这里插入图片描述
图2-5 嵌套列表的文字缩小了

当列表多级嵌套并且给每一级使用em定义字号时,就会发生文字缩小的现象。代码清单2-7和代码清单2-8的例子里,设置无序列表的字号为0.8em。选择器选中了网页上每个<ul>元素,因此当这些列表从其他列表继承字号时,em就会逐渐缩小字号。

代码清单2-7 使用em指定列表的字号

body {
  font-size: 16px;
}

ul {
  font-size: .8em;
}

代码清单2-8 嵌套列表

<ul>
  <li>Top level
    <ul>           (以下2行)这个列表嵌套在第一个列表中,继承它的字号
      <li>Second level     
        <ul>          (以下2行)这个嵌套在上一个列表中,继承第二个列表的字号
          <li>Third level      
            <ul>       (以下2行)以此类推
              <li>Fourth level    
                <ul>
                  <li>Fifth level</li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

每个列表元素的字号等于0.8乘以其父元素的字号。算出来第一级列表的字号为12.8px,第二级缩小到10.24px(12.8px × 0.8),第三级缩小到8.192px,以此类推。同理,如果指定一个大于1em的字号,文字会逐渐增大。我们想要的是指定顶部的字号,然后保持子级的字号一致,如图2-6所示。

在这里插入图片描述
实现这种效果的代码如代码清单2-9所示。它设置第一级列表的字体为0.8em(跟代码清单2-7一致)。代码清单2-9里的第二个选择器选中了嵌套在某个无序列表中的所有无序列表,也就是除了顶级列表以外的其他列表。嵌套列表的字号等于其父级的字号,如图2-6所示。
代码清单2-9 纠正文字缩小的问题

ul {
  font-size: 0.8em;
}

ul ul {         (以下3行)嵌套的列表应当跟其父级的字号一致
  font-size: 1em;     
}

这样确实解决了问题,尽管这个方式不完美。设置一个值,然后马上用另一个规则覆盖。如果不用提升选择器的优先级来覆盖规则,就更好了。

这些例子告诉我们,如果不小心的话,em就会变得难以驾驭。em用在内边距、外边距以及元素大小上很好,但是用在字号上就会很复杂。值得庆幸的是,我们有更好的选择:rem。

2.2 使用rem设置字号

当浏览器解析HTML文档时,会在内存里将页面的所有元素表示为DOM(文档对象模型)。它是一个树结构,其中每个元素都由一个节点表示。<html>元素是顶级(根)节点。它下面是子节点,<head><body>。再下面是逐级嵌套的后代节点。
在文档中,根节点是所有其他元素的祖先节点。根节点有一个伪类选择器(:root),可以用来选中它自己。这等价于类型选择器
html,但是html的优先级相当于一个类名,而不是一个标签。

rem是root em的缩写。rem不是相对于当前元素,而是相对于根元素的单位。不管在文档的什么位置使用rem,1.2rem都会有相同的计算值:1.2乘以根元素的字号。代码清单2-10先指定了根元素的字号,然后用rem定义了无序列表的相对字号。
代码清单2-10 使用rem指定字号

:root {                       ←---- :root伪类等价于类型选择器html
  font-size: 1em;       ←---- 使用浏览器的默认字号(16px)
}                          

ul {                        
  font-size: .8rem;
}

在这个例子里,根元素的字号为浏览器默认的字号16px(根元素上的em是相对于浏览器默认值的)。无序列表的字号设置为0.8rem,计算值为12.8px。因为相对根元素,所以所有字号始终一致,就算是嵌套列表也一样。

可访问性:对字号使用相对单位

有些浏览器给用户提供了两种方式来设置文字大小:缩放操作和设置默认字号。按住Ctrl+或Ctrl-,用户可以缩放网页。这种操作会缩放所有的字和图片,让网页整体放大或者缩小。在某些浏览器中,这种改变只会临时对当前标签页生效,不会将缩放设置带到新的标签页。

设置默认字号则不一样。不仅很难找到设置默认字号的地方(通常在浏览器的设置页),而且用这种方式改变字号会永久生效,除非用户再次修改默认值。这种方式的缺点是,它不会影响用px或者其他绝对单位设置的字号。由于默认的字号对某些用户而言很重要,尤其是对视力受损的人,所以应该始终用相对单位或者百分比设置字号。
与em相比,rem降低了复杂性。实际上,rem结合了px和em的优点,既保留了相对单位的优势,又简单易用。那是不是应该全用rem,抛弃其他选择呢?答案是否定的。

在CSS里,答案通常是“看情况”。rem只是你工具包中的一种工具。掌握CSS很重要的一点是学会在适当的场景使用适当的工具。我一般会用rem设置字号,用px设置边框,用em设置其他大部分属性,尤其是内边距、外边距和圆角(不过我有时用百分比设置容器宽度)。
这样字号是可预测的,同时还能在其他因素改变元素字号时,借助em缩放内外边距。用px定义边框也很好用,尤其是想要一个好看又精致的线时。这些是我在设置各种属性时常用的单位,但它们仅仅是工具,在某些情况下,用其他工具会更好。
提示 拿不准的时候,用rem设置字号,用px设置边框,用em设置其他大部分属性。

2.3 停止像素思维

过去几年有一个常见的模式,更准确地说是反模式,就是将网页根元素的字号设置为0.625em或者62.5%(如代码清单2-11所示)。
代码清单2-11 反模式:全局重置font-size为10px

html {
  font-size: .625em;
}

我不推荐这样写。代码清单2-11将浏览器的默认字号16px缩小为10px。这的确能简化计算:如果设计师希望字号为14px,那么只需要默默在心里除以10,写上1.4rem就可以了,而且还使用了相对单位。

一开始,这会很方便,但是这样有两个缺点。第一,我们被迫写很多重复的代码。10px对大部分文字来说太小了,所以需要覆盖它,最后就得给段落设置1.4rem,给侧边栏设置1.4rem,给导航链接设置1.4rem,等等。这样一来,代码容易出错的地方更多;当需要修改代码时,要改动的地方更多;样式表的体积也更大。

第二,这种做法的本质还是像素思维。虽然在代码里写的是1.4rem,但是在心里仍然想着“14像素”。在响应式网页中,需要习惯“模糊”值。1.2em到底是多少像素并不重要,重点是它比继承的字号要稍微大一点。如果在屏幕上的效果不理想,就调整它的值,反复试验。这种方式同样适用于像素值。(在第13章中,我们将进一步研究具体规则来改进这种方法。)

2.3.1 设置一个合理的默认字号

如果你希望默认字号为14px,那么不要将默认字体设置为10px然后再覆盖一遍,而应该直接将根元素字号设置为想要的值。将想要的值除以继承值(在这种情况下为浏览器默认值)是14/16,等于0.875。

将代码清单2-12加入到一个新的样式表中,作为基础样式。代码清单2-12设置了根元素()的默认字体。
代码清单2-12 设置真正的默认字号

:root {        ←----  使用HTML选择器
  font-size: 0.875em;     ←---- 14/16(理想的px/继承的px)=0.875
}

现在你已经给网页设置了想要的字号,不用在其他地方再指定一遍了。你只需要相对它去修改其他元素(比如标题)的字号。

接下来创建一个如图2-7所示的面板。基于根元素的14px字号,用相对单位来构建这个面板。
在这里插入图片描述

3 视口的相对单位

前面介绍的em和rem都是相对于font-size定义的,但CSS里不止有这一种相对单位。还有相对于浏览器视口定义长度的视口的相对单位。

视口——浏览器窗口里网页可见部分的边框区域。它不包括浏览器的地址栏、工具栏、状态栏。
如果你不熟悉视口的相对单位,请先看下面的简单介绍。

  • vh:视口高度的1/100。
  • vw:视口宽度的1/100。
  • vmin:视口宽、高中较小的一方的1/100(IE9中叫vm,而不是vmin)。
  • vmax:视口宽、高中较大的一方的1/100

比如,50vw等于视口宽度的一半,25vh等于视口高度的25%。vmin取决于宽和高中较小的一方,这可以保证元素在屏幕方向变化时适应屏幕。在横屏时,vmin取决于高度;在竖屏时,则取决于宽度。
图2-10展示了一个正方形元素在不同屏幕尺寸的视口中的样子。它的宽度和高度都是90vmin,等于宽高的较小边的90%,即横屏高度的90%,或者竖屏宽度的90%。

在这里插入图片描述
当一个元素的宽和高为90vmin时,不管视口的大小或者方向是什么,总会显示成一个稍小于视口的正方形
代码清单2-18是该元素的CSS样式。它生成了一个大正方形,不管如何缩放浏览器,它都能在视口中显示。可以在网页里加上

来看效果。
代码清单2-18 用vmin定义正方形元素的大小

.square {
  width: 90vmin;
  height: 90vmin;
  background-color: #369;
}

3.1 使用vw定义字号

相对视口单位有一个不起眼的用途,就是设置字号,但我发现它比用vh和vw设置元素的宽和高还要实用。

如果给一个元素加上font-size: 2vw会发生什么?在一个1200px的桌面显示器上,计算值为24px(1200的2%)。在一个768px宽的平板上,计算值约为15px(768的2%)。这样做的好处在于元素能够在这两种大小之间平滑地过渡,这意味着不会在某个断点突然改变。当视口大小改变时,元素会逐渐过渡。

不幸的是,24px在大屏上来说太大了。更糟糕的是,在iPhone 6上会缩小到只有7.5px。如果能够保留这种缩放的能力,但是让极端情况缓和一些就更棒了。CSS的calc()函数可以提供帮助。

3.2 使用calc()定义字号

calc()函数内可以对两个及其以上的值进行基本运算。当要结合不同单位的值时,calc()特别实用。它支持的运算包括:加(+)、减(-)、乘(×)、除(÷)。加号和减号两边必须有空白,因此我建议大家养成在每个操作符前后都加上一个空格的习惯,比如calc(1em + 10px)。

代码清单2-19用calc()结合了em和vw两种单位。删除之前样式表的基础字号(以及相关的媒体查询),换成如下代码。

:root {
  font-size: calc(0.5em + 1vw);
}

现在打开网页,慢慢缩放浏览器,字体会平滑地缩放。0.5em保证了最小字号,1vw则确保了字体会随着视口缩放。这段代码保证基础字号从iPhone 6里的11.75px一直过渡到1200px的浏览器窗口里的20px。可以按照自己的喜好调整这个值。

我们不用媒体查询就实现了大部分的响应式策略。省掉三四个硬编码的断点,网页上的内容也能根据视口流畅地缩放。

4 无单位的数值和行高

有些属性允许无单位的值(即一个不指定单位的数)。支持这种值的属性包括line-height、z-index、font-weight(700等于bold,400等于normal,等等)。任何长度单位(如px、em、rem)都可以用无单位的值0,因为这些情况下单位不影响计算值,即0px、0%、0em均相等。

警告 一个无单位的0只能用于长度值和百分比,比如内边距、边框和宽度等,而不能用于角度值,比如度,或者时间相关的值,比如秒。

line-height属性比较特殊,它的值既可以有单位也可以无单位。通常我们应该使用无单位的数值,因为它们继承的方式不一样。我们在网页中加上一些文字,看看无单位的行高会如何影响样式。将代码清单2-20添加到网页中。
代码清单2-20 继承line-height的标记

<body>
  <p class="about-us">
    We have built partnerships with small farms around the world to
    hand-select beans at the peak of season. We then carefully roast in
    small batches to maximize their potential.
  </p>
</body>

接下来给body元素指定一个行高,允许它被网页上其他元素继承。不管在网页设置了什么字号,这种方式都会按照预期显示(如图2-11所示)。
在这里插入图片描述
为每个后代元素重新计算无单位的行高
将代码清单2-21添加到样式表中。这个段落继承了行高1.2。因为段落字号是32px(2em × 16px,浏览器默认字号 ),所以此时行高的计算值为38.4px(32px×1.2)。每行文字之间都会有一个合理的间距。
代码清单2-21 用无单位的数值定义的行高

body {
  line-height: 1.2;   ←---- 后代元素继承了无单位的值
}                        

.about-us {
  font-size: 2em;
}

如果用有单位的值定义行高,可能会产生意想不到的结果,如图2-12所示。每行文字会重叠。对应的CSS如代码清单2-22所示。

在这里插入图片描述
图2-12 继承line-height,导致行重叠

代码清单2-22 用有单位的值定义行高,产生了意想不到的结果

body {            (以下3行)后代元素继承了计算值(19.2px)
  line-height: 1.2em;  
}                          

.about-us {
  font-size: 2em;   ←---- 计算值为32px
}

这些结果源于继承的一个怪异特性:当一个元素的值定义为长度(px、em、rem,等等)时,子元素会继承它的计算值。当使用em等单位定义行高时,它们的值是计算值,传递到了任何继承子元素上。如果子元素有不同的字号,并且继承了line-height属性,就会造成意想不到的结果,比如文字重叠。

长度——一种用于测量距离的CSS值的正式称谓。它由一个数值和一个单位组成,比如5px。长度有两种类型:绝对长度和相对长度。百分比类似于长度,但是严格来讲,它不是长度。

使用无单位的数值时,继承的是声明值,即在每个继承子元素上会重新算它的计算值。这样得到的结果几乎总是我们想要的。我们可以用一个无单位的数值给body设置行高,之后就不用修改了,除非有些地方想要不一样的行高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值