微信城市服务的 UI 优化


城市是我家 创建靠大家


 
  闲话不说,我们直接进入本文的主题-微信城市服务的 UI 优化。

  说起这次优化的初衷,是因为微信内的“调整字体”功能,如下左图所示,用户可以根据自身的需要调整屏幕内所有文字的大小,这确实是一个不错的功能。但是,UI 上的展示却有一些可以改进的地方。


  这里的改进我总结主要为4个方面,接下来我们就逐个去讨论并解决它们。

  1. 在字体变大的情况下,很容易出现超过容器元素的宽度。

  如上左图,我们可以看到“网络不良信息举报”字段就出现了文本省略号截断。由于移动端没有类似 PC 端的 title 属性,可以让鼠标放上去显示原来的文本内容,所以对于可能会溢出的内容,我们并不应该全部采用省略号截断的方法,而是根据实际情况以及和产品的沟通去决定是采取换行、截断或者滚动条。

  如果采用换行,可以加上下面的样式:

1
2
3
4
line-height: 1.2; // 按需设置
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3; // 按需设置

  如果是截断的话,可以使用下面样式,详细介绍可以戳我这篇文章:CSS之文本截断

1
2
3
4
width: 200px; // 设置宽度
overflow: hidden; // 截断溢出
text-overflow: ellipsis; // 将隐藏的溢出部分显示出省略号
white-space: nowrap; // 强制不换行

  滚动的话,overflow 就可以搞定啦~

  2. 因为固定高度(height)的原因,会导致在字体变大的情况下出现内容在垂直方向上的不完全显示。

  关于这点,我就啰嗦几句大家或许都知道的姿势-内联元素的高度从何而来?其实内联元素的高度是由内容区域和行间距直接影响,同时内容区域高度+行间距 = line-height。同时,对于 line-height 的取值一共有 5 种,其中比较特殊的就是 number,它会根据当前元素的 font-size 大小计算。如 font-size: 16px; line-height: 2,那么此时 line-height 的值就是32px。想更“深入”了解的话,可以戳这里:css 之 line-height 和 vertical-align

  由以上 2 点,我们就知道怎么解决固定高度带来的问题了~那就是去除文字元素的 height ,同时根据之前的高度给它们都加上 number 类型的 line-height。举个栗子,如果有个p 元素的 height: 32px; font-size: 16px,我们就可以将 height 去除,再加上 line-height: 2 就可以啦。

  做到这里,就够了吗?起码我觉得还不行,这就是后面我需要单独介绍的:城市服务全站 rem 化。

  3. 在字体可以调整的情况下,我们应该多使用 flex 布局,同时注意不要混用 flex 布局和其他 css 样式(某些安卓机有 bugs)

  比如下图(优化过的)头部的 banner 样式,如果可以用 flex 属性实现需求时,就不要用非 flex 属性去写。再举个栗子,下图 banner 中的 “深圳”、“消息”和“个人中心”,我们直接用 justify-content: space-between 就可以,而不要给中间的“消息”加 margin: 0 auto; 否则某些安卓机就会无法正常显示。
  

  4. 全站 rem 化

  提到 rem,相信前端和 UI 同学都熟悉,因为它相对于流布局、响应式布局更加灵活,而且兼容性更好。这样的好东西用在“调整字体”功能上,才是最佳的解决方案。

  咳咳,老规矩,先科普下 rem。首先它确实是用来设置字体大小的,但它也可以用于 width、height、margin 这些样式的单位,其本质是根据页面的根元素来设置 font-size 大小。很多人都爱拿它和 em 比较,并且认为 em 是根据父元素的字体大小来设置 font-size,其实这是个误区。事实上,根据 W3C 标准,它是相对于使用了 em 单位的元素字体大小,之所以父元素的字体大小会影响 em 值,完全是因为 em 属性的继承性(遗传效果)。又想“深入”了解的话,请戳:综合指南: 何时使用 Em 与 Rem

  其实使用 rem 单位的主要目的应该是确保无论用户如何设置自己的浏览器,我们的布局都能调整到合适大小。一个站点最初设计可以专注于最常见的默认浏览器中字体大小 16px。总而言之,如果确实需要更改元素的字体大小,比如微信里的“调整字体”功能,通过使用 rem 单位,我们也能保证布局的完整性。

  接下来,我们说下具体的实现:整个过程的原理并不复杂,首先我们编写代码的时候还是按照 px 去实现需求,然后再根据 html 元素的 font-size 将 px 替换成 rem,最后通过 media-query 来进行不同屏幕的适配。

  不过有很多细节需要处理,下面开始一一介绍。这里是用的构建工具是我们组开发的 tmt-workflow ,如今已经升级为一款具有 GUI 的开源工具 WeFlow,安利一下,非常棒的一款前端利器!
  

  这里先需要重点介绍 3 个前端包 postcss-pxtoremposthtml-px2remgulp-tmtsprite,前 2 个是通过正则匹配分别将我们 css 文件(外联样式)和 html 文件(内联样式)中的 px 自动转换成 rem。

  但是由于现在一般前端构建都会使用“合并雪碧图”,而对于雪碧图来说,不能使用 rem,否则会导致图像无法正常显示。这时我们就需要修改下 gulp-tmtsprite 中的 sprite.js,将其中与 background 相关属性设置的单位改成大写的 PX,这样它们就不会被转成 rem,算是构建流程中预留给强制不使用 rem 的 hack。

1
2
3
// CSS替换
var code = 'background-image: url("' + opt.spriteOut + cssBaseName + '.png");'
code += ' background-position: -' + (sprite.x + sprite.meta.margin) + 'PX -' + (sprite.y + sprite.meta.margin) + 'PX;'

1
2
3
4
5
6
7
8
// 添加 media query
lodash.each(sprite.meta.className2x, function (item) {
retinaCssContent += item
retinaCssContent += '{background-image:url("' + opt.spriteOut + cssBaseName + '@2x.png");'
retinaCssContent += '-webkit-background-size:' + retinaLayerInfo.width / 2 + 'PX;'
retinaCssContent += 'background-size:' + retinaLayerInfo.width / 2 + 'PX;'
retinaCssContent += 'background-position: -' + ((sprite.x + sprite.meta.margin) / 2) + 'PX -' + ((sprite.y + sprite.meta.margin) / 2) + 'PX;}'
})
1
2
3
4
5
6
7
8
// 添加 media query
lodash.each(sprite.meta.className3x, function (item) {
retinaCssContent += item
retinaCssContent += '{background-image:url("' + opt.spriteOut + cssBaseName + '@3x.png");'
retinaCssContent += '-webkit-background-size:' + retinaLayerInfo.width / 3 + 'PX;'
retinaCssContent += 'background-size:' + retinaLayerInfo.width / 3 + 'PX;'
retinaCssContent += 'background-position: -' + ((sprite.x + sprite.meta.margin) / 3) + 'PX -' + ((sprite.y + sprite.meta.margin) / 3) + 'PX;}'
})

  注意一点,因为 1px 转化成 rem 会有小数 bug,所以工作流中会忽略 1px 的转换,详情戳:rem 产生的小数像素问题

  接着引入一个设置 html 元素 font-size 的 media-query 文件,用于适配不同屏幕,code 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// REM 适配 <html>

// 375px (iPhone 6)
// 此为基准值, 与视觉设计稿 `宽度/2` 保持一致
html {
font-size: 20PX;
}

// 17.06 = 320*20/375 (iPhone 5)
// 此处 hack 写法,使用 19px 代替 17px,兼容过小(<10px)的中文字体使用
@media only screen and (min-width: 320px) {
html {
font-size: 19PX !important;
}
}

// base root size (iPhone 6)
@media only screen and (min-width: 375px) {
html {
font-size: 20PX !important;
}
}

// 21.33333333 = 400*20/375 (Most Android)
@media only screen and (min-width: 400px) {
html {
font-size: 21.33333333PX !important;
}
}

// 22.08 = 414*20/375 (iPhone 6 Plus)
@media only screen and (min-width: 414px) {
html {
font-size: 22.08PX !important;
}
}

// 25.6 = 480*20/375 (iPad)
@media only screen and (min-width: 480px) {
html {
font-size: 25.6PX !important;
}
}

  最后,在 .tmtworkflow 文件中加入开启 rem 的设置就大功告成啦~

1
"supportREM": false
MaybeXia wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创分享,您的支持将鼓励我继续创作!