Icon和文本对齐方式的探索

栏目: CSS · 发布时间: 5年前

内容简介:在Web中很多场景中都会使用到Icon,那么就会面临Icon和文本对齐的处理。而这个对齐效果的处理又不是一件轻易的事情,特别是面又众多不同移动终端的情形之下。那么今天这篇文章就来和大家一起探讨一下这方面的话题。这里所说的Web中的图标是指Web中的Icon图标。从《Web中的图标》一文中,我们可以得知,到目前为止,在Web中使用图标的方式主要有:不管是哪一种方式,都有各自的利弊,至于怎么选择,可以阅读《Web中的图标》一文。在这里,我们主要来探讨的是

在Web中很多场景中都会使用到Icon,那么就会面临Icon和文本对齐的处理。而这个对齐效果的处理又不是一件轻易的事情,特别是面又众多不同移动终端的情形之下。那么今天这篇文章就来和大家一起探讨一下这方面的话题。

Web中的图标

这里所说的Web中的图标是指Web中的Icon图标。从《Web中的图标》一文中,我们可以得知,到目前为止,在Web中使用图标的方式主要有:

img
svg

不管是哪一种方式,都有各自的利弊,至于怎么选择,可以阅读《Web中的图标》一文。在这里,我们主要来探讨的是 图标和文本的对齐姿势

Web中引用图标的姿势

在Web应用程序或者Web页面中,引用Icon图标,常见的方式主要有:

<elem>
    <img src="...">文本
</elem>

<elem>
    文本<img src="...">
</elem>

<elem>
    <svg></svg>文本
</elem>

<elem>
    文本<svg></svg>
</elem>

<elem>
    <iconfont></iconfont>文本
</elem>

<elem>
    文本<iconfont></iconfont>
</elem>

但在很多情况之下,为了更好的让Icon图标和文本能对齐(垂直对齐),大部分同学喜欢用一个行内元素,比如 <span> 把文本包裹起来,就像下面这样:

<elem>
    <img src="..."><span>文本</span>
</elem>

而实际中,有些场景是无法让我们人肉的给文本内容添加类似 <span> 这样的标签,特别是在CMS系统中的操作,或者说通过JavaScript动态插入内容。

对于如何使用CSS让Icon图标和文本能够垂直对齐,我想大家脑海中第一浮现的属性是 vertical-align 。那么,是否使用该属性就真的能让Icon图标和文本能完美的实现垂直居中呢?希望大家带着这样的一个问题继续往下阅读。

浏览器的默认行为

首先来看看浏览器的默认行为。先来看两种情形:

<elem>
    <img />case
</elem>

<elem>
    <img /><span>case</span> 
</elem>

为了让样子好看一点,添加一点基本样式:

body {
    padding: 2vw;
    font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue",
    "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC",
    "WenQuanYi Micro Hei", sans-serif;
    font-size: 14px;
    line-height: 1.7;
    -webkit-font-smoothing: antialiased;
}
svg,img {
    width: .85em;
}

.fas {
    font-size: 170px;
}

在你的浏览器下看到的效果如下:

Icon和文本对齐方式的探索

接下来稍稍改变一下,让浏览器自行让Icon和文本垂直对齐。到目前为止,主流的有两种方式:

  • inline-flexalign-items
  • inline-blockvertical-align

前者采用的是CSS Flexbox的模式,后者是较为传统的垂直对齐模式。一般情况之下, inline-flexalign-items 运用于:

<elem>
    <img />case
</elem>

即: 文本没有额外的HTML标签包裹 !而 inline-blockvertical-align 一般用于:

<elem>
    <img /><span>case</span>
</elem>

事实上, inline-flex 也可以运用于后者的结构中。

特别声明,示例代码中的 <img /> 标签可以是 <svg> 或者 <iconfont> ,其中 <span> 标签也可以是其他任何你希望的HTML标签或者HTML自定义标签。

为了能更为形象的看出它们之间的差异性。来做几个测试用例,看看浏览器对其渲染效果的差异性。

先看 inline-flex (不管带不管 span 标签):

.demo {
    display: inline-flex;
    align-items: center;
}

看上去似乎对齐:

Icon和文本对齐方式的探索

如果按照 inline-flexalign-items 的属性特性描述来判定,Icon和文本是应该对齐(注意是 应该对齐 )。那么我们把页面的截图放到设计软件中来描绘制一下:

Icon和文本对齐方式的探索

为了能更好的展示测试效果,我把文本换成了三种情形。仅从页面上的显示效果(肉眼看上去),似乎对齐,但事实并非如此。

接着再来看第二种对齐方式:

.demo img,
.demo svg,
.demo .fas,
.demo span{
    display: inline-block;
    vertical-align: middle;
}

浏览器渲染出来的效果如下:

Icon和文本对齐方式的探索

左侧和右侧的效果有明显的差异性。主要是左侧的文本内容没有使用额外的HTML标签包裹。如果你观察足够仔细的话,使用 inline-blockvertical-align:middle 会造成文本离开 baseline 的基线,这种现象会直接影响上下间距和相邻元素的对齐。

特别声明:上面测试效果截图来源于 Chrome v67.0.3396.99。MacOS v10.12.6。其他平台,特别是Android系统下测试结果略有偏差!

简单分析

前面我们看到了浏览器对于Icon和文本垂直对齐的渲染效果。不管使用的是哪一种方式(浏览器对齐方式),最终看到的效果都只是近似对齐。为什么会这样呢?这里简单的来分析一下。

IFC概念

IFC是“Inline Formatting Context”的缩写,类似于BFC的一个概念,其主要用来 定义行内盒子(Inline Box)组成上下文中的表现特性 。有关于IFC的详细描述, 可以查阅W3C规范中的相关说明

根据W3C规范的描述我们可以得知:

由IFC描述可以了解到,行内盒子(Inline Box)是由 行内级元素(Inline Level Elements)文本(Contents) 所组成,而行内盒子水平排列所构成的一行矩形区域,被称作 行盒子(Line Box)

从IFC的场景中再回到我们的实际场景中,Icon图标是一个行内级元素,而文本是另外一个行内级元素,把这两个元素形成的两个行内盒子放在一起,构成了一个行盒子。除了 inline-blockinline-flex 元素和文本是行内级元素外, display 属性为 inlineinline-table 的元素以及像 imginputvideo替换元素(Replaced Elements) 都是行内级元素,但它们在行盒子中的设计计算方式不太相同。

替换元素的高度等于它们本身的高度(包含 margin ) ,而非替换元素的高度,其计算非常复杂,其中字体度量(Font Metrics)对其影响就较大。

Font Metrics

Font Metrics常常被称之为 字体度量 ,指的就是字体中的一系列参数,这些参数对于CSS而言是不可见的,但我们可以借助类似 FontForgeOpentype.js工具 来获取。

Icon和文本对齐方式的探索

如果你对字体度量较为感兴趣,强烈建议你花一些时间 这篇文章 进行了解。这里用一张经典的图来展示我们更为关注的一些信息:

Icon和文本对齐方式的探索

而我们关注的是,怎么得到元素的实际高度。假设我们的示例中,设置了 font-size100px ,它的高度并不一定等于 100px ,比如这里,其实际高度就是 100 / 2048 * (2167 + 536) ≈ 132px

Icon和文本对齐方式的探索

其他概念

这里所说的其他概念,指的就是CSS中有关于字体相关的一些属性:

vertical-align
baseline
line-height
font-size

那么在这里我并不想花太多时间来解释这些概念或者说这些属性的具体使用。但对于Icon和文本的对齐方式的的确确会受到这些属性的值的影响。或者简单的说,将会受到字体度量相关的参数影响,而这几个又是其重要的一些参数。

假设你对这些属性和相应的概念有了深入的了解。如果你对它们还不够了解,建议你花时间先阅读下面这些文章,因为这些文章可以更好的帮助你理解后面的内容,和找到解决方案的依据:

解决方案

特别声明,整个方案的解决思路来源于@长天之云的《 图标如何对齐文本 》一文。

虽然使用 inline-flexalign-itemsinline-blockvertical-align 可以达到对齐效果,但这些效果都是近似对齐。因此很多同学会质疑,为什么没有真正对齐,特别是在Android系统上的一些APP中。造成这种现象主要是我们没有搞清楚浏览器是如何决定图标放置,它们对齐又是参照什么方式。

如果要彻底解决,那就需要搞清楚它们之间的关系。

假设你对字体的特征和CSS的 vertical-align 有了一定的了解。因此我们直接进入主题。先来看一张图:

Icon和文本对齐方式的探索

从这张图中,可以获取很多有价值的信息:

  • 最左侧是一个已对齐的Icon图标,它的尺寸是 1.2 x 1.2 em
  • 最右侧是文字特征的一些横向参考线,除了 cap-line 之外都是 vertical-align 的可选值,每种值得到的不同效果,可以阅读 vertical-align ,你应该知道的一切 一文
  • vertical-align 作用于文本和非文本(比如示例中的 imgsvg 等元素)效果将不同,比如 vertical-align 取值为 baseline 时:“同一行内不同字体类型、字体大小或不同行高的文本对齐在相同的 baseline 上;同一行内不同尺寸的图片底边对齐在 baseline 上”
  • xHeight 为小写字母 xheight ,可以用 ex 单位来表示( 1ex 大约等于 0.5em ),如果 vertical-align 取值为 middle ,那么 middle 的值就是 xHeight 的一半( xHeight / 2 ),因此,如果仅对图片应用于 vertical-align: middle 时,图片看上去会偏下
  • capHeight 为大写字母高度(CSS和JavaScript都无法获取),大多数字体约为 0.7em

了解这些信息之后,我们可以考虑Icon图标和大定字母中间对齐,即: baseline 往上先移动 capHeight / 2 。 这样一来可以考虑:

从baseline基线开始偏移

默认情况之下,Icon图标底边贴在 baseline 上,可以先移动Icon图标自身 50% (向下移动,比如 translateY(50%) ),使其中间对齐 baseline ,然后再上移 capHeight / 2

.demo img {
    vertical-align: baseline;
    transform: translateY(calc(50% - 0.35em))
}

从middle基线开始偏移

Icon图标正中间正好在 middle 基线上,所以可以先将图片下移 xHeight / 2 ,然后再上移 capHeight / 2

.demo img {
    vertical-align: middle;
    position: relative;
    top: -.1em; // xHeight / 2 - cpaHeight / 2 = (.5 - .7) / 2 = -.1em
}

也可以使用 ex

.demo img {
    vertical-align: middle;
    position: relative;
    top: calc(.5ex - .35em);
}

其实这种方式也是我们常用的一种方式,但最终的效果也只能得到近似对齐的效果,因为示例中的偏移值会根据当前字体不同而有细微差异。比如下图所展示的数据,就是根据Font Metrics的参数,针对不同字体计算出来的值:

Icon和文本对齐方式的探索

感觉花这么多时间,并不解决我们想要解决的问题。那么有没有办法实现精确的对齐呢?先来看内容,再来找答案吧。同样先上一张图:

Icon和文本对齐方式的探索

内联元素在不同条件下产生了不同的边界(颜色填充区域),先来解释一下这张图展示的相关信息:

j 表示基础的 line-height ,也就是 1em 高,等同于当前的 font-size:100px 。当 inline-blockinline-flex 元素设置为 line-height: 1 时,就可以得到它,图中橙色区域。值得注意的是, j 的边界不在任何参考线上( vertical-align 的值对应的参考线)。也就是说,使用这个值的时候要特别小心,特别是设置了 overflow: hidden 时,有可能会造成内容被截断。这也就是为什么很多同学在一些运用中会发现,文本之类的显示不全。

x 表示安全的 line-height ,是 inline 元素默认的高度(也是 text-toptext-bottom 的距离)。也对应 (ascender + descender) / unitsPerEm 计算得到的值(Font Metrics的一些参数),在我们的示例中,采用的字体对应的值是 1.17777

S 表示实际的 line-height ,是 inline-blockinline-flex 元素默认的高度(也是 topbottom 的距离)。当父容器设置 line-height: 1.7 时,元素对应的实际 line-height100px * 1.7 = 170px

有了这些信息只是我们实现精确对齐的基础,关键点我们还需要借助一个参考特。这里可以借鉴CSS规范中的 Strut概念

On a block container element whose content is composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element. The minimum height consists of a minimum height above the baseline and a minimum depth below it, exactly as if each line box starts with a zero-width inline box with the element's font and line height properties. We call that imaginary box a "strut." (The name is inspired by TeX.).

也就是说,创建一个局部的容器,生成不可见文本(零空格,模拟Strut),让不可见文本对齐Line Box中其他文本,让图标对齐这个不可见文本。

局部容器的高度可以是上图中 S 的高度( inline-flex 居中),也可以跟随图标的高度(图标绝对定位,保持与容器位置相同),也可以固定(图标绝对定位居中,但缺陷是不能超过行高太多)。

这样一来,我们的结构可以修改为:

<elem>
    <span> <img /></span>文本
</elem>

同样可以使用 inline-blockinline-flex 来完成。先来看 inline-block 的CSS代码:

span {
    position: relative;
    display: inline-block;
    line-height: 1; // 使文本高度为图标高度
    width: 1em; //图标大小,用来占位
}
span img {
    position: absolute;
    top: 0;
    left: 0;
    width: 1em;
    height: 1em;
}

对于 inline-flex 要比 inline-block 简单的多:

span {
    display: inline-flex;
    align-items: center;
}

原来方案就是这么的简单。

特别声明,整个方案的解决思路来源于@长天之云的《 图标如何对齐文本 》一文。

除了手动在HTML结构中添加 &#8203; 来模拟Strut之外,还可以借助CSS的伪元素 ::before 来处理:

<elem>
    <span>
        <img />
    </span>
    文本
</elem>

在上面的代码中添加:

span::before {
    content: '\u200b'
}

为了一劳永逸,也可以考虑将其封装成一个组件,比如@长天之云将其封装成了一个 React组件 。如果你感兴趣的话,也可以将其封装成Vue组件等。

小技巧:如果你项目中使用的是 svg 标签制作Icon图标。为了能让图标像Icon Font那样缩放,建议你在 svg 中设置 widthheight 的时候采用 em 单位,比如 width: 1em;height:1em ,就相当于SVG图标会根据 font-size 进行缩放。有关于这方面更详细的介绍可以阅读《 Align SVG Icons to Text and Say Goodbye to Font Icons 》一文。 译文可以点击这里阅读

总结

Icon图标和文本对齐是日常开发中常碰到的细节问题,在不同的环境和场景中都会花费一定的时间来处理这样的细节。这篇文章主要深纠了一下Icon图标和文本对齐的一些原理,通过这些原理找出相应的解决方案。文章提供了两种解决思路,一种是大家时常用的近似对齐方式,另一种是精确的对齐方式解决方案。

如果您有更好的解决方案,欢迎在下面的评论中一起交流和探讨!

扩展阅读


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Struts 2 in Action

Struts 2 in Action

Don Brown、Chad Davis、Scott Stanlick / Manning Publications / 2008.3 / $44.99

The original Struts project revolutionized Java web development and its rapid adoption resulted in the thousands of Struts-based applications deployed worldwide. Keeping pace with new ideas and trends......一起来看看 《Struts 2 in Action》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具