CSS能够在众多的布局标准中脱颖而出的制胜手段就是其强大的文本处理能力,比如最"简单"的盒子边缘文字即将超出就自动换行的能力在CSS流的概念里几乎是天生的,并逐渐成为了行业内的“常规认知”,然而同时代的SVG标准要想让文字换行,还需要你手动处理一下,对于计算机来说,没有什么是与生俱来的,CSS在图文布局方面所定制的许多标准在现在看来其实是非常“人性化”的,本章我们就来深入探索一下CSS的文本处理机制。
个人将本章内容分成上下两个章节,本节主要介绍文本(font)属性相关的知识,下一章则主要介绍一些处理文本的CSS属性。闲话不多说,让我们来看看font家族有哪些成员,以及这些成员有哪些特性吧。
1.font-size的亲戚line-height&远方亲戚vertical-align
说起font属性,在平时的布局中,最常用的就是font-size属性,说起font-size,我们通常用一个具体的数值去定义字体的大小,然而除了定义字体的大小外,font-size其实还有一大堆亲朋好友,跟font-size有着或远或近的联系,由于跟font-size有染的CSS属性太多,这里只详细介绍部分属性,有些远房亲戚或是平时不怎么用的属性就一笔带过了。
提到文本,就不得不提内联元素,提到内联元素,就不得不提line-height和vertical-align。line-height的部分类别属性是相对于font-size计算的,而vertical-align的百分比属性值又是相对于line-height计算的,因此我们可以利用这个特性实现一些“自适应布局”。例如,下面的CSS代码组合
<div>文字<img src="./删除.png"></div>
<style>
div {font-size: 20px;line-height:1.5;
}
div > img {width: 16px; height: 16px;vertical-align: 25%;position: relative;top: 8px;
}
</style>
本例中,line-height:数值的计算值是font-size*数值 = 20px*1.5 = 30px
vertical-align:百分比的计算值是line-height*百分比 = 30px * 25% = 7.5px
在内联元素章节,我们了解了文字的基线时字符x的下边缘,而图片一般以自己的下边缘作为基线,因此图片下边缘默认和中文的两个汉字字形底边缘往上一点的位置对齐,然后我们通过vertical-align:25%(注意这是一个估计值)声明让图片的下边缘和中文汉字的中心线对齐,如下图所示。红线表示中心线
当图片下边缘对齐文字中心线的时候,我们就可以通过transform偏移图片本身,来使得图片的中心线和文字的中心线对齐了,这里我使用了relative相对定位,实现的效果是一样的。
最终我们实现了文字和图标的动态对齐效果(感兴趣的可以尝试修改文字font-size的值进行测试)。
2.font-size的近亲ex、em和rem
为什么要说ex,em和rem是font-size的近亲呢,因为这些单位都是font-size的相对单位,是根据font-size的值进行计算的,并且计算得到的值可以变成一个相对单位用于布局。
ex是字符x的高度,font-size的值越大,ex的计算值也就越大,关于ex的内容,在内联元素那章已经深入探究过,这里不过多介绍,有兴趣的可以点这个传送门。
下面来看看单位em,顾名思义,em就是字母'M'的宽度?的高度?准确的说,不是。用官方的话讲:em是值一个字模的高度(这里可以参考作者的看法,脑补下活字印刷术的字模)。由于其计算值接近"M"的宽高值,因此称其为em。由于大写的"M"和中文字体都拥有方方正正的特性,因此em也可以被看作是一个中文字的宽度。例如,浏览器默认的font-size是16px,如果<div>的宽度是160px,那么这个div正好可以放下十个汉字,这里的160px = 10em = 10个汉字。
在本节的开头我标红了一句话,不知大家能否根据这句话做对下面这个例题(反正我是没做对)
<!-- 这可太秀了 -->
<span>hello</span>
<span>world</span>
<style type="text/css">
span{font-size: 2em;margin: 0 1em;
}
</style>
已知浏览器的默认字体是16px,请问span标签的font-size和margin计算值是多少?
正常思维:font-size:2em = 2* 16px = 32px,margin:1em = 16px 那不就是32px和16px嘛?
然鹅,真相并没有那么简单,来看浏览器给出的真相是什么。
浏览器给出的2em的计算值竟然跟1em的一模一样?
既然浏览器已经给出最终的结果了,那我们就来分析一波为什么1em = 2em?不知你有没有注意到,我在概括ex,em,rem的时候,不但标红了整句话,还给某个词加粗了,是哪位幸运观众获得了这份殊荣呢——font-size!为什么我要给font-size加粗呢?因为这个相对计算值非常关键。以上面那道题为例,浏览器在拿到margin:0 1em的时候会做一件什么事情呢?首先他会发现,这里有个相对单位em,既然有em,那margin就得问font-size要值了,此时font-size是2em,那font-size看到em也要找font-size要值?那特么不是死循环了吗?
放心,一个小小的CSS还不至于让浏览器进入死循环,CSS会优先计算font-size:2em = 2*浏览器默认的font-size=2*16px = 32px,然后再告诉margin:0 1em,我算好了,你拿去用吧,因此margin = 0 1em = 1*font-size = 32px。于是最终的计算值,font-size和margin都是32px。
搞明白相对的概念后,我们可以想到利用相对单位做弹性布局,然而em这个单位受到当前上下文的font-size影响,不是特别稳定,为了解决这个局限性,另外一个和font-size密切相关的单位rem就出现了。
rem顾名思义,就是root(根)元素的font-size的相对单位,他只会受根元素font-size大小的影响,因此rem被广泛应用于移动端的弹性布局方案中,虽然rem是根据em衍生出来的,但他们却有完全不同的命运,em本来是主角,如今却被摁在冷板凳上万年上不来台,rem却变成了移动端布局的香饽饽,只能说世事无常鸭~
3.font-size的“偏门”属性
font-size还支持关键字属性,这点恐怕很多人不知道(又包括我了)。font-size的关键字属性分为以下两类
(1)相对尺寸关键字。指相对于当前元素的font-size进行计算
larger:大一点。是<big>标签的默认font-size。
smaller:小一点。是<small>标签的默认font-size。
(2)绝对尺寸关键字。与当前元素的font-size无关,仅受浏览器设置的字号影响(注意不是根元素,划重点!浏览器字号怎么设置麻烦自己百度)
绝对尺寸关键字总共有7种,非常大,很大,大,中(medium),小,很小,非常小。
这两个尺寸关键字在不同浏览器的表现各不相同,尤其是相对尺寸关键词,看看就好了,基本没什么使用价值,绝对尺寸关键字,除了C位出道的medium,其他基本都没用。个人认为,了解一下这两个偏门属性就好。
4.特殊的font-size:0与文本隐藏
在PC端的Chrome浏览器下有个12px的字号限制,就是文字的font-size的计算值不能小于12px,当然之前也遇到过有个变态需求非得让我把字体变成10px的,可以尝试用transform:scale()去改变元素大小实现(当然浏览器都规定字号不小于12px了,尽量还是遵循一下)
由于浏览器有字体最小12px的限制,因此你设置font-size:<12px的值均会被当作12px来处理,然而有一个值例外,那就是0,你可以理解为0是一个文字隐藏的关键字,当然他比关键字还厉害一点的是,他真的可以让font-size以0px的值参与计算。然而font-size:0被设计出来之后,跟rem一样,并没有干自己的老本行,而是在一些特殊领域发挥着一些余热,如“如何解决图片底边空白问题”,就可以设置父元素的font-size:0,来消除幽灵空白节点的影响。
5.字体家族族谱管理员font-family
font-family,翻译成中文,就是字体家族,font-family的默认值受操作系统和浏览器的控制,我们常用的Windows和OS系统的默认字体就不一样,同一台系统的Chrome和Firefox浏览器的默认字体也不一样。
font-family除了支持字体名称的关键字外,还支持“字体种类”。常用的字体名称有
font-family:simsun(宋体)
font-family:'Microsoft Yahei'(微软雅黑) 如果字体名称包含空格,一定要用引号包起来。
当然font-family也支持对应的中文名称,但是尽量使用英文,以防字体解析失败。
下面我们来探究一下偏冷门的“字体种类”,我个人认为,font-family给字体做了族谱后,还给各种习性相近的家族做了个分类,这个分类就是“字体种类”。MDN上文档分类如下
font-family:serif(衬线字体)、sans-serif(无衬线字体)、monospace(等宽字体)、cursive(手写字体)、fantasy(奇幻字体)、system-ui(系统字体)
对于中文网站,后面三种字体的应用场景有限,就不过多展开,这里着重介绍一下衬线字体,无衬线字体和等宽字体。
衬线字体和无衬线字体是字体家族中两种比较常见的字体,所谓衬线字体,通俗讲就是笔画开始、结束的地方有额外装饰而且笔画的粗细会有所不同的字体,如“宋体”。而无衬线字体则没有这些额外的装饰,而且笔画的粗细差不多,如现在最常用的“雅黑”字体。要注意,不管是衬线还是无衬线字体,或是上述“字体种类”中的任意一款字体,都有默认的字体关键字,经过个人测试,谷歌浏览器下serif(衬线字体)的默认字体包就是宋体。sans-serif(无衬线字体)的默认字体包就是微软雅黑。当然不同的操作系统和浏览器可能有自己的偏好,因此"字体种类"可以算作是按照操作系统喜好展现哪种字体的一种方式。
下面我们再来讲讲等宽字体的实践价值,所谓等宽字体,一般是针对英文字体而言的,因为中文字体在介绍em的时候就提到了,每个中文字体近似于是等宽高的,然而英文字母的大小写却又很大的不同,如下面这个例子。
<div>iiiiiii</div>
<div>MMMMMMM</div>
其实等宽字体的好处我都不用多说了,我们直接看,我在CSDN上用CSDN编辑器打出来的代码,上下是完全对齐的,要做到这种对齐,就必须让每个字符所占据的位置保持一致,而在浏览器的默认字体下,两行文字所占据的位置就差的有点远了,等宽字体的应用非常多,所谓仁者见仁智者见智,用得到的时候自然就会想到了。
6.细腻的font-weight
font-weight表示“字重”,就是文字的粗细程度,font-weight有两个常用的关键字,normal和bold,事实上除了这两个字面意义上的关键字之外,font-weight还支持以下关键字,
分别是:100/200/300/400/500/600/700/800/900。
以及相对于父级元素的lighter和bolder。
可能有人怀疑我脑子有问题,为什么不直接写100-900呢?因为,数字100并不是数字,他是关键字,只是叫100罢了,如果你来一个100的亲戚,100.000001,不好意思,font-weight不认识,也不认可。这时候又有人要说了,font-weight:100也没有生效呀,为什么设置了font-weight:100之后字体跟200/300/400没什么区别?这锅就不能甩给浏览器了,浏览器是支持检测这些关键字的,之所以看不到粗细变化,是因为我们系统里缺乏对应粗细的文字,因此通常情况下,我们只用到关键字normal(400)和bold(700)就足够了,如果要看到这种细微的变化,需要我们的操作系统安装这些字体包,当然实际生产环境中你不可能要求用户去按照除了浏览器之外的东西,因此想要解决这个问题,就需要借助@font-face了。这个属性会在后面深入探究,这里不过多展开。
7.font家族其他属性font-style和font-variant
font-style除了支持normal和italic(斜体)外,还支持oblique关键字,事实上这个关键字并没有什么软用,这里小小的做个展开。italic表示引用该字体的斜体字体包,通常情况下,很多文字包并没有单独的斜体包,但有些英文字体包会有斜体字体包,如果招不到斜体字体包,则让字体直接倾斜。oblique关键字的作用就是直接让文字倾斜,因此我说这个关键字没什么软用,通常情况下,斜体字体包会比文字直接侵袭要好看得多,谁会去用oblique呢?
font-variant也是个不符合我国国情的CSS属性,他的作用是实现小体型大写字母,这个属性能让m和M的体型保持一致,这个属性在母语是英文的国家可能用的比较多,对于我们来说就是个鸡肋属性,了解一下即可。
8.深藏不露的font属性
font支持缩写,其语法如下:
[ [ <'font-style'> || <'font-variant'> || <'font-weight'> ]? <'font-size'> [ / <'line-height'> ]? <'font-family'> ]
这里我想跳过font缩写的相关介绍,因为这属于CSS的糟粕部分,原文如下:
对于大部分的可缩写属性,缩写并不是个问题。您可以声明你想要的,任何选项都可以缺失,如果没有则会应用初始值。例如list-style
和background
没有应用的值99%的时间无论如何都不会被继承的,所以值的设置与否无伤大雅。
但是,很多排版属性都预期从父辈继承。因此,当你使用font
缩写,事情会变得混乱。如果你对该属性的复杂性不熟悉,估计你要抓破脑袋了。
换句话说,如果我在<body>
元素上声明文字粗体,我可能本希望里面的文字都继承粗体。结果,一旦应用了缺少font-weight缩写属性的font缩写,文字不是粗体显示的了。
原文到此结束,本人想吐槽font缩写的两个问题,第一,font-family不能省略,font-family那么又臭又长的属性连个默认值都没有,每次缩写都得声明,这也太浪费我时间了。第二,line-height可以省略,但会被继承,也就是你又是漏写了个line-height,这个line-height就重置为normal了,就继承给后代了,这设定也忒.....
综上所述,不建议使用font缩写。
font属性除了缩写用法,还支持关键字属性值,这个恐怕很多人不知道(又包括我了),关键字列表如下
caption:包含说明文字控件的字体(如按钮,下拉等)。
icon:标签图标使用的字体,影响所有文件以及文件夹名称字体。
menu:菜单使用的字体(如下拉菜单和菜单列表)。
message-box:消息框使用的字体。
small-caption:标记小控件使用的字体。
status-bar:窗体状态栏使用的字体。
值得注意的是,声明了font:关键字后,就无需定义font-size,font-family等属性了,因为这些关键字本质上也是一种缩写,已经包含font的各种属性了。
font关键字在实际使用场景中就是可以让网页跟着系统走,要知道现在已经有很多桌面软件可以修改系统的默认字体了,如果让浏览器能根据用户的“心情”显示对应的字体的话,看起来是不是很智能呢?这里我提供两种方式,让字体随着系统的默认字体改变。第一种,之前提到的font-family字体种类中还有一个被忽略的关键字system-ui,使用示例如下
<style>
html{font-family:system-ui}
</style>
第二种
<style>
html{font:menu}
</style>
9.深入探究@font face规则
通常我们使用@font face是为了引入字体库,或字体图标库,用法一般如下
<p>我要用特殊字体</p>
<style>
@font-face{font-family:'example'; //随便取个名字src:url(example.ttf); //引入字体包
}
p{font-family:'example';
}
</style>
事实上,@font-face变量除了必要的font-family和src之外,还支持以下属性。
@font-face {font-family: 'example';src: url(example.ttf);font-style: normal;font-weight: normal;unicode-range: U+0025-00FF;font-variant: small-caps;font-stretch: expanded;font-feature-settings:"liga1" on;
}
支持的有很多,但是部分属性跟英文字母的关系较深,例如font-variant
,font-stretch
,font-feature-settings
这3
个属性,不深入了解,unicode-range的用处较小也不深入了解,因此我们只需要了解以下属性即可。
@font-face {font-family: 'example';src: url(example.ttf);font-style: normal;font-weight: normal;
}
下面我将详细介绍这五个属性。
(1)font-family
font-family是一个字体变量,名称可以根据自己喜好来命名,但注意尽量不要占用系统的字体关键字,如"Microsoft Yahei"。如果使用系统的关键字命名,会覆盖系统的字体,可能对代码维护造成一定的困扰,需要注意一下。
(2)src
src表示引入的字体资源。既然是资源,肯定包括内部资源和外部资源。src属性给内部资源和外部资源的引入方式做了区分,如果需要引入内部资源,需要用local()功能符,如果是外链字体,则使用url()功能符。
如果你看到local功能符,一般是为了偷懒,如我想把'Microsoft Yahei'简写成'YH',就可以给内部资源重命名一下,如下
@font-face{font-family: YH;src: local("Microsoft Yahei");
}
引入外部资源的时候,还有一个搭配的功能附,format(),这个format是用于系统解析字体包之前判断字体包的格式的,常用的字体包格式有eot,ttf,woff2,woff等,因此在系统解析字体包之前声明一下字体包的格式能缩短一些解析时间,可能只是极短的时间,所谓积少成多,多写几个字母也不会占用你多久。在引入外部资源的时候,建议的写法如下
@font-face{font-family: 'example';src: url("example.ttf") format('ttf');
}
(3)font-style和font-weight
刚才我在讲font-weight和font-style的时候都提到了系统字体的概念,即在什么样式下该用什么样的字体,如有些英文字体在声明斜体后就会有专门的倾斜字体,而不是字体倾斜一个角度。因此@font-face可以在你使用该自定义字体的时候,在声明了对应的font-style/font-weight的时候,使用该字体,听起来有点绕口,来一个具体实例
如,你声明了几个@font-face如下
@font-face{font-family: 'example';src: url("example100.ttf") format('ttf');font-weight:100;
}
@font-face{font-family: 'example';src: url("example200.ttf") format('ttf');font-weight:200;
}
@font-face{font-family: 'example';src: url("example300.ttf") format('ttf');font-weight:300;
}
这几个font-face的名字都是example,但并不会产生覆盖,因为这些自定义字体需要在特定的条件下才会生效,这个特定条件在你定义这个字体的时候就已经规定好了,即font-weight。因此如果你用下面的写法,这个自定义字体就不会生效
<p>我想用自定义字体</p>
<style>
p{font-family:'example';
}
</style>
未生效的原因是没找到对应的font-face变量,你必须声明一个有效的font-weight才可以,如下写法:
<p>我想用自定义字体example200</p>
<style>
p{font-family:'example';font-weight:200
}
</style>
font-style和font-weight同理,有兴趣的可以自己实验。
CSS的font部分的探究就到此为止了,下半章会围绕文本处理属性展开,感兴趣的点个关注吧~