人的五感中, 视觉获得的信息量是最大的. 在发明了文字之后, 书写和阅读变成为了信息传递最有效的方案. 现代的人几乎无时不刻在阅读, 有个笑话说上厕所的时候把旁边的洁厕剂的说明书读了30遍, 说明了人们对阅读有多么深的渴望.

对于传达信息的一方来说, 除了文字本身的内容, 文字展现的形式, 诸如字体, 颜色, 大小, 材质, 也会影响到接受者最终对信息的理解. 在这其中, 字体是一个很重要的因素, 因为在文字映入读者眼帘的那一刻, 字体本身所蕴含的意象, 就会在潜移默化中影响读者. 读者可以感受到强劲与温柔, 庄严与调皮, 复古或现代.

字体的获取

然而, 当我们打开系统自带的字体列表, 不免感到忧心忡忡, Windows自带的字体, 好像都不怎么好看. 而那些专业网站和杂志上的字体, 都是字体公司辛辛苦苦设计出来, 并且收费的.

难道就没有什么好看的免费字体吗? 天无绝人之路, google提供了一项免费的字体服务, 里面提供了600多种免费字体, 可以随意使用. 进入Google Font之后, 你可以根据不同条件搜索字体, 比如衬线, 无衬线. 然后Google会提供即时的预览. 此外, Google还提供这些额外的功能:

  1. 下载字体文件到本地
  2. 提供javascript代码, 让网站引用google提供的web font
  3. 选中字体, 跳转到typecast这个在线字体设计应用里进行进一步设计

免费字体推荐

There are over 600 typefaces in the Google web fonts directory. Many of them are awful. But there are also high-quality typefaces that deserve a closer look. Below are examples of these typefaces in action.

虽然有了免费字体库, 但是那么多字体, 一个个看怎么来得及呢? 好在有前人栽树, 请看这个网站: beautiful-web-type. 在这个网页设计中, 作者精选了十几种精美的网络字体, 配上文字和设计, 展现出这些字体的独特魅力.

基于他的列表, 我从无衬线(sans serif)和衬线(serif)两种西文字体大类中再精选两个, 推荐给大家.

无衬线 Sans Serif

无衬线字体, 是指那些笔画粗细一致的字体, 最最常见的字体就是Windows自带字体Arial了. 在数码时代, 因为无衬线字体的弯曲比较少, 线条比较分明, 适合作为大段文字的字体, 长时间阅读也不吃力. 同时, 作为标题使用, 也显得苍劲有力. 这里, 我要推荐的是Open SansLato这两种字体.

Open Sans

Open Sans字体比Arial字体在显示大段文字的效果上好很多, 因为它的字腔更大, 所以字与字之间的更宽, 上下的行距也比较宽. 比Arial宽敞多了.

下图是用typecast生成, 左为Arial, 右为Open Sans

Arial vs Open Sans

Lato

Lato也是一款无衬线字体, 它提供了非常丰富的字重的选择, 从细如发丝到非常粗重. 感觉很适合做各种标题

左为Lato-Regular, 右为Open Sans

Lato vs Open Sans

衬线

衬线字体(Serif), 主要是指带爪形装饰的西文字体. 最常见的就是Times New Roman这种字体了. 因为最初印刷行业都是使用衬线字体来印刷书籍的, 所以使用衬线字体会给人带来一种复古, 优雅的感觉. 但是在数码时代, 衬线字体因为过多的曲线, 不太适合字体大小较小的正文.

这里, 我要推荐的是CardoMerriweather

可以看到Cardo比较纤细, 营造出一种高端, 精致的感觉, 但是在电子屏幕上作为正文显示, 读起来比较吃力.

Merriweather更粗, 设计师还提供了Ultra Black字重, 作为标题显示显得非常掷地有声.

左为Merriweather, 右为Cardo

Merriweather vs Cardo

总结

以上总结了一下Google提供的免费字体, 欢迎大家尝试, 如果还有不错的选择, 也欢迎留下评论.

Web信息架构——设计大型网站(第3版)

这篇文章其实写于去年年初,现在搬到新的博客来。


我的工作是为企业的业务部门设计dashboard,dashboard是一种将数据高度浓缩再呈现的展示和分析方法。因此,我对信息架构这本书产生了兴趣。

《Web信息架构》的两位作者都是这个领域的专家。他们首先解释了信息架构是什么,对于网站来说,信息架构是在了解用户的信息需求后,设计的信息的展现和组织方式。有效的信息架构使得企业员工和网站用户能够更快的找到他们想要的信息,从而提高企业效率或者客户的满意度。

在介绍完信息架构的概况和重要性之后,作者开始说明信息架构体现在网站中的各个元素,主要分为组织系统,导航系统,搜索系统和labelling系统。前面三个系统对于现在的网民来说都不陌生,但是最后一个labelling因为翻译的原因,不太好理解。中文版把labelling翻译成了标签系统,我一开始还以为是标签云的标签(Tag),但其实,这里的labelling是对网站中每个文本元素命名的意思。如果没有规范的命名系统,网站的内容和标示,可能会缺乏统一性,让用户难以理解,为了解决这个问题,书中引入了一种叫做受控词表的方法,这个方法相当于建立了一套命名的主数据,一种命名的规范,当然,如何创建和维护受控词表也是一门学问。

接下来,本书介绍了进行信息架构的工作方法和流程。与一般软件开发工程一样,主要有研究用户需求,制定策略,设计,实施,维护,管理几个阶段。本书主要介绍了前三个阶段。作者认为,研究信息架构,主要从三个方面入手,分别是:情景,用户和内容。 情景,主要指客户所在的组织,他们的目标和流程等相关信息。用户,要研究用户对信息的需求和信息需求的行为,比如,员工想要在企业通讯录中寻找某个员工的联系电话,这是他的信息需求,他可能使用员工的姓名进行搜索,这是他的信息需求的行为。越是了解用户的需求和行为,越能设计出符合用户需求的信息架构。内容,主要指网站包含的各种内容,以及他们的所有权,格式,各种元数据。作者在书中为列举了研究这三个方面的不同方法,比如focus group,benchmark等等。

然后,作者进入了下一个阶段,制定策略,在这个阶段中,信息架构的工作者需要制定出整个信息架构项目的方针政策,例如信息架构的管理模式,使用的信息架构方法,还有建立信息架构时的侧重点等等。作者还专门介绍了这个阶段的流程方法:think,articulate,communicate,test。我觉得这四个词很有意义,我们在进行开发工作的时候,出了自己思考开干,更要去表达自己的理解和思考,让相关的人认可这些想法,或者快速的进行验证这些想法的可行性,而不是在最后验收阶段才发现重大的问题。

接下来,作者介绍了设计阶段的几种产出,有蓝图,线框图和受控词表。最后作者介绍了在实践中的一些要点,比如要对信息架构做推销,还有要考虑和组织的商业策略的关系,利用信息架构暴露出组织的业务策略的分歧点。

看完这本书,我除了了解了信息架构的概况之外,还学习到了一些新的方法论,比如受控词表和制定策略的流程。另外,我觉得这本书写的非常全面,提供了一种说明某一项工作的思维框架,首先说明工作的内容和重要性,然后介绍工作具体的内容,工作的流程,每一步的产出是什么,进入下一步的标志是什么。最后,作者表现出一种非常强的政治敏感性和大局观,除了做好信息架构的本职工作,了解客户(包括客户内部的政治环境),说服客户,也是很重要的工作。

2015年末尾的时候,我在搜索工程师文化的时候,找到这么一篇Open Culture Manifesto,在宣言的作者Martin的自我介绍里,有他自己整理的一些不错的软件开发的资源列表。这让我想起以前看到的一个数据可视化的博客的作者,也在自己的个人网站中列出了很多关于数据可视化的资源

我觉得这是一个很好的尝试,一个是对自己的已有的资源的整理,同时还能给其他人带来帮助,所以,最近我也整理了一份出来。可以直接点击站点右上角的Resources进行访问。

元旦早晨,我起床后准备烧水泡茶,手握住水壶的那一刻,觉得好冷,手都僵了。这个时候,突然想起这两天玩的游戏,放逐之城。

放逐之城(Banished)是一款短小精悍的模拟经营游戏。游戏开始,你带领一群被放逐之人来到一个山谷之中,然后从头开始,建造家园,繁衍后代,生存下去。游戏一开始有五个家庭,也就是十个成年人,十二个小孩,孩子到了十岁,就会长大成人,可以开始工作。游戏的主要目标就是生存,首先,人活着就会消耗食物。其次,到了冬天,还需要燃料来保持温暖。如果这两个主要的资源得不到补充,村民就会挨饿或者受冻,如果情况一直持续,就会死亡。

要获得食物和各种资源,玩家需要安排工人获得在地表的三种主要资源:木材,石料和铁来建造各种设施,然后指派成年人到设施中进行工作。 比如,游戏中获得食物的设施主要有以下几种:

  1. 采集类,狩猎小屋可以获得鹿肉,钓场可以捕鱼,采集小屋可以获得各种山珍野味。采集类的设施每个季度都有收获。但是需要确保周围的森林不受破坏。
  2. 农田和果园,需要有人耕种和维护,只有秋天才会收获,并且果园需要几年时间等果树长大后才会有收成
  3. 牧场。后期通过贸易所交易获得鸡牛羊等家禽后,可以开始建造牧场来畜牧,这些设施可以提供鸡蛋和肉类。

另外,通过建造新的住宅,适龄的青年就会住在一起组成新的家庭,然后就会有孩子出生。

虽然游戏的机制看上去不复杂,但是我第一次玩到50人口,就失败了。 原因有以下几点: 首先,过快的人口扩张,导致食物的需求量一下陡增,这个时候,需要建造新的设施来提供食物。 偏偏这个时候,村民工作使用的工具也宣告短缺,而工具的生产,需要铁,祸不单行,露天的铁矿已经被采空,依山而建的铁矿坑的产出速度太慢。于是一系列的连锁反应导致食物和柴火的短缺,最终第一个村子中村民纷纷饿死。

攻略

所以,以下是我的经验和教训

首先要了解整体的供求关系。

供给方面的数据其实比较容易获得,因为点击每个设施的数据面板,可以看到这个设施的当前产量和去年一整年的产量。大多数的食物设施在满员工作的情况下,每年的年产量是800。所以把所有的设施的去年产量加在一起,就可以估计出总体产量了。等到后期建造了市政厅之后,数据会更加快捷的获得,不需要手动的计算了。

需求方面的数据比较难获得,游戏中只提供了库存信息,而并没有消耗信息。我所采用的方法,是在游戏稳定下来的一个阶段,记录下来当时的库存量,一年之后的库存量,以及这中间的产出量,去年库存 + 去年总产量 - 今年库存就可以计算出人均消耗的资源数量,作为后期进行预测的重要指标。

通过这种方法,我的经验是每年需要准备150 * 人口数的食物产量才能保证食物的充裕。对于柴火,这个系数是16。相当于一个伐木场一年不间断工作的柴火产量可以满足50个人的需求

设施的布局

我的布局是以集市为中心的一个布局。集市是一个覆盖范围超大的设施,可以帮忙调配覆盖范围内的需求和产出的调配,这样,工人可以专注于自己的生产,而家中需要的食物和柴火可以由集市的小贩来负责运送。

以集市为中心,四方可以有两个方位建造一开始的采集中心,每个中心分别建造一个狩猎小屋,一个采集小屋和一个护林小屋,其中一个可以加上草药小屋。 另外两个方位,可以作为居民区重点发展,还有一个方位,一开始可以建造农田,后期可以按需求进行改变。

后期农田的产量比采集要高,可以把一个采集中心改成农业中心。

在初期进行建造的时候,在建筑工地旁边空出一些地方来设置料堆可以节约工人搬运材料的时间

人口的控制

人口的控制是游戏中最大的难点,首先,玩家的成就就是创建一个尽可能大的稳定的社会,但是,如果一下子让人口激增,就会进入资源短缺的陷阱。另外,如果忘记增加人口,也有可能产生人口萎缩的问题。 对此,我的做法有两条:

  1. 每个季度增加一个房子,不能同时增加多个房子,保证人口增长的稳定性,避免人口生育的高峰
  2. 检查每个新房子入住后,新人的年龄,如果新人的年龄偏大,说明需要增加更多的房子来增加人口,如果新人的年龄偏小,可以放缓房子的建造速度。 我设定的阈值是25,不过这个并没有什么根据。

此外,游戏中还有一个影响人口结构的重要因素就是学校,建立学校。 建立学校之后,孩子会推迟成年,而要先进入学校学习五年之后才能成年。 所以,学校的建立会巨大的改变人口结构,所以建立学校要慎重,最好能够保证所有的资源的富足之后,再进行建造。

资源的控制

一开始,地面上有很多的铁和石料,其实这个时候应该尽快组织工人把这些裸矿先开采完,这样不但有充裕的铁和石料,还能让护林小屋专心生产木材。后期,需要建造采石场和矿坑才能开采铁和石料,一定要所有准备,才能保证资衔接的顺利,否则会遇到建造设施却没有矿石或者工具的问题

感想

元旦里我总共打了三盘放逐之城,这个仅仅80M的游戏让我产生了很多思考。

起初我的指令基于直觉,但是50人口之后,就无法再维持一个稳定的社会了。 于是我意识到,需要系统性的理解游戏规则,于是我把游戏规则简化为供求关系。而第二步,就是要分析供给和需求的量分别是多少,这样,才能估计每一步操作会带来的影响。 具体的方法是,把每个季度的生产数据记录在纸上,汇总统计出总体消费量,从而求出人均的消费量,成为将来决策的关键指标。这里,我的教训是,想要为未来做打算,一定要对过去做总结,同时要抽取出关键的指标来简化管理。

第二,游戏达到150人口之后,我把所有能造的建筑都造了,以为万事大吉,开着十倍速什么都不做,结果过了几年,食物的存储量快速下跌,于是又发生了饥荒。我想,这可能就是很多统治者一开始是开明之君,等到晚年的时候,变的昏庸的原因,那就是因为懈怠。

最后,整个游戏虽然是欧洲中世纪的风格,但却更像远古时代的社会,所有的资源都是共享的,是一种原始共产主义。整个世界没有灾祸,没有饥荒,没有阶级斗争,没有罪犯,没有懒汉,但即便是这么一个桃花源般的理想环境,却也非常难于管理和维持,可见管理一个国家的难度有多高了。

上周末,我在可视化世界历史,主要使用了QlikSense和QlikSense的扩展。然而我遇到了一个问题,不得不深入了解QlikSense扩展的实现原理,从而修改这个插件,满足我的需求。这里记录一下目前对QlikSense扩展开发的认识。

问题

我的世界历史可视化作品,最早的文明是公元前3300年的古埃及,等到我输入到Excel之后,才发现,Excel只支持公元1900年之后的年份。于是我在数据源表格中只记录历史节点的年份信息,然后在QlikSense中使用创建日期的函数MakeDate(Year, 1, 1)来创建日期,没想到,QlikSense也只支持公元后的年份。于是,我被迫使用这么一种变通方法:将最早的公元前年份加上一个Offset,伪装成一个公元后的日期,等到在Dashboard中展示的时候,通过Dual函数,减去Offset,重新渲染成带公元前后的字符串。

这招对QlikSense自带的折线图是有效的,但是对我采用的Timeline扩展确没有起效,于是,我不得不分析QlikSense扩展的实现原理。这里主要参考的是@stefanwalther在github上的QlikSense扩展开发教程

QlikSense Extension开发的一些知识

@stefanwalther老师这个教程本身还处在连载状态,我学习到足够修改Timeline插件的知识之后,就暂时先放下了。这里记录一些学习到的知识,以备后用。

QlikSense Extension的核心

其实QlikSense扩展的核心有两部分,一个是.qext元数据文件,里面包含了扩展的名称,图标,说明等信息。还有一个是JavaScript脚本文件,这个才是重头戏,负责渲染图形,处理各种事件。

define( [ /* dependencies */ ],
	function ( /* returned dependencies as arguments */ ) {
		'use strict';
		return {

			// Paint resp.Rendering logic
			paint: function ( $element, layout ) {
				// Your rendering code goes here ...				
			}
		};
	} );

这个就是扩展脚本的整体框架,核心就是paint函数,里面有两个重要的参数

  • $element, 其实就是被渲染的HTML结构
  • layout, 包含了数据和属性信息

调试

  1. 可以在桌面版QlikSense里,按住Ctrl + Shift + 右键,点击Show DevTools
  2. 打开一个QlikSense应用之后,用自己的浏览器访问localhost:4848/hub,这样还可以使用自己熟悉的前端调试工具,比如FireBug
  3. 可以使用console.log('someMessage', someObject)来打印调试信息

从Qlik Engine中获得数据

接下来的问题是,扩展是怎么样从QlikSense中获得数据的呢?

数据可以通过layout.qHyperCube获得

  • layout.qHyperCube.qDimensionInfo, 包含了使用到的维度的信息
  • layout.qHyperCube.qMeasureInfo,包含了使用到的度量的信息
  • layout.qHyperCube.qDataPages,详细的数据

hc.qDimensionInfo[i].qFallbackTitle就是dimension的名字

hc.qDataPages[0].qMatrix可能没有全量的数据,可以随着图表的滚动,来获得新的数据。

Timeline扩展的分析

通过以上的知识,可以知道,QlikSense扩展的核心是paint函数,在Timeline脚本的284行,作者把qMatrix中的数据,转换成一个dataset,然后赋给vis.DataSet,就会调用vis这个第三方可视化库,创建最终的图形。

var dataItems = new vis.DataSet(dataSet);
var container = document.getElementById(containerId);
var timeline = new vis.Timeline(container);
timeline.setOptions(options);
if (useGroups) timeline.setGroups(groups);
timeline.setItems(dataItems);

构造这个DataSet,使用的是这些数据:

var dataItem = {
	id: e[0].qElemNumber,
	content: e[1].qText,
	start: dateFromQlikNumber(e[2].qNum)
};

所以,最终,只要修改dateFromQlikNumber,加上我的workaround,把QlikSense中得到的日期时间,减去Offset,就可以让公元前的日期显示为一个负数。