美文网首页
line-height vertical-align

line-height vertical-align

作者: HIOOOOIH | 来源:发表于2017-02-26 22:19 被阅读0次

    原文出处:http://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align

    • 无授权,基本上是翻译原文,加上自己的理解和注释,当做一个笔记。
      作者说:这两个属性也许没有看上去那么简单,甚至是最难的之一。原因在于:内联格式化上下文(inline formating context)。
      因为我们对这两个属性知道的太少了,比如line-height:normal,设置行高为正常,但是什么是正常呢?也许是1,也许是1.2,在字体这么多设置中,它们的行为总是一样吗?而且它们有什么影响吗?

    之后举了一个font-size都是一样的三个一样标签的内联元素,字体不同,发现不一样高,而且有一个高出了64px。
    font是根源所在:
    1.一个字体(font)定义它的em-square(UPM ,unit per em),类似于容器,每个字符都会被绘制。这个方形的容器使用相对单位,一般设置为1000,但是它也可以是1024或者任何其他。
    2.根据相对单位,可以设置上升下降(ascender,descender,capital height,x-height等等)。一些值也有可能泄露在容器外面。
    3.在浏览器中,相对单位被缩放以适合期望的字体大小。

    这里作者用了一个软件检测字体的详细指标。(https://fontforge.github.io/en-US/
    上升+下降的值=字体em-square大小。
    Mac OS上相对应的是HHead Ascent / Descent值,在Windows上使用Win Ascent / Descent值,值也许会有不同。
    比如一个ascent(上升)是1100,descent(下降)是540,em-square是1640,设置时的高度就是164px,这个计算的高度定义了一个元素的内容区域。内容区域视为属性应用的区域。

    • 1ex是小写字母,也就是对应的x-height,所以它是一个会变的数值,而不是像em基于font-size,而不是计算高度。
    在原图基础上加了属性方便查看

    可以看到,它可以根据其宽度由许多线组成。每行由一个或多个内联元素(HTML标签或文本内容的匿名内联元素)组成,称为行框(line-box),一个line-box是基于其子女的高度。
    因此,浏览器计算每个内嵌元素的高度,并且因此定下line-box的高度(从其子的最高点到其子的最低点)。因此,行框总是足够高以包含其所有子项(默认情况下)。
    其实知道每个line-box的高度,就知道一个元素的高度。

    但是实际是看不到line-box的,也不能用CSS控制它,设置个背景也没有多大用处。

    <b>line-height:to the problems and beyond</b>

    一个line-box的高度由孩子的height计算,没有说它孩子content-area的高度。这产生了很大的区别。

    即使它可能听起来很奇怪,一个内联元素有两个不同的高度:内容区域高度(content-area)和虚拟区域(virtual-area )高度(作者发明了术语虚拟区域O__O "…)。
    1.在内容区高度(content-area)由字体规格(font metrics)定义(如前所示)
    2.在虚拟区域的高度是line-height,它的高度用于计算line-box的高度(it is the height used to compute the line-box’s height)

    计算出的虚拟区域和内容区域之间的高度差称为leading。一半leading被添加在内容区域的顶部,另一半被添加在底部。因此,内容区域总是在虚拟区域的中间。
    基于其计算值,line-height(虚拟区域)可以等于,高于或小于内容区域。在较小的虚拟区域的情况下,leading是负的,并且line-box在视觉上小于其孩子。
    还有其他种类的内联元素:

    取代内联元素(<图片>,<input>,<svg>等)
    inline-block和所有inline-*元素
    参与特定格式化上下文的内联元素(例如,在flexbox元素中,所有flex项目都被块化)
    对于这些特定的内联元素,高度的计算基于他们的height,margin和border属性。如果height是auto,则line-height使用并且内容区域严格等于line-height。

    上图!同样是原作者的图 ![Uploading line-height-yes-no_400235.png . . .]

    之后是讨论了两种字体的line-height:1,发现内容区域不一样大,
    <b>很明显,设置line-height: 1是一个坏的做法。</b>
    无单位的值是font-size相对的,而不是内容区域相对的,并且处理小于内容区域的虚拟区域是我们的许多问题的起源。
    并且作者发现在1117中字体中1059字体,大约95%,有一个line-height计算大于1.他们的计算从0.618到3.378。

    <b>vertical-align:one property to rule them all</b>

    奇怪的是,默认的基线对齐可能会导致更高的(!)行框,一个行框的高度是从它的孩子的最高点到它的孩子的最低点计算的。

    继承的font-family可能不是想象中的大小,因为每个标签的基准(baseline)很可能是不同的,所描述的line-box高于预期。因为浏览器计算每个line-box都是以0宽度开始的,称为strut。(This happens because browsers do their computation as if each line-box starts with a zero-width character , that the spec called a strut.)

    规范是这么写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比也不同,使用align也不可靠。middle不是真的在中间。

    vertical-align: top / bottom align to the top or the bottom of the line-box

    vertical-align: text-top / text-bottom align to the top or the bottom of the content-area

    依旧是原博客图

    注意,在所有情况下,它对齐虚拟区域,因此是不可见的高度。看看这个使用vertical-align: topline-height的例子。看不见可能产生奇怪,但不令人惊讶的结果。

    我说怎么对不齐呢

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

    <b>CSS is awesome</b>

    我们已经讨论过如何让line-height和vertical-align一起工作,但现在的问题是:字体指标可以用CSS控制吗?
    简答:不。
    字体指标是不变的,所以我们应该能够做一些事情。

    例如,如果我们想要一个使用 Catamaran的文本,其中captical-height正好是100px高?
    看起来可行:让我们做一些数学。

    首先,我们将所有字体指标设置为CSS自定义属性,然后计算font-size获得100px的captical-height。

    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”字母的顶部和底部?为了实现这一点,我们必须vertical-align根据上升/下降比率来计算。
    首先,计算和内容区域的高度:line-height: normal

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

    然后需要:

    the distance from the bottom of the capital letter to the bottom edge
    the distance from the top of the capital letter to the top edge

    像这样:

    p {
        …
        --distanceBottom: (var(--descender));
        --distanceTop: (var(--ascender) - var(--capitalHeight));
    }
    

    我们现在可以计算vertical-align,这是乘以计算的距离之间的差异font-size( which is the difference between the distances multiplied by the computed font-size这句啥意思??)。(我们必须将此值应用于内联子元素)

    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>Takeaways</b>

    我们学到了什么:

    • 内联格式化上下文真的很难理解

    • 所有内联元素都有2个高度:

    • 内容区域content-area(根据字体规格)

    • 虚拟区域virtical-area(line-height)

    • 这2个高度中没有一个可以可视化,毫无疑问。
      (如果你是一个devtools开发人员,想要工作,这可能是真棒)

    • line-height: normal 是基于字体度量

    • line-height: n可以创建小于内容区域虚拟区域

    • vertical-align 不是很可靠

    • 一个line-box的高度是根据它的孩子们的line-height和vertical-align
      属性来计算

    • 我们不能很容易地获取/设置CSS的字体指标

    • 有一个相关的未来规范来帮助垂直对齐:线网格模块

    但我还是喜欢CSS :)

    后记:没想到花了好几个小时,很佩服原作者这种默默的奉献,他一定花了比我更多的时间去写,去做图让读者更好理解。

    相关文章

      网友评论

          本文标题:line-height vertical-align

          本文链接:https://www.haomeiwen.com/subject/jvaowttx.html