向程序学习 UI 设计

I think everybody in this country should learn how to program a computer, because it teaches you how to think.

乔布斯在《遗失的访谈》中说过「每个人都应该学习编程,因为它教会你如何思考」

前言

在上一家公司,由于 UI 妹子离职和招聘问题,不得以兼任了 UI 视觉工作。虽然之前的产品/交互工作和 UI 有很多交集、自己的个人项目中也会有一些 UI 设计,但却不曾独自去设计一个完整产品的 UI。这一回,算是从「动口」到「动手」了,好在是 B 端产品,对视觉美观性要求没那么高,相对而言统一性更为重要。

之前作为 UI 上游环节时,我总是会给 UI 提意见,要求尽量统一、要求考虑实现成本……现在自己做 UI 后,才发现满足自己的要求挺困难的。虽然最终并没有达到我自己满意的程度,但这段实战经历,确实使我对 UI 设计的统一性有了一些更深入的理解,尤其是将「视觉设计的感性思维」和「程序实现的逻辑思维」融合后,有了一个全新的视角。

我的这些理解,对于很多前端工程师来说,可能属于「常识」;但对于没有前端经验的产品/交互/UI 设计师而言,我相信是有一定帮助的。如果我自己能早几年想明白,至少前几个项目在 UI 的统一性上可以提升不少。

P.S. 本文出现的代码都是 CSS 样式代码、没有任何复杂的逻辑。没有代码基础的同学,请不要抗拒,当成英文来读就行。

如何绘制一个界面?

从设计视角来看

  • 在各类设计工具(如 Sketch)中,是图层的组合


    Sketch 图层

从工程视角来看

  • 在 CSS 中,是各种「盒子」之间的组合
  • QT 中,是各种 Widgets 的组合

无论从哪个角度,界面都是通过各种元素之间的各种组合、叠加,拼接而成。

如何使绘制出的界面统一?

既然界面是由各种元素拼出来的,那要达到整体界面统一,这些构成元素自然需要先统一。统一的方式,就包括建立一套「控件库」(或者说 Symbol、模板、Library、UI Kit……有许多不同的称呼),总之就是预定义一些「元素」及「元素组」,通过对有限的「元素」及「元素组」做统一,使得最终组合而成的界面也能在某种程度上实现统一。

如果你从事过 UI 相关工作,一定或多或少了解过诸如此类的「控件库」、或是 Atomic Design 之类的设计思想。

如何设计一套统一的「控件库」?

前面的都是概念,这才是实际要做的事情。为了省篇幅,我仅以「按钮」为例,讲述一下我对于「统一控件」的理解。

阶段一

这其实是我刚接触 CSS 时的理解,现在只是回顾一下最初那种浅显的想法。
虽然这种理解非常浅显,但不少软件连这一步都没做到。
最初我以为,统一是:

所有的中号按钮,高度都是 24px
所有的蓝色按钮,色值都是 #0000FF
……
总之,只要最终看效果上去统一就行,没有考虑其背后的实现,没有「控件库」的概念。

写成代码,可能会是这样:

  • 假设有 99 个按钮,每个按钮都有自己的样式,只是这 99 种样式被强行设成了相同。
.button-1 {
  height: 24px;
  background-color: #0000FF;}
.button-2 {
  height: 24px;
  background-color: #0000FF;}
...
.button-99 {
  height: 24px;
  background-color: #0000FF;}
阶段二

这是我自己动手做 UI 之前的想法,要求 UI 设计师预先规划好所有可能会出现按钮(3 种尺寸、每种尺寸又有 3 种不同颜色,共计 9 种按钮)。


枚举出 9 种按钮

这时候,我以为统一是:

预先枚举出所有可能用到的标准控件,加入控件库。
程序实现时,统一使用控件库中的标准控件。
……
虽然有了「控件库」的概念,但实现手段是非常粗暴的「枚举法」

写成代码,可能会是这样:

  • 假设有 99 个按钮,所有按钮都是只从这 9 种预设样式中选一种
.button-normal-small {
  height: 20px;
  background-color: #FFFFFF;}
.button-normal-middle{
  height: 24px;
  background-color: #FFFFFF;}
.button-normal-large {
  height: 32px;
  background-color: #FFFFFF;}

.button-blue-small {
  height: 20px;
  background-color: #0000FF;}
.button-blue-middle{
  height: 24px;
  background-color: #0000FF;}
.button-blue-large {
  height: 32px;
  background-color: #0000FF;}

.button-red-small {
  height: 20px;
  background-color: #FF0000;}
.button-red-middle{
  height: 24px;
  background-color: #FF0000;}
.button-red-large {
  height: 32px;
  background-color: #FF0000;}

阶段三

这时,我需要自己去制作控件库了。作为一个懒人、我可不想枚举出所有的按钮。所谓懒惰使人进步,我带着问题去看了一下各大网站的网页代码。很容易就能发现了规律,人家是用多个 css 类叠加出来的。
对嘛!设计的时候是 3 种尺寸、3 种颜色,也就是分「尺寸」和「颜色」这 2 个维度,那实现的时候也应该是用维度叠加,而不是枚举。
原来需要画 3 × 3 = 9 个按钮,现在只需要画 3 +3 = 6 个了!


分成 Color 和 Size 两个维度

这时候,我以为统一是:

将一个控件抽象成「特定维度的叠加组合」
实现时,通过限定这些维度来实现统一

写成代码,可能会是这样:

  • 分别设「color、size」这 2 个维度的样式,使用按钮时,用这 2 个维度的叠加效果。
.button.color-normal {
  background-color: #FFFFFF;}
.button.color-blue {
  background-color: #0000FF;}
.button.color-red {
  background-color: #FF0000;}

.button.size-small{
  height: 20px;}
.button.size-middle{
  height: 24px;}
.button.size-large{
  height: 32px;}

阶段四

其实对于「设计指南」而言,通常就是只到「阶段三」的层次。强如 Google 的 Material Design,虽然标注很细致,但依然是属于「阶段三」这个层次。


Material Design

基于 Material Design 的这种标注,你可以尝试解释一下这 2 个问题:

  1. 为什么按钮的高度是 36dp?
  2. 为什么这个按钮的最小宽度(min-width)是 64dp?

怎么样?这些具体的 dp 值是怎么得到的,能解释清楚吗?至少我做不到。

之前作为 UI 的上游环节,我没有深入去思考、停留在了这些表面的标注上。现在真正自己去设计一套控件库时,才发现,如果只看这些表面的细节,再详细都不够,还应当更进一步去思考得到这些结果的「推导过程」,否则只是在依葫芦画瓢、不是真正的理解。
回想到曾经看过的一个印象深刻的观点「优秀的设计,每一个像素都是有据可循的」

那么,什么叫做「有据可循」?

  • 按我个人的理解,就是可以用准确的逻辑规则表述、而不是凭感觉
    (其实感觉也是一种基于经验的规则,又名「神经网络算法」,只是我们自己无法理解、表述)

什么又是准确的逻辑规则?

  • 代码!(伪代码也行)

顺着「用准确的代码解释每一个像素」这个思路,我又重新审视了一遍控件库。联想到之前做网站时扫过一眼的 scss(用变量定义 css 的一种语言),终于领悟到了「阶段四」:

控件的每一个属性都应该是有推导过程的「相对值」,而不是一个凭感觉直接得到具体结果的「绝对值」。

写成代码,可能会是这样:

  • 所有「色值」、「像素值」都用「变量」表示。当然了,这不是真实的例子,只是为了做简单的示例
    (关于「变量」的详细说明,请看下一节 Variables)
.button.color-normal {
  background-color: $color-normal;}
.button.color-blue {
  background-color: $color-blue;}
.button.color-red {
  background-color: $color-red;}

.button.size-small{
  height: $small;}
.button.size-middle{
  height: $middle;}
.button.size-large{
  height: $large;}

Variables(变量)

Variables,仅指 CSS 样式层面的变量。(这里我参考了 Blueprint 的命名,用了 Variables 一词,在其他很多设计系统(Design System)中,也都有类似概念。比如在 Saleforce Lightning 中,它被称之为 Tokens。)

等等,之前不是说「设计指南只到阶段三的层次」么?怎么突然很多设计系统中又有类似概念了?

还是因为代码。

  • 在成熟的 Design System 中,除了设计指南部分,通常还有完整的开源代码
  • 只是我之前看这些 Design System 时,未曾想到去看源代码部分

以我们自己的按钮举例,一层层拆解它的「变量」吧。(为了简化,暂不考虑按钮的 hover、down、disable、loading 等状态)。

先来看一下绝对值

这是我参照 Material Design 做的标注(单位都是 px)


尺寸标注

对着这个按钮,再来看一下这 2 个问题:

  1. 为什么按钮的高度是 24px?
  2. 为什么这个按钮的最小宽度(min-width)是 54px?

回答这 2 个问题之前,还需要先进一步拆解一下「按钮」

按钮是什么?
  • 在 Sketch 中,是「背景」上叠加「文字」
  • 在 HTML&CSS 中,是「背景容器」内塞入「文字」。

既然是要「用准确的代码解释每一个像素」,我们得用代码的逻辑来看。CSS 盒模型 了解一下?

盒模型简单的说就是:
按钮的尺寸 = 容器外边距(margin) + 容器边框(border) + 容器内边距(padding) + 内部文字的尺寸

我们这个按钮对应的实际盒模型如下:


CSS 盒模型
拆解问题一

参照上图的盒模型,可以先开始回答第一个问题

  1. 为什么按钮的高度是 24px?

解:

因为

  • 外边距为 0;
  • 边框为 1px,但有上下两条边,因此还要 ×2;
  • 垂直方向的内边距为 2px,同样有上下两个边距,也要 ×2;
  • 文字高度为 18px;

所以,将上述值相加即可得到

  • 0 + (1px * 2) + (2px * 2) + 18px = 24px

继续追问……

文字的高度为什么是 18px?

解:

因为

  • 使用了标准字号 12px
  • 文字行高是字号的 1.5 倍,这点应该属于 UI 设计的常识吧?

所以,可以得到

  • 12px * 1.5 = 18px

将标准字号命名为 $font-size-m,其对应的标准行高即为

  • $line-height-m = $font-size-m * 1.5

继续追问……

为什么 border 是 1px? 垂直 padding 又是 2px?

解:

这 2 个问题,如果只看按钮本身,确实很难再解释为什么了
但它们也不是一个孤立存在的绝对值,背后也是有特定含义的变量

1px 的 border,可以称之为「细边框」,即 $border-width-thin: 1px;
所有需要「细边框」的地方都用 $border-width-thin
类似的,2px 的 padding,可以称之为「超小间距」,即 $spacing-xs: 2px;
所有需要「超小间距」的情况都用 $spacing-xs

最终用「代码公式」来回答这问题,就是

24px = ($border-width-thin * 2) + ($spacing-xs * 2) + $line-height-m

拆解问题二

再来看一下问题二

为什么这个按钮的最小宽度(min-width)是 54px?

解题思路和「问题一」类似,这里只解释一下关键点:

因为我是按照最少 3 个汉字的宽度来设计的最小尺寸

  • 之前提到过,标准字号命名为 $font-size-m,是 12px
  • 换算一下,12px * 3 = 36px

另外水平方向的 padding 设了 8px,再加上两边各 1px 的边框

  • 36px + 8px * 2 + 1px * 2 = 54px

所以,最终用「代码公式」来回答这问题,就是

  • 54px = $font-size-m * 3 + ($spacing-xs * 2) + ($border-width-thin * 2)
「尺寸」总结

回顾一下几个涉及「尺寸」的属性,及其对应的变量值,在代码中如下:
(还有 border-radius 也是尺寸相关的属性,只是不影响宽/高计算,因此未曾提及。)

min-width: $font-size-m * 3; //最小宽度
padding: $spacing-xs $spacing-m; //内边距(前者为垂直方向、后者为水平方向)
font-size: $font-size-m; //字号
line-height: $line-height-m;  //行高
border-width: $border-width-thin; //边框粗细

至于 $spacing-xs、$spacing-m、$font-size-m、等等,这些「变量」的值具体应该设多少,这里暂不深入。本文的主要还想表达「所有值都用有据可循的变量表示」这种思路,至于每一个变量的具体值设定,那是另一个庞大的话题了。

以下链接是我自己的设定方式,如有兴趣可供参考
https://github.com/UXplayer/GS_Guideline/blob/master/_sass/variables.scss

回到「阶段四」的例子

还记得之前「阶段四」的示例代码吗?是类似这样不真实的、极其简单的例子

.button.size-small{
  height: $small;}

如果按照我们实际用的按钮来看,按钮「尺寸」维度应该如下:

  • 对于不同尺寸的按钮,其「最小宽度、内边距、字号、行高」都一同变化
.size-small{
    min-width: $font-size-s * 3;
    padding: 0 $spacing-s; 
    font-size: $font-size-s;
    line-height: $line-height-s;
}
.size-normal{
    min-width: $font-size-m * 3;
    padding: $spacing-xs $spacing-m;
    font-size: $font-size-m;
    line-height: $line-height-m;
}
.size-large{
    min-width: $font-size-l * 3;
    padding: $spacing-s $spacing-l;    
    font-size: $font-size-l;
    line-height: $line-height-l;
}
颜色

对于「颜色」维度,就是背景色、文字色、边框色,这些包含色值的属性。
在颜色的使用上,UI 设计师通常都还是会预定义好色板的。即便没有用精确的代码来解释,如果有用 Sketch 的 Symbol,也已足够保证统一性。此外,颜色在代码实现上,通常也还是会枚举出色板中的颜色作为「变量」,除非你是做开源库,需要有一些色板的生成算法,否则不会尺寸那样包含各种计算规则。
因此「颜色」部分就不再赘述了,思路和「尺寸」部分是一样的,甚至更简单一些。

总结

啰里啰嗦地写了那么多,其实主旨还是那一点:

控件的每一个属性都应该是有推导过程的「相对值」,而不是一个凭感觉直接得到具体结果的「绝对值」。

一个完整的控件库,要比按钮复杂的多,但万变不离其宗,无非是「推导过程」更长一些罢了。
像程序学习,用工程思维、逻辑思维去重新审视你自以为熟悉的 UI 设计,也许还会发现更多的新大陆。

最后

我们的项目是用 QT 开发的 PC 端产品,我在给开发提需求的同时,用 HTML&CSS 写了一部分控件,可以点此查看 在线演示项目 (做了一些调整,和实际项目并不完全相同)
该演示项目的源代码可以在 GitHub 查看。因为我这个只是简单的 Demo 演示,比起大型开源项目的源码,读起来应该容易不少。
我为什么要自己用代码写一遍?
首先,批量调整样式时,代码比 Sketch 快得多;
其次,便于和开发沟通(给开发直接看代码,要比用自然语言解释规则轻松多了)


参考资料
Palantir Blueprint
Saleforce Lightning
Ant Design

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容