深入了解CSS字体度量,行高和vertical-align

本文英文出处:http: //iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align 著作权归作者所有。
转载自https: //www.w3cplus.com/css/css-font-metrics-line-height-and-vertical-align.html

line-heightvertical-align在CSS中是两个简单的属性。如此简单,大多数人都相信自己已经完全理解它们是如何工作的以及如何使用它们。但事实上并不如此。他们其实很复杂,也是CSS中难点之一,而且也是CSS中特性之一:内联格式化上下文(inline formatting context)

比如可以设置line-height带有长度单位的值或一个无单位的值,但其默认值是normal。那么在CSS中normal是什么呢?我们常常认为它是(或者应该是)1或者1.2,甚至也可以说,CSS规范都不清楚是哪一个。我们也知道,没有单位的line-height是相对于font-size的,但问题是,font-size: 100px;在使用不同的字体(font-family)表现的行为是不一样的,所以line-height总是相同或不同的吗?真的是1还是1.2吗?另外vertical-alignline-height的影响又是什么呢?

要深入研究CSS的机制可以说没有这么简单……


首先来聊font-size

首先来看一个简单的HTML代码,一个<p>标签中包含了三个<span>标签,每个<span>都使用不同的font-family

<p><span class="a">Ba</span><span class="b">Ba</span><span class="c">Ba</span>
</p>p  {font-size: 100px;
}
.a {font-family: Helvetica;
}
.b {font-family: Gruppo;
}
.c {font-family: Catamaran;
}

每个元素使用相同的font-size,但使用不同的font-family,但渲染出来的line-height是不同的:

即使我们意识到这种行为,但还是不清楚为什么font-size:100px时元素的height不是100px?我测量发现:Helvetica字体的高度是115px,Gruppo字体的高度是97px和Catamaran字体的高度是164px

起初似乎有点奇怪,但它是完全可预期的。这主要还是font-family的原因。那就要搞清楚它是如何工作的:

  • 字体定义其em-square,每个字符将会绘制出自己的容器。这个正方形使用相对单位和生成一个1000单位。但它也可以是1024,2048或者其他
  • 根据其荐对单位,字体的度量可以根据一些设置(ascender,descender,capital height,x-height等)来决定。注意,有些值是em-square之外的值
  • 在浏览器中,相对单位是用于缩放用来适应所需的font-size

让我们来看Catamaran字体,并且在FontForge中来看这个字体的度量参数:

  • em-square是1000
  • 上升(ascender)是1100和下降(descender)是540。相同的测试下,浏览器使用HHead Ascent/Descent值(Mac)和Win Ascent/Descent值(Windows),这些值可能不同。我们还需要注意,Capital高度是640和x-height的值是485

这意味着Catamaran字体在1000个单位的em-square使用了1100 + 540个单位,也就是说font-size:100px的时候,其高度是164px。这个计算高度定义了元素内容高度(在这篇文章中其它部分引用这个术语content-area)。你可能想到是内容区域相当于background属性。

我们也可以预测,大写字母是68px高度(680个单位)和小写字母(x-hegiht)是49px高度(485个单位)。因此,1ex = 49px1em = 100px,而不是164px(值得庆幸的是,em是基于font-size计算,而不是height)。

在继续深入之前,先要了解这涉及到什么?当<p>元素呈现在屏幕上,它根据它的宽度可以有很多线。每一行是由一个或多个行内元素(HTML标签元素或匿名内联元素文本内容)组成,专业术语称为行盒(line-box)。line-box的高度是基于它的子元素高度的。浏览器为每个行内元素计算的高度都是line-box(子元素的最高点到最低点)。因此line-box的总高度足以包含所有子元素(默认情况下)

每个HTML元素实际上是一个line-box的堆栈。如果你知道每个line-box的高度,实际上你就知道每个元素的高度。

如果我们把前面的HTML结构更新成:

<p>Good design will be better.<span class="a">Ba</span><span class="b">Ba</span><span class="c">Ba</span>We get to make a consequence.
</p>

它会生成三个line-box:

  • 第一个和最后一个每个包含一个匿名内联元素(文本内容)
  • 第二个包含了两个匿名内联元素和三个<span>

<p>元素(黑色边框)产生了一个line-box(白色边框),其包含了内联元素(实心边框)和匿名内联元素(虚线边框)。

我们清楚的看到,第二个line-box明显比其他的line-box要更高,根据子元素的内容区域(content-area)计算得来,更具体地说,是使用了Catamaran字体。

困难的是line-box创建部分是我们无法看到的,也不是用CSS控制它。即使在::first-line应用了background也无法直接在视觉上看到第个line-box的高度。


line-height问题

直到现在,我们介绍了两个概念:content-arealine-box。如果仔细阅读了前面的内容,你应该知道line-box的高度是根据子元素高度来计算,而且我并没有说是子元素的内容区域(content-area)的高度。这是有很大区别的。

尽管这听起来可能有些奇怪,内联元素有两个不同高度:内容区域(content-area)高度和虚拟区域(virtual-area)高度(这是我发明的术语virtual-area高度,你在规范中是找不到任何相关的内容)。

  • 内容区域高度是由字体来决定的(前面介绍过)
  • 虚拟区域(virtual-area)高度是line-height,它的高度用于计算line-box的高度

行内元素有两个不同的高度。

也就是说,line-height普遍的看法是不同基线(baseline)的距离。在CSS中,它并不是这样。

计算虚拟区域(virtual-area)和内容区域(content-area)高度差称为leading。leading添加在内容区域顶部,另一半添加在内容区域底部。因此,内容区域总是在虚拟区域的中间。

根据其计算值,line-height(virtual-area)相同情况下比content-area更高或更低。对于较小的virtual-area,leading是负值和line-box要比它的子元素更小。

还有其他的内联元素:

  • 替代内联行内元素(<img><input><svg>等)
  • inline-block元素
  • 行内元素参与特定格式化上下文(如,Flexbox元素,和所有的Flex项目)

对于这些特定的行内元素,高度计算基于他们的heightmarginborder属性。如果hegiht的值是auto,然后使用line-heightcontent-area严格上等于line-height

无论如何,我们仍然面临的问题是line-heightnormal值是多小?答案是,其计算content-area高度还是依据于里面的字体来度量。

我们回到FontForge。Catamaran的em-square是1000,但我们看到ascender/descender的值:

  • 生成的Ascent/Descent: ascender是770,descender是230。用于绘制字符(OS/2)
  • 度量的Ascent/Descent: ascender是1100,descender是540。用于内容区域高度(hhea和OS/2)
  • 度量线的间距:通过Ascent/Descent度量使用line-height: normal(hhea)

在我们的示例中,Catamaran字体定义了0个单位的线间距(Line Gap),因此line-height: normal的值将等于内容区域,也就是1640个单位或1.64

作为比较,Arial字体的一个em-square是2048个单位,其ascender是1854,descender是434,线间距是67。这意味着,font-size: 100px的内容区域是112px(1117个单位)和line-height115px1150个单位或1.15)。所有这些度量都是特殊字型,由字体设计师来设置。

显而易见,设置line-height:1是一个非常糟糕的做法。我提醒你,font-size没有单位的观念是相对的,但内容区域不是相对的以及处理虚拟区域小于内容区域有很多问题存在。

但并是只有line-height:1。不论真假,我电脑上安装了1117种字体(是的,我安装了所有的Google Web字体),其中1059种字体,占全部字体的95%左右,计算的line-height大于1。它们计算line-height是从0.6183.378。你得记住,是3.378

line-box计算的小细节:

  • 对于内联元素,paddingborder增加了其background区域,但不会增加内容区域高度(甚至是line-box高度)。因此,你在屏幕上看到的不一定就是内容区域。margin-topmargin-bottom对内联元素不生效。
  • 对于行内替代元素,inline-blockblocksified行内元素,paddingmarginborder都会增加高度,所以内容区域和line-box的高度也会增加

vertical-align:一个属性控制一切

前面我没有提到vertical-align属性,即使它是计算line-box高度的一个重要因素。我们甚至可以说,vertical-align属性对于行内格式化上下文中的leading有很大的作用。

vertical-align的默认值是baseline。你注意到度量字体的ascender和descender?这些值是基于baseline,具有一定的比例。那么ascender和descender之间的比例真的是50/50,它可能会产生意想不到的结果,例如所有兄弟元素。

先从这个代码开始:

<p><span>Ba</span><span>Ba</span>
</p>p {font-family: Catamaran;font-size: 100px;line-height: 200px;
}

两个<span>元素继承了<p>元素的font-familyfont-size和固定的line-height。基线将会匹配以入line-box的高度等于他们的line-height

如果第二个元素设置更小的font-size呢?

span:last-child {font-size: 50px;
}

这听起来很奇怪,但默认基线对齐可能导致更高的line-box,如下图所示。我提醒你,line-box的高度是从它的子元素最高点和最低点计算。

有一个观点可以得到支持,那就是line-height设置不带任何单位的值,但有时你需要做一个完美的Vertical-rhythm。说实话,不管你选择什么,你总是会有困难的。

看看另一个例子。<p>元素的line-height值设置了200px,并且包含了一个<span>元素,这个<span>元素继承了<p>元素的line-height

<p><span>Ba</span>
</p>p {line-height: 200px;
}
span {font-family: Catamaran;font-size: 100px;
}

line-box有多高?我们期望的是200px,但如果不是,我们得到的又是什么?这里不同的是<p>元素有自己的字体(默认是serif)。<p><span>之间的基线可能是不同的,因此line-box的高度是高于预期的。这是因为浏览器给每个line-box计算都是开始于一个任意字符。规范中称之为strut。

一个看不见的角色,但的确是会有可见的影响。

就我自己一些经历,我们将面临同样的问题,那就是兄弟元素。

基线对齐是完了,但vertical-align:middle可以拯救它们?可以阅读规范:

Middle “aligns the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent”.

基线的比例不同,以及x-height比例,所以中间对齐是不可靠。最坏的情况下,在大多数的情况下,中间就从来没有真的中间过。这里面有太多的因素参与其中,使用CSS是不能设置这些因素的(x-height,ascender和descender比例等)。

它有四个值,这可能在某些情况下是有用的:

  • vertical-align: top | bottom和line-box的顶部或底部对齐
  • vertical-align: text-top | text-bottom和内容区域的顶部或底部对齐

注意了,在所有情况下它都是在虚拟区域中,所以是看不见的高度。看看这个简单的示例,使用vertical-align:top,看不见的line-height可能产生一些很奇怪的结果。

最后,vertical-align还能接受数值,提高或降低盒子的基线。最后一个选项可以派上用场。


CSS 无所不能

我们已经讨论过了line-heightvertical-align在一起是如何工作,但现在的问题是如何使用CSS来控制字度的度量指标?简短的回答:没有。即使真的如此,我也想我们应该可以做些什么?那么有关于字体度量,我们应该能够做些什么?

例如,如果我们想要给文本使用Catamaran字体,可以把其capital高度扩展到100px?通过一些数学计算,似乎可行。

首先设置度量字体的五个自定义属性,然后计算font-size,从而得到capital高度是100

p {/* font metrics */--font: Catamaran;--capitalHeight: 0.68;--descender: 0.54;--ascender: 1.1;--linegap: 0;/* desired font-size for capital height */--fontSize: 100;/* apply font-family */font-family: var(--font);/* compute font-size to get capital height equal desired font-size */--computedFontSize: (var(--fontSize) / var(--capitalHeight));font-size: calc(var(--computedFontSize) * 1px);
}

很简单,不是吗?但如果我们想要让文本在可视区居中,让剩余的空间均分在”B”字的顶部和底部,应该怎么做呢?为了达到这一目的,我们必须基于ascender和descender比例计算出vertical-align

首先,计算line-height:normal和内容区域的高度。

p {--lineheightNormal: (var(--ascender) + var(--descender) + var(--linegap));--contentArea: (var(--lineheightNormal) * var(--computedFontSize));
}

这时,我们需要:

  • 大写字每底部距离底部边缘的距离
  • 大写字母顶部距离顶部边缘的距离
    像这样:
p {--distanceBottom: (var(--descender));--distanceTop: (var(--ascender) - var(--capitalHeight));
}

我们现在可以通过距离乘以font-size计算出vertical-align

p {--valign: ((var(--distanceBottom) - var(--distanceTop)) * var(--computedFontSize));
}
span {vertical-align: calc(var(--valign) * -1px);
}

最后,我们设定所需的line-height和计算它,保持一个垂直对齐:

p {…/* desired line-height */--lineheight: 3;line-height: calc(((var(--lineheight) * var(--fontSize)) - var(--valign)) * 1px);
}

添加一个图标和字母”B”垂直对齐,现在很容易就能做到:

span::before {content: '';display: inline-block;width: calc(1px * var(--fontSize));height: calc(1px * var(--fontSize));margin-right: 10px;background: url('https://cdn.pbrd.co/images/yBAKn5bbv.png');background-size: cover;
}

示例的地址可以点击这里。

注意:这个测试只是出于演示目的。你不能依赖于此。如果字体不加载,备用字全有可能具有不同的字体度量参数,它就没法正常工作了。

在一部分示例中,大家看到很有以–开头的,这是CSS的原始变量,也称之为CSS自定义属性。


总结

这篇文章我们学到了什么:

  • 行内格式化上下文真的很难理解
  • 所有行内元素都有两个高度
  • 内容区域(content-area)基于字体的度量参数
  • 虚拟区域(virtual-area)就是line-height
  • 这两个高度是无法可视的(如果你通过开发者工具,你可以看到)
  • line-height:normal是基于字体度量参数
  • line-height: n有可能创建一个虚拟区域比内容区域更小
  • vertical-align不是很可靠
  • 一个line-box的高度计算是基于它的子元素的line-heightvertical-align属性
  • 我们没有办法直接通过CSS来获取或设置字体的度量参数
  • 未来可能会有一个垂直对齐的规范来解决这些看似问题的问题:Line Grid Module

相关资源

  • 获取字体度量参数的工具:FontForge,opentype.js
  • 在浏览器中计算line-height:normal和一些比例
  • Ahem,一个特殊字体,帮助我们如何理解它怎么工作
  • 一个更深,更透彻阐述行内格式化上下文
  • 两份详细介绍line-height的PPT:CSS line-height和Deep dive line-height

博客名称:王乐平博客

CSDN博客地址:http://blog.csdn.net/lecepin

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401676.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML5 Canvas制作雷达图实战

雷达图又叫蜘蛛网图&#xff0c;是一种对各项数据查看很明显的表现图&#xff0c;在很多游戏中&#xff0c;对游戏中的每个角色的分析图一般也用这种图。 下面&#xff0c;用HTML5的Cavas来实现雷达图。 效果 一、创建Canvas var mW 400; var mH 400; var mCtx null;var c…

AlphaBlend

AlphaBlend实现透明效果&#xff0c;只是仅仅能针对某块区域进行alpha操作&#xff0c;透明度可设。 TransparentBlt能够针对某种颜色进行透明&#xff0c;只是透明度不可设。 AlphaBlend&#xff1a; BLENDFUNCTION bn; bn.AlphaFormat 0; bn.BlendFlags 0; bn.BlendOp AC_…

ECMAScript 6网页样式修正器

最近在看ES6这一方面的图书&#xff0c;在搜索的过程中发现了《ECMAScript 6 入门-阮一峰》&#xff0c;感觉还不错。因为我个从比较喜欢看纸质的书&#xff0c;就想把这本书给打印下来。 但是网页版的《ECMAScript 6 入门-阮一峰》设置的样式只适合在网页上查看&#xff0c;并…

PWA(Progressive Web App)入门系列:(一)PWA简介

前言 PWA做为一门Google推出的WEB端的新技术&#xff0c;好处不言而喻&#xff0c;但目前对于相关方面的知识不是很丰富&#xff0c;这里我推出一下这方面的入门教程系列&#xff0c;提供PWA方面学习。 什么是PWA PWA全称Progressive Web App&#xff0c;直译是渐进式WEB应用…

Vue DevTools可使用修正方法

因为工作要求&#xff0c;目前主要在用Vue.js技术栈做开发&#xff0c;调试是必不可少的&#xff0c;这里会用的Vue DevTools的调试工具&#xff0c;问题就出在这里&#xff0c;当用Vue DevTools做调试时&#xff0c;很多时候都不能用&#xff0c;提示没有监测到Vue&#xff0c…

ZRender实现粒子网格动画实战

注&#xff1a;本博文代码基于ZRender 3.4.3版本开发&#xff0c;对应版本库地址&#xff1a;ZRender 库。 效果 实现分析 通过上面显示的效果图&#xff0c;可以看出&#xff0c;这种效果就是在Canvas中生成多个可移动的点&#xff0c;然后根据点之间的距离来确定是否连线&am…

CSS动画实战:创建一个太极Loading图

这里主要是使用CSS的animation和伪类来构建&#xff0c;分析设定关键帧的执行顺序和时间段。 效果 动画分析 首先通过效果对动画执行进行一下分析&#xff1a; 边框的四条边进行按顺序动画加载 。矩形边框变为圆行边框。太极图内部图案渐渐出现。太极图旋转。整个动画逆序执…

PWA(Progressive Web App)入门系列:(二)相关准备

前言 在上一章中&#xff0c;对PWA的相关概念做了基本介绍&#xff0c;了解了PWA的组成及优势。为了能够更快的进入PWA的世界&#xff0c;这一章主要对在PWA开发中&#xff0c;需要注意的问题&#xff0c;运行的环境及调试工具做介绍说明。 浏览器要求 因为目前各浏览器对于…

PWA(Progressive Web App)入门系列:(三)PWA关键技术Manifest

前言 前面说过&#xff0c;让Web App能够达到Native App外观体验的主要实现技术就是PWA中的manifest技术&#xff0c;本章会详细说明manifest的实现&#xff0c;及各个参数的具体含义&#xff0c;还将了解如何定义Web App的启动图标、启动样式等。 简介 manifest是一种简单的…

利用百度LBS做一个小Demo

为什么80%的码农都做不了架构师&#xff1f;>>> 申请ak&#xff08;即获取密钥&#xff09;http://lbsyun.baidu.com/apiconsole/key?applicationkey 去这儿注册一个开发者账号即可拼写发送http请求的url譬如这样的调用http://api.map.baidu.com/geocoder/v2/?ad…

PWA(Progressive Web App)入门系列:(四)Promise

前言 这一章说一下ES6的Promise对象。为什么要在PWA系列的文章中讲Promise呢&#xff1f;因为PWA中的许多技术API中都是以Promise返回的方式返回的&#xff0c;为了对后续章节中PWA技术API更好的理解&#xff0c;这里就来说一个Promise对象。 Promise出现的背景 在JavaScrip…

图文详解如何搭建Windows的Android C++开发环境

原地址:http://www.apkbus.com/android-18595-1-1.html ////TITLE:// 图文详解如何搭建Windows的Android C开发环境&#xff08;一&#xff09;//AUTHOR:// norains//DATE:// Thursday 14-April-2011//Environment:// Cygwin 1.7.9// Android NDK r5//1. 下载A…

PWA(Progressive Web App)入门系列:(五)Web Worker

前言 在说Service Worker前有必要说一下Web Worker&#xff0c;因为Service Worker本身就属于Web Worker的延伸&#xff0c;大部分功能也是基于Web Worker进行的扩展。 背景 众所周知&#xff0c;JavaScript引擎是以单线程调度的方式进行&#xff0c;我们无法同时运行多个Ja…

Glob Patterns匹配模式使用

前段时间在用workbox时&#xff0c;在做precache时&#xff0c;匹配模式基于的是Glob Pattern模式&#xff0c;于是就看了下相关文档。 下面翻译一下node-glob的使用&#xff0c;原文&#xff1a;https://github.com/isaacs/node-glob#glob-primer Glob 像在shell里面&#x…

Workbox CLI v3.x 中文版

在写PWA应用时&#xff0c;用到WorkBox工具&#xff0c;使用过程中发现没有中文的帮助文档&#xff0c;为了体验好一些&#xff0c;也为了方便自己和他人查看&#xff0c;在这里翻译了一下workbox-cli。 Workbox CLI 是什么? Workbox命令行&#xff08;在workbox-cli包内&…

Workbox.routing v3.x 中文版

NAMESPACE STATIC VERSION V3.6.1 类 NavigationRoute NavigationRoute可以轻松创建匹配浏览器navigation requests的Route。 它仅匹配mode设置为navigate的请求。 您可以只使用blacklist和whitelist参数中的一个或两个&#xff0c;将此路由应用于导航请求中。 RegExpRout…

Workbox.strategies v3.x 中文版

NAMESPACE STATIC VERSION V3.6.1 该模块提供了大多数serviceworker常用的缓存策略的简单实现。 类 CacheFirst cache-first请求策略的实现。 缓存优先策略对于带版本号的资源是非常有用的&#xff0c;像这种URLstyles/example.a8f5f1.css&#xff0c;因为它们可以长时间缓存…

集算器访问HTTP数据的代码示例

使用集算器&#xff08;esProc&#xff09;可以很方便的从http数据源读取数据进行处理。本例子中有一个servlet&#xff0c;对外提供json格式的雇员信息查询。Servlet访问数据库的员工表&#xff0c;保存了员工的信息&#xff0c;如下&#xff1a;EID NAME SURNAME …

PWA(Progressive Web App)入门系列:Cache Storage Cache

前言 目前浏览器的存储机制有很多&#xff0c;如&#xff1a;indexedDB、localStorage、sessionStorage、File System API、applicationCache 等等&#xff0c;那为什么又制定了一套 Cache API 呢&#xff1f;对比其他存储机制有什么优势&#xff1f; 简介 Cache API 是一套…

「浏览器插件」无广告国内视频平台直接播放插件

前段时间发现一些比较不错的解析国内视频平台的一些 API 接口&#xff0c;很早之前基于这些接口做过一个 Android 端的播放软件&#xff0c;但为了更方便使用吧&#xff0c;于是做了一个 Chrome 的浏览器插件&#xff0c;解析接口也是在线更新的&#xff0c;所以用起来会比较方…