前端切图仔还原UI设计师的设计稿是对UI的一种尊重,不能还原那就是你太菜了。每次我们欢天喜地的去找UI走查的时候,UI是这样的
你一看代码:
.center {
display: flex;
justify-content: center;
align-items: center;
}
心中一惊:这都能对不齐,那一定是你的机子有问题,建议换一台,UI可能要让你当场横尸街头。今天我们来深究一下这个问题。
问题复现
首先我们把问题复现出来,我们不用搞什么花里胡哨的内容,直接一个span标签扔上来,你可能会说我傻,一个span和谁对齐,我们给它加个border试试
.border {
border:1px solid black;
}
我们来看效果,这是在华为Mate30自带浏览器中渲染的结果
如果你看不出有什么问题,那么我们再展示一下PC端chrome渲染的结果
能看出问题了吗?显然在安卓上渲染的文字,在content area(黑色方框的内容)中就已经不居中了,它会偏上,而我们后续的操作都是在对齐content area,所以我们没办法用CSS去解决这个问题,即使解决了也是一种hack的方式,因为CSS是没办法影响到content area的。那么什么可以影响到content area?字体可以,我们来试一试下面的代码,更改一下字体,我们看看现在在PC端会渲染成什么样子
.fantasy {
font-family: fantasy;
}
渲染出来是这个结果
那么我们此时有一个需求,很简单,用div实现一个button,里面的文字需要居中
.button {
border: red 1px solid;
width: 50px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
margin: 10px;
}
我们来看看两种字体渲染的结果:
很明显,第一种字体没有实现居中,这是因为它在content area中本来就已经偏上了,所以当content area居中的时候,字体一定不可能居中。注意,这是在PC端渲染的结果。所以你能想到为什么移动端的文字不能居中的么?对,就是默认字体的问题。那么移动端的默认字体会是什么呢,不同手机厂商都不相同,因为国内安卓手机太多了,所以也就导致可能会选中content-area中的文字已经偏离的字体。
如果我们使用height和line-height相同的方式居中,文字会和在content-area中有相同的偏离。但是如果我们用flex去居中,确发现conten-area中的内容也偏离了,但是和直接渲染span偏离的又不同了。
比如下面这种:
这是通过设置height和line-height同高的居中方式,可以看到conten-area中的文字偏上导致没有居中
而这是在PC端的渲染效果
可以看到这是很完美的居中了
而当我们用flex布局去居中时,得到的结果是这样的
可以看到content-area中的文字偏下了…所以导致整体渲染偏下了,为什么偏下,这个真涉及到了我的知识盲区,我只能把锅甩给安卓的渲染了。
网上说法
你拿着这个问题去google去百度,能得到这几种说法:
小于12px的文字会有这个问题
通过博客中放出的截图,确实在小于12px时content-area中的文字会偏离,但是大于12px就不会
但是在我的手机上,无论是多大都会出现偏离的现象。所以看看发文日期,只能说一句:时代变了,大叔
安卓寻找默认字体的机制有问题
文章链接放在了末尾,其实我觉得这篇文章说的有部分内容是对的,部分内容和我目前实验的结果不同,各位看官自行实验评判一下吧。
字体问题
我个人是最赞同这种观点的,这个问题不仅仅会出现在安卓上,也会出现在ios,PC端上。但是因为PC端,IOS端选择字体的时候可能有一套固定的机制,所以总能选中content-area内文字居中的字体,所以才能正常的居中。而安卓选择默认字体的时候百花齐放,导致content-area内的文字已经偏离了,后续进行任何修改都是没办法挽回的。
思考原因
为什么网上会出现这么多关于居中问题的说法,又各自给了解决方案,但是今天在我的手机上却没有效果。我个人觉得:这依赖于安卓渲染机制。各大手机厂商不能统一标准,所以可能会出现各种的渲染方式。我们看到的文章大多数是好几年以前的文章,手机几年的时间变化非常快,所以可能之前的有的问题现在没有了,之前的解决方案现在不行了,我更倾向于把这个没有居中的问题当作安卓的一个bug。
同时,我觉得最靠谱的就是安卓的字体渲染有问题,所以我们想要从根本上解决问题,那就得从字体入手,其他方式都没办法去解决这个问题。
尝试解决
指定默认字体
总而言之,就是安卓渲染的字体有问题…那么我们指定一个字体方正:
@font-face {
font-family: fz;
src: url('./font/fz.ttf') format('truetype');
}
.fz {
font-family: fz;
}
这是在PC上渲染的结果
这是在安卓上渲染的结果
可以看到,我们的居中已经不会出现偏差了,因为我们强制的去使用了同一种字体。那么我们现在再来尝试一下将文字和图标对齐。
可以看到我们的方正是没问题的,而默认字体会出现文字下沉的情况
放大两倍再缩小两倍
就我观察到的情况而言…这种方法并没有什么用,文字还是会下沉
而有时候这种情况确实有用,我个人觉得是因为我们在项目中使用的是rem,而rem到px的时候可能会产生很多奇怪的px数值,比如奇数和小数,这时候不同的手机可能会对这种px进行不同的处理。所以我们放大两倍相对于减少奇怪px出现的情况,让各种安卓机型尽量能对他进行一致的处理,当然,如果px解读正确了,但是字体还是不对,自然还是无法居中,所以表现就是没有任何效果。
用margin微调
别笑,我们试试,加一个margin-bottom: 2px
浏览器的效果:
安卓的效果:
IOS的效果
怎么样,视觉效果是不是还可以。但是你仔细观察就会发现,其实在浏览器和IOS上文字是偏上的,但是乍一看还是可以的。
完美解决方案
上面说的方案其实都不怎么样,有没有一种完美的解决方案呢?有,我们看这个
https://www.w3.org/TR/css-inline-3/#leading-trim
leading-trim这个属性的作用就是删除掉文本上下的空白,这样我们的content-area就完全等于我们的字体高度了,所以,我们居中的时候自然就能很完美的将文本居中,和小图标居中也自然是轻轻松松。
可惜的是目前这个属性还是草案的一部分,我们现在没办法直接使用他,所以还是只能用hack的方式去解决这个问题
总结
经过一番研究,我认为,这就是安卓的一个bug…所以才会出现各种说法,也出现了各种解决方案,但是你去从尝试的时候又会发现,这玩意没用啊。
所以我们总结一下:
- 不要在移动端使用line-height,移动端的渲染方式已经够奇怪了,不要再给他增加不确定因素
- 使用rem时,为了确保最后的px值不那么奇怪,我们可以放大缩小两倍,但是不一定有用,或者直接使用px不要使用rem。这也可以解决12px以下的字体渲染错误的问题(虽然我还没能复现这个问题)
- 最后,我们可以通过margin去进行微调,让视觉效果在各端保持基本的一致
其实最好的方法还是指定字体,但是中文字体一般都很大,在加载时很慢,所以我们如果想指定字体,就必须要解决这个问题,而如何去解决字体加载的问题,这又是另一个问题了,不在我们这次的讨论范围之内。
参考资料
- https://imweb.io/topic/5848d0fc9be501ba17b10a94
- https://rprns.me/2018/07/27/%E5%85%B3%E4%BA%8E%20Android%20%E4%B8%8B%20line-height%20%E6%96%87%E5%AD%97%E5%9E%82%E7%9B%B4%E5%B1%85%E4%B8%AD%E5%81%8F%E7%A7%BB%E7%9A%84%E6%80%9D%E8%80%83/