[译文]用CSS的box-decoration-break属性做出多行缩进
界面设计

Category Archives:译文

[译文]用CSS的box-decoration-break属性做出多行缩进


有时候做网页设计,针对inline型元素希望换行的时候能够将样式自动地应用到每一行上。那么CSS的box-decoration-break属性则可以帮倒忙

原文:callmenick.com

这篇文章介绍box-decoration-break属性。

笔者最近做项目,想要在设计上实现:

  1. 文本高亮,比如背景颜色高亮
  2. 高亮仅覆盖文本区域
  3. 文本每一行应该有左右内间距

比如,有一段文本:

<span>Hello, this is a long string of text that spills onto many lines</span>

笔者希望它是这样的:

笔者希望能够通过CSS简单优雅地解决这个问题。这样就必须将文本设置为display:inline

因为如果是block结果是这样的:

可是直接用inline属性,结果是这样的:

这时就要用到box-decoration-break这个属性了

MDN上对这个属性的解释是:

当一个盒元素被分成好几份时,box-decoration-break属性会将backgroundpaddingborder等等这些属性是如何作用的。

当一个行内元素断成几行时,这个属性让不同行之间保持了一致性。它的默认选项是slice,这个选项相当于行内元素没有发生分离。

笔者认为可以这样去理解这个属性:一个行内元素本身具有一个样式,被断开成很多行后,每一个行又成为了一个新的行。

但是,当应用了这个属性的另一个选项

box-decoration-break: clone;

后,所有的原有的样式都会被用在断开的各个元素上。也就是说,paddingborder这些样式会加在各个片段上,就好像他们是独立的元素一样。

笔者在codepen上面做了个例子,你也可试试看

这个属性目前浏览器支持良好

[译文]shader入门


几年前跟朋友一起开发Unity3d游戏,为了能达到很好的效果尝试过各种着色器,当时就总在想如果能自己改写shader就好了。最近研究WebGL,发现如果想要定制一些特殊的视觉效果,也离不开shader技术。shader的编写有一定门槛和难度。这篇文章翻译自html5rocks,虽然是很早之前的一篇文章,但是看了不少介绍shader的技术文章,还是感觉这篇讲的最易懂,是一篇章入门级的好文,不用很懂WebGLS,但需要有点Three.js基础。shader的正规翻译叫「着色器」,只是我感觉这么翻译总是不够达意,所以译文通篇对shader都不作翻译保留原词。下面是原文部分:

写在前面

笔者之前介绍过Three.js。如果没看过的话可以看看,这篇文章离不开Three.js。

Three.js把很多WebGL中的高难度问题抽象出来变成了一个简单易用的库。但我们有时候禁不住还是想做一些特殊的效果,看看能在屏幕上做出点什么更酷炫的东西。这就需要用到Shader了。

两种Shader

WebGL并非固定渲染管线,它采用的是一种精简的方式,我们没有太多现成的方法可以随意使用。然而它的管线是可编程的,这种实现方式门槛更高,但是也更强大。面对可编程管线,程序员需要负责从顶点到渲染的一切环节。Shader技术就是这条管线中的一个环节。

Shader分为两种:
1. 顶点vertex shader
2. 片段fragment shader

两种shader跟他们的名字其实没什么关系,它们都是运行在显卡的GPU上。现代GPU对shader所常用的功能做了大量优化,所以我们可以完全地把shader的功能交给GPU,CPU则负责其他的功能。

顶点shader

标准几何形体都是由顶点构成的。同时每一个顶点都会赋予一个顶点shader。顶点shader可以对顶点做很多事情,但它最重要的使命就是通过一个叫gl_Position的4D浮点向量来定义顶点最终在屏幕上的位置。这事实上是一个把3D位置投影成2D坐标的过程,其实现原理深究起来可以讲很多东西,但幸亏有Three.js我们可以忽略掉那些高难度操作直接设置gl_Position

片段shader

一个物体有了顶点,也能投射到2D屏幕上,那么它应该是什么颜色?材质和灯光是否会对它产生影响?这就是片段shader所关心的。

片段shader也有一项最关键的使命:通过一个叫gl_FragColor的4D浮点向量定义顶点颜色。那它为什么叫「片段」shader?因为三个点围成一个三角形,落在三角形中的像素都需要被绘制出来,所谓片段指的就是被三个顶点各自颜色所影响的三角形里的像素信息。片段shader是以插值的方式来获取颜色数据,比方说一个点是红色的,一个点是蓝色的,那么片段shader生成的颜色信息就是从红色到紫色再到蓝色。

shader变量

在shader中,你可以声明三种变量:UniformsAttributesVaryings。最开始听到这三个名字的时候,笔者其实有点懵,因为此前的工作笔者从来都没接触过这三个东西。笔者认为你可以这么去理解它们:

  1. Uniforms 变量是同时传给顶点shader和片段shader的,在一帧动画的渲染期间不会发生任何变化。比如说一个灯光的位置。
  2. Attributes 变量用于一个单独的点上,而且它只针对顶点shader。它可以让不同的点散发不同的颜色,与顶点是一种一对一关系。
  3. Varyings 变量是在顶点shader中定义的,如果类型和命名相同,它也可以和片段shader共享。这种变量的最典型例子就是一个顶点的法线,虽然是顶点shader的数据,片段shader也可以用它来计算亮度。

介绍完两种shader以及三种变量,我们可以试试看做一个最基本的shader。

Hello World

下面是一个简单的顶点shader:

/**
 * Multiply each vertex by the model-view matrix
 * and the projection matrix (both provided by
 * Three.js) to get a final vertex position
 */
void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}   

下面是一个简单的片段shader:

/**
 * Set the colour to a lovely pink.
 * Note that the color is a 4D Float
 * Vector, R,G,B and A and each part
 * runs from 0.0 to 1.0
 */
void main() {
    gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
}

就这些。

在顶点shader部分,我们通过Three.js传了两个uniforms变量。这两个变量一个叫模型-视图矩阵,一个叫投影矩阵,都是4D矩阵。你不用太深究它们的工作原理,当然能了解一下更好了。反正它们相乘在一起就可以把一个3D位置投射成2D位置。

Three.js已经把上面的两段小代码附加在它的shader中,你编写shader的时候就不用再去操心了。实际上Three.js默认附带的远不止这些,还有比如光照信息、顶点颜色以及顶点法线等。如果你不用Three.js的话就要自己写一大堆的uniforms和attributes,真的。

MeshShaderMaterial 材质

前面写好了shader,接下来如何在Three.js中使用呢?很简单:

/**
 * Assume we have jQuery to hand and pull out
 * from the DOM the two snippets of text for
 * each of our shaders
 */
var shaderMaterial = new THREE.MeshShaderMaterial({
    vertexShader:   $('vertexshader').text(),
    fragmentShader: $('fragmentshader').text()
});

通过这样的写法,Three.js就能把你写的shader赋给相应的面片材质。运行一下,会得到下面的结果:

http://oiducgj8q.bkt.clouddn.com/161221-shader-intro-01.png

实际上我们可以给MeshShaderMaterial再添加uniforms和attributes这两个属性。这两个属性的值可以是向量、整型或浮点。Uniforms在一帧中都保持不变,可以为一个单一数值。Attributes是给到每个顶点的,所以最好是数组。Attributes数组中的值与顶点数组中的值应该是一一对应的关系。

接下来

下面我们要花点时间做一个动画循环,这将会用到attributes、uniform两个类型,外加一个varying变量好让顶点shader可以给片段shader发送数据。之前的粉色球会变得像被从上面点亮了一样,侧面还有脉冲的感觉。通过这个例子你一定能对三种类型的变量使用有更深入的理解,尽管最终效果有那么点迷幻。

模拟灯光

我们改变一下着色方式,让它看起来不那么扁平。正常来讲我会打一盏Three.js中的灯,但是现在谈灯光还有点早,所以我会模拟一个灯光出来。

这回我们改进一下顶点shader,让每个顶点都能从片段shader获取一个法线值。我们通过Varying来实现:

// create a shared variable for the
// VS and FS containing the normal
varying vec3 vNormal;

void main() {

    // set the vNormal value with
    // the attribute value passed
    // in by Three.js
    vNormal = normal;

    gl_Position = projectionMatrix *
                  modelViewMatrix *
                  vec4(position,1.0);
}

在另一边的片段shader中,我们定义了一个相同的变量名vNormal,也就是顶点shader中的法向量。同时我们还准备了一个可以模拟光照的向量light,接下来我们让顶点法向量与光照模拟向量做点乘,计算的结果就近似模拟了一个平行光的照射效果。

// same name and type as VS
varying vec3 vNormal;

void main() {

    // calc the dot product and clamp
    // 0 -> 1 rather than -1 -> 1
    vec3 light = vec3(0.5,0.2,1.0);

    // ensure it's normalized
    light = normalize(light);

    // calculate the dot product of
    // the light to the vertex normal
    float dProd = max(0.0, dot(vNormal, light));

    // feed into our frag colour
    gl_FragColor = vec4(dProd, dProd, dProd, 1.0);

}

运行的结果是这样的:

null

之所以使用向量点乘,是由于点乘所得到的结果某种程度上就代表两个向量之间的「近似程度」。对于两个单位向量,如果它们方向一致,点乘结果就是1,如果方向相反,结果是-1。我们把刚刚的计算用在光照上,那么右上方的顶点的值约等于1,相当于完全照亮,另一侧的光照则近乎于0,背面则为-1。负数则转为0。

Attributes

接下来我们通过attribute给每个顶点一个随机的值,顶点将以该值沿着法线向外凸出。
下面是代码:

attribute float displacement;
varying vec3 vNormal;

void main() {

    vNormal = normal;

    // push the displacement into the three
    // slots of a 3D vector so it can be
    // used in operations with other 3D
    // vectors like positions and normals
    vec3 newPosition = position + 
                       normal * 
                       vec3(displacement);

    gl_Position = projectionMatrix *
                  modelViewMatrix *
                  vec4(newPosition,1.0);
}

运行一下发现似乎没有任何变化,这是因为attribute还没有在MeshShaderMaterial中创建,shader也就没管太多直接用一个0值代替了,有点像占位符的作用。下一步我会在Three.js中给MeshShaderMaterial添加attribute,Three.js会自动地帮我们把两者结合起来。

值得注意一下,我之所以把newPosition赋值给一个新建的vec3变量,是因为所有的attribute属性是只读的。

更新MeshShaderMaterial

attribute是与每个顶点一一对应的,所以我们对每个顶点都使用attribute。代码如下:

var attributes = {
    displacement: {
        type: 'f', // a float
        value: [] // an empty array
    }
};

// create the material and now
// include the attributes property
var shaderMaterial = new THREE.MeshShaderMaterial({
    attributes:     attributes,
    vertexShader:   $('#vertexshader').text(),
    fragmentShader: $('#fragmentshader').text()
});

// now populate the array of attributes
var vertices = sphere.geometry.vertices;
var values = attributes.displacement.value
for(var v = 0; v < vertices.length; v++) {
    values.push(Math.random() * 30);
}

运行一下,球体看起来是这样的:
null
虽然球看起来就像被削了一样难看,但好消息是所有这些顶点的置换变换都是在GPU上运行的。

添加动画

我们应该让它动起来,为此需要实现两点:

  1. 需要一个变化的uniform值来在每一帧改变物体的形变。我们可以用到sin或cos函数。
  2. 在JS中引入动画循环功能。

我们会在MeshShaderMaterial和顶点Shader中同时添加uniform。

首先是顶点shader:

uniform float amplitude;
attribute float displacement;
varying vec3 vNormal;

void main() {

    vNormal = normal;

    // multiply our displacement by the
    // amplitude. The amp will get animated
    // so we'll have animated displacement
    vec3 newPosition = position + 
                       normal * 
                       vec3(displacement *
                            amplitude);

    gl_Position = projectionMatrix *
                  modelViewMatrix *
                  vec4(newPosition,1.0);
}

然后是MeshShaderMaterial:

// add a uniform for the amplitude
var uniforms = {
    amplitude: {
        type: 'f', // a float
        value: 0
    }
};

// create the final material
var shaderMaterial = new THREE.MeshShaderMaterial({
    uniforms:       uniforms,
    attributes:     attributes,
    vertexShader:   $('#vertexshader').text(),
    fragmentShader: $('#fragmentshader').text()
});

完事后运行一下,画面是这样的:

null

由于amplituede的值为0,所以看不出有什么变化。这时我们在JS中用requestAnimationFrame来创建动画,uniform值的变化也是在这个方法中实现。

var frame = 0;
function update() {

    // update the amplitude based on
    // the frame value
    uniforms.amplitude.value = Math.sin(frame);
    frame += 0.1;

    renderer.render(scene, camera);

    // set up the next call
    requestAnimFrame(update);
}
requestAnimFrame(update);

最后

null

终于这个丑陋的小球开始像心脏一样跳动了。

这篇文章仅仅是个介绍,希望你能理解shader的原理,并做一些自己的酷炫shader出来!

[译文]设计可交互的信息图(上)

jpeg

Designing Interface》是一本非常好的界面设计理论书。书中将「设计模式」作为设计界面的重要方法。但是这本书的中文版翻译的并不好,好像不是界面设计的从业人员翻译的。在这个系列里,我会简单地翻一下这本书,一章一章地进行,从自己最感兴趣的章节开始。文字不会全翻,但一定会抓重点。

信息的可视化,能够让人们直观地从视觉上获取内容和数据,帮助观察者快速地推演出结论。它的形式涵盖可以有:
· Map :地图、位置图、对应图
· Flowcharts :流程图
· Barplots :条形图
· Diagrams :示意图
· Table : 表格
· Tree Views :树状图

有别于传统平面设计的是,交互中的信息图是可以允许用户主动去选择以何种方式来观察信息。任何一个静态的数据如果能够让人们与之互动,将会极大地降低其内容被人们理解的难度。
在交互式的信息图中,用户不再是个被动的观察者,而是一个参与者。设计交互式信息图需要首先了解两种用例,一方面,用户可能是在数据内容中找寻某个具体的信息点,这时我们需要提供如搜索、筛选这样的功能;另一方面,用户可能仅仅是想了解个大概,这时我们要能允许用户在全局和局部之间来回切换。

====接下来,介绍一下书中这一章所涉及的设计模式:====

模式1:略带细节的缩略图 / Overview plus detail


用一个缩略图来代表全局内容,并且在缩略图中用一个小框框来表示当前窗口所处的位置。

Photoshop中典型地使用了这一模式

百度指数也是同样运用了这个模式,他们在坐标图下面的时间轴上放置了一个缩略图,同事还可以让用户拖拽以调整观察的时间区间。

模式2:数据提示 / Datatips


当一个数据可视化的页面上存在大量数据并存,并且每个数据节点都包含一些细节信息时,我们可以考虑用「数据提示」来为用户提供更加具体的信息。具体体现在当用户鼠标经停在某个兴趣数据点上的时候,可以弹出一个小窗口来展示信息。我们的用户在浏览一些专业性的数据页面时,需要在整体和细节上来回切换,「数据提示」这一模式则很好地帮助了用户。

百度统计的管理者界面以及我使用的七牛云存储的管理界面都出现了这一模式的运用

模式3:数据高亮 / Data Spotlight


有些数据图表包含的信息量非常大,层层信息交叠在一起后,用户很难短时间内梳理数据间的关系,也很难聚焦在感兴趣的数据上深入观察。这时候,「数据高亮」这个模式就展示了它的作用。
当用户的鼠标经停在某一个感兴趣的数据点上面时,我们可以将这个点高亮,同时让其他数据变得隐晦一些,这样能够帮助用户更仔细地查看他想看到的。

这是一个叫做「旧金山犯罪地点」的网站,能够根据警方记录将整个旧金山的犯罪地点按照犯罪类型和时间予以显示,我猜想可能是为了给人们看到哪里更加容易出事吧。他们使用了这种模式,能够让你高亮地查看某一类具体的犯罪类型,从视觉上来说就很方便用户进行归类。

另一个例子是华盛顿邮报,他们的一个叫做「美国最高机密」的项目,也是用到了这个模式。这个半圆形的图表汇集了大量的信息,但是当鼠标停在某个具体部门上时,又能将其他的部门暂时遮盖一下,不至于让用户在浏览时太过眼花缭乱。

「数据提示」与「数据高亮」经常在一起使用,后者的特别在于还会同时遮住其他数据要素。
记得在使用这个模式的时候留给用户以及服务器一些响应时间,否则用户移动鼠标的瞬间就给出反馈会让体验很不好。

模式4:动态检索 / Dynamic Queries


用户有时候想从一大堆数据中去除掉无关的维度,想看看哪一组数据符合了他想要的标准,或是想了解几组特定数据间的直接对比,这时候他们不希望看到无关的数据干扰视线。这种用例,可以考虑「动态检索」这个模式。它能即时动态地筛选信息,让用户通过滑动条、复选框来进行数据选择。这个模式的好处显而易见,几乎所有的用户都会使用鼠标的点选或拖拽,本来属于数据库管理层面的增删改查等操作在UI层面变得非常简单。更重要的是,即时动态的反馈效果能够刺激用户一直保持查询的动作连贯,形成一种“心流”。

还是用之前提到的「旧金山犯罪地点」这个网站为例.它的左侧和下侧均提供了对数据查看的动态检索,用户每一次更改查询条件,都会即时地反馈在地图上

注意,在设计时不要忘记给用户一个「All」选项——时刻保持让用户能够回到所有数据中来。

模式5:数据提炼 / Data Brushing


当我们为用户提供了一组数据的不同显示方式时,比如同时给了坐标图和图表,则可以考虑为用户提供这种模式的交互。它允许用户在选择了数据中的某些兴趣点时,同时在各个图标上予以高亮显示。
这个模式的意义在于:有时候,在不同的语境下观察同一组数据,往往能够洞悉更多真相。

这个书里面的例子来自一个叫Weeplaces的网站,现在貌似打不开了。其大意就是在上方的地图和下方的时间轴中同时展示着某个人的签到信息。等于是一种时间和空间上的并联显示。

这个是百度地图的例子。点击左侧列表中的地点,会在右侧同时地显示位置,反过来也是一样。这种模式就是一种数据提炼DataBrushing(说实话我觉得翻成‘数据提炼’也不太达意,但实在不知道怎样可以更好滴表达)

模式6:局部放大 / Local Zooming


这个模式是为了给用户一种类似于“上帝视角”的方式,让用户可以从高到低地观察数据,既能纵观全局,又能对某一局部有所深入了解。
不同于普通的缩放,这个模式在放大观看数据的同时,还能保持对整体数据的观察。如果用在一个列表中,可以采用挤压其他数据项;如果用在地图中,可以采用放大镜的形式。

这个是书里面给出的例子,软件名字叫Inxight,反正我是没用过。图片是从谷歌上扒的。通过截图,我们可以看到它允许用户在整体数据中,局部地放大某一块数据来仔细查看。

这个是书里面的例子,网站名字叫「cartifact.com」,它们提供的地图服务中,可以用放大镜来显示某一区域的细节。

这个是笔者找到的例子,来自「彭博通讯社」的活动页面,这个活动是基于彭博的数据,给出一系列“数据之最”比如最有创新力的国家是哪里等等。我们可以看到,当你点击了具体的一个条目时,该条目被放大显示,同时其他条目都被挤压到上下方了。

(译文)像一个移动设计师那样思考


移动设计不仅仅是设计APP、或是移动网站,也不是设计途径或工具的使用方法,而是一种设计思维。移动设计需要你深入地理解移动,理解它的用户环境和使用场景。
原文地址:www.smashingmagazine.com/2015/04/10/thinking-like-ap-app-designer

移动应用设计有很多需要做的,不仅仅是吸引眼球。你需要深入理解设备,与设计web一点也不一样。

笔者从传统印刷进入设计行当,后来才了解交互设计和用户体验。

莫种程度上,我发现设计就像一个重复性的循环:找到一种万无一失的设计模式,用在每个项目上都能快速推进,但是反过来却发现设计的结果好像千篇一律。就好像最初代的iOS和安卓发布时候给人的感觉。

后来我找到了设计中的新领域:移动应用设计。

最近发布的Appwatch,一方面由于打开了一种设计的新可能性而让人兴奋,另一方面,对从未有过的交互以及用例确实让人有点恐惧。

市场不是没有过这种设备,但是新的技术必须有新的设计来配套。

当然,第一次这种大跃进发生在20年前,互联网。在那之前我们设计师还只是关注印刷产品。互联网让我们必须继续学习如何与媒介发生互动。用户不再只是个观察者。

转变是很艰难的,这也是为什么当时大多数的网站看上去像是按钮上的标志——设计师们还没有理解新媒介的特点。

今天发生在APP上的事情也是同样。很多应用看上去就像是把mini-sites搬到了手机里。然后,被比例、字体、点击事件、手势一类的东西给彻底搞垮。他们没有意识到是谁在用app,在什么地方以及用什么设备。

当设计移动时,跳脱web结构非常有必要。我们必须以一种不一样的心智来对待移动设计。

怎样才能成为移动设计师?

·改变工作方法
必须牢记:在你还在倒弄那些花哨的设计时,类似的app可能已经有几百款了。你根本没时间去在几周、几个月的时间里雕琢。

这就来了“精益用研”,一种快速小幅度的方法:持续不断地迭代设计和开发。记住:用户用过之前,什么都说了不算!

因此,app设计不能从PS或AI开始,应该从线框原型开始。这样,一旦变动来临,很快就可以修正。

从视觉开始设计是设计师的惯常。我们几乎已经习惯了。

最近一段时间,当我提出来设计师甚至不要从电脑开始工作而是从纸上画画开始时,很多人都感到吃惊。这样做的好处是防止我们一上来就思考什么文件尺寸、颜色字体等等,这些东西其实都在阻碍我们创意的发挥。

“移动优先”,或者说从最小的屏幕开始,是一个很好的策略。从小屏幕到大屏幕的思考更为容易。

刚开始这么做的时候,我是不习惯的。我建议你去试试看,你会发现你的优先级变了,你会突然之间明白对于移动设计来说什么才是最重要、不可或缺的。

你可以设想你是在旅行,只有一个最小号的手提箱,那么你会带些什么?肯定是最有用的东西!这不是跟智能手机上的移动设计一样吗。

从另一半身上学习

好的应用是设计师、程序员共同努力的结果。你需要变得更有效率。可以通过并行工作的模式来缩减迭代成本。
要去学会使用那些能很快地把你的设计演示出来的设计工具。
我刚到巴塞罗那,参与了一家小型初创科技公司。在那我发现我被一群程序员环绕着,都等着我交东西。
业余,我学习着使用Xcode以及编程语言,这些让我能够制作更加复杂的设计提案。
告诉你个窍门:尽量跟团队里的人使用同样的术语,这样大家会感觉用同样的语言在交流。

去使用不同的操作系统

作为设计师,千万别把自己固在iOS里。

每一种操作系统的交互模式都不一样。设计师必须快速地转换才能正确地同化设计。比方说:Tab的位置、怎样显示菜单、何时何种方式来显示一二级功能等等,在iPhone和Android上都不一样。

Instagram似乎没有好好做安卓设计,只是把IPhone的方案照搬了过来:Android上面Tabs应该在上面)

奋力做原型

敏捷开发的方法需要在实施前就能清楚理解产品的功能和长相。原型则能够帮助我们评估产品的可用性。理想状态下,我们应该快速地做出原型。

还记得Palm Pilot?它最早的原型中有一个就是用木头做的,然后把界面打印在纸上。雇员们则每天把它放在口袋里,好像它是一台真的设备一样。
原型除了能测试大小、重量、使用舒适度,更重要的是看一看你在脑子里想加的功能是不是真的必须,如果是,那就弄!

根据你的工作流程、你想要达到的目标、你最后期望的结果来选择最合适的工具。举个栗子,当我只有一些纸上的线框稿时,我会用POP这款应用。它只需找两个照片,就能快速地加上手势和过场。
但是当一个概念更加深入了以后,我觉得用一些更加具体化的设计软件更方便,比如Sketch,然后用MarvelFlintoInVision
这类工具做更有细节的原型。
另一方面你要了解,安卓目前正在它的Material Design中越来越多地引入“微交互( micro-interaction )”,在原型中设计出微交互是许多当下设计师的责任。

不要相信你的眼睛

从前给Web做设计时,你事可以看着你的设计最终一步步走向实现的。但对于APP,你需要在移动设备上测试它的功能和界面,这对于你选择合适的对比和大小是十分重要的。
IOS和安卓都有相应的工具来帮助设计师在不同分辨率的屏幕上检测效果。对于Ios,最有名的是LiveView。我常用的是Skala,它带一个桌面版,这样你可以在Mac上使用。你还可以试试Sketch Mirror,如果你用Sketch的话。
在我的工作习惯中,我总是在手机上预览一下设计效果,然后才开始下一步。我在一段时间里只设计一个操作系统上的界面,并且始终把手机保持离自己很近。当第一个线框稿好了以后,我就能够在手机上确定图形、字体以及点击区域的大小、比例、颜色是否合适。如果你不在第一时间去了解这些,后面改起来是非常麻烦的事情。
还有,如果你在给不同的屏幕尺寸做设计,那么需记住像素不再是最好的度量方式,因为它在不同的OS上有不同的表现。iOS上的points和安卓上的DIP就是这么回事。

以谦卑之心做设计

不要觉得你在创造什么终极解决方案之类的。APP永远不会完结,它需要不断地进化。正因如此,我们不能以绝对主观的立场来应对界面设计。真相只有从可用性测试里用户遇到的问题破译出来。

(译文)设计师应了解的关于DIP的那点事


这篇文章翻译自Google Chrome的设计师 Sebastien Gabriel,目的是为了给设计师解答诸如DIP、DPI这类关系不大但容易混淆的概念。原文穿越:sebastien-gabriel.com/designers-guide-to-dpi/home,下面是文章正文————

什么是dpi和ppi

DPI = Dots Per Inch,最开始用在打印上,指的是在一英寸的纸上打印多少滴墨水。DPI越小打印的质量就越差。

这个概念后来被迁移到了电脑屏幕上,只不过变成了PPI,也就是Pixels Per Inch,原理是一样的:你的屏幕一英寸能显示多少像素。

Windows电脑的起初默认PPI是96,Mac电脑是72,不过80年代之后这个数字就不太一样了。一般的非高清屏的桌面(包括Mac)PPI都在72到120之间。在72到120之间做设计能够保证你的作品在任何地方的比例都相差不大。

举个栗子(见下图):
一个MacCinema27寸显示屏的PPI是109,也就是每英寸屏幕上能显示109个像素。它的显示屏宽度大概在23.5英寸,23.5*109约等于2560,这就是它分辨率2560*1440中的宽。

对你的设计所造成的影响

假定你在上文提到的屏幕上设计一个109*109的蓝色方块,这个方块在屏幕上会有一个1*1英寸的物理尺寸。但如果用户的屏幕PPI是72,蓝色方块的实际大小就会变的大些。下面是一个模拟效果。

屏幕分辨率与原生分辨率

屏幕分辨率决定了你的设计是怎样呈献给用户的。好在目前LCD显示器已经替代了CRT显示器,用户们普遍能够得到一个较好的PPI。

分辨率定义了一个屏幕能够显示的像素,一个2560*1440的显示,2560是宽,1440是高。当然你明白这并不是实际的物理尺寸。

今天的LCD显示器都有一个预定义的分辨率,或者说原生分辨率,这个分辨率就是这个屏幕能显示的像素数。拿27寸的Cinema显示器来说,它的PPI是109,原生的分辨率是2560*1440。如果你降低它的分辨率,显示的东西看上去就会变大。

 

之所以东西变大了是因为,屏幕原本是2560*1440的,这时分辨率下降了,但是一个一个的像素们还是在那,而且以一英寸109个像素的方式显示。系统为了填补这个空白会做什么?当然就是把所有的东西都拉伸。GPU或CPU会根据新的比例重新计算每个像素应该显示什么。

如果你想让27寸显示屏的分辨率变成1280*720,也就是原来的分辨率减半,GPU就不得不模拟出一个是原来两倍大小的像素来填充屏幕。由于比例正好是二等分,所以看上去还不会太模糊,但是如果变化的比例是1/3或3/4呢?看下图:

左边的窗口是在OSX的默认分辨率1440*900下渲染的,右侧是将屏幕分辨率改成了1024*640后同样的窗口

来看另一个例子。左侧是一条1px的直线在原生屏幕上,现在把分辨率调小50%。为了填充屏幕CPU会把包括这条线在内的一切都变大150%,也就是乘以1.5。由于没有半个像素,系统能做的就是把颜色均分,然后填充在图形的周围像素上,这就造成了模糊。

这就是为啥你在MackbookPro retina上更改分辨率的时候,它会用“looks like”这样的口吻。

什么是4k

你最近很可能听了很多4k的说法(至少在我写这篇文章,2014年初)。想明白这个,你要先了解HD。

事先声明,这里的内容都是超简化了的。我只想谈谈最通常的分辨率。

HD这个词适用于比1280*720大的一切分辨率,或者说720p以上。

full HD指的是1920*1080.大多数的电视用的是这个分辨率,越来越多的高尖手机也是这样,比如Galaxy S4,HTC one,Sony Xperia Z,Nexus 5等。

4k则是从3840*2160起。也有人管他叫Quad HD或Ultra HD。4k的分辨率是1080p的四倍。还有另外一种4k,分辨率是4096*2160,通常用在投影仪上。

把4k显示器连在电脑上是什么样?

现有的系统是无法延展成4k的,如果把4k显示器连接在在chromebook或macbook上,它将会用常规的比率配合最高分辨率的组件(如200%或@2x)来显示,好让所有的东西看起来比较小。举个例子:你把一个12寸的4k屏连在一个12寸的高清屏电脑上,所有的东西看起来都是缩小了两倍。

下面的例子就是

retina显示

iPhone4发布的时候苹果用到了“retina”这个词,之所以这么叫是因为设备的PPI太高,以至于肉眼的视网膜无法区分屏幕上的像素。

这个解释对当时的设备来说没问题,但是随着屏幕变得越来越好,我们的眼球现在已经足够观察那些细致的像素,尤其是那些圆角处理过的UI元素。

技术上讲,他们只不过是在同样的物理屏幕上多显示了两倍的像素。

iPhone 3G/S是3.5英寸,分辨率480*320,163PPI

iPhone 4/S是3.5英寸,960*640,326PPI

没错,非常精确的两倍。屏幕上的元素看起来都清晰了两倍因为同样的物理尺寸里像素正好多了4倍。

下面这个图就是设计APP在不同环境时的情况:

苹果已经用了“Retina”这个词,所以其他公司只能用“HI-DPI”或其他。

倍数

倍数就是你在给不同PPI设备做设计时的数学救世主。从此以后你不用在管那些不同设备的技术规格之类的。

让我们拿iPhone3G和4举栗子。屏幕上同样的物理尺寸,一下子多了两倍的像素。所以倍数就是2。也就是说只要把各种组件的尺寸都乘以2,就能适配4G分辨率了。

假设我们做了个44*44的按钮(这也是iOS的推荐触摸尺寸),我们叫它“Jim”。

要让Jim在iPhone4上看上去正合适,只需要把它设计的两倍大小就可以。如下图

只需要一张Jim.png和一张Jim@2x.png就OK了

这时候你可能会问“那倍数除了2还应该有其他的吧?”答案是确实有,而且它是一个十足的噩梦。我之所以这么说,是因为倍数太特么的多了。

我们先说单位(units),对于多DPI设计来说,“单位”比“像素”更重要。这就要引入“DP”和“PT”。

什么是DP、PT和SP

DP和PT是用来规范不同DPI设备的。

DP也叫DiP,全称是Device independent Pixel(设备独立像素),PT代表Point(点)。PT是给苹果用的,DP是给安卓用的,其实本质上一样。

它们都是在定义尺寸,但跟设备倍数(multiplier)无关。它们极大地帮助了不同工种比如设计师和工程师之间的沟通。

拿我们之前的小按钮Jim举栗子。Jim在普通的非视网膜屏幕上是44像素宽,视网膜屏幕上是88像素宽。这回我们给它加上一个20像素的边距因为它想有点自己的空间。这个边距在retina上应该是40像素,可是40像素在非retina上是不行的。

那该咋办?这样:我们就拿非retina上的100%作为一切的基准。

这回,我们让Jim的尺寸是44*44DP(PT),边距是20DP(PT)。 不论在什么PPI的设备上,Jim就是44*44DP不变了。

Android和iOS系统自己将会为这个尺寸做适配,把它转化成正确的倍数。

SP是从DP何PT分离出的一个概念。它的全称是Scale-independent pixels,专门用来定义字体大小。SP受安卓设备的字体设置的影响。定义SP和定义DP差不多。16SP对于阅读来说就非常不错。

PPI配置

讲了PPI、视网膜、倍数,现在来聊聊一个重要的话题:“如果我在设计工具中修改了PPI配置会怎样?”。

这么说吧:只要不关系到打印,PPI设置就没啥用。

PPI设置是留给印刷用的。如果你仅仅是给web设计,对你位图的尺寸没有任何影响。

下面是一个例子。我打开PS,调成72PPI,画一个80*80像素的方块,和一段16磅的文字。第二个是用同样的尺寸,只不过是144PPI。


结果就是,文字变大了两倍,方块没变。原因是PS或其他设计工具会根据PPI的不同拉伸跟pt有关的元素,导致了文字变成了两倍大小,但是用pixel定义的元素,PS不会管它。一个像素就是一个像素,不管你PPI是多少。只不过不同PPI的屏幕显示像素不同。

但有一点你要记住,如果你的设计源文件里用了不同PPI,在文件之间相互嵌入时会转换大小,这个肯定让你头疼的。

解决办法?一直用72PPI吧,PS的默认。

iOS上的PPI

接下来聊聊平台设计规范。

从屏幕尺寸和DPI的角度来说,iOS有两种移动设备、两种笔记本/桌面屏幕。

在移动端,有iPhone和iPad。

在手机的范畴,老的3GS是非retina的。iPhone5以上用的是更高的屏幕,但是和IPhone4、4s的DPI是一样的。

2014苹果秋季发布会上,iPhone又多了两个成员:6 和6Plus。

iPhone6比5大了0.7寸,但PPI没变。iPhone6 Plus却引入了iOS的一个新的倍数:@3x。

理解iPhone6 Plus与其他iPhone设备在显示上的区别时有一个很特殊的地方:它转换了视觉上的采样。

给iPhone6做设计的时候,你是子在一个1334*750像素的画布上设计,然后手机就渲染出1334*750像素的界面。但是iPhone6 Plus的分辨率比它渲染的图像的要小,因此你需要在2208*1242的分辨率下进行设计,然后6Plus会把它降成1920*1080像素。下面是图示:

实际尺寸比渲染尺寸小15%,会产生一些小瑕疵,比如原本很精确的细节,却多出了半个像素。不过分辨率实在是太高,除非你离得很近,否则看不出来的。下面是一个图示:

感谢Paintcode在发布会后提供了非常好的解读。这里是他们做的说明页面。

下面是关于iPad的图示:

在这里,笔者建议从基础的PPI比如100%/1x开始做设计,然后乘以2,并在@2x的屏幕上检验你的设计。下面的案例是在iOS上用的Chrome需要的组件说明:

你会发现每个组件需要两个图片,一个叫name.png,一个叫name@2x.png。这是个iOS的习惯,应该遵守。

Android上的PPI

Android平台的设备比iOS要多的多。结果导致你要面对一系列的屏幕尺寸和DPI,包括那些大得像平板一样的手机以及小得像手机一样的平板。

接下来跟iOS不太一样了。我们先来讨论倍数和DPI。

我们有手机和平板两个设备范畴,它们都有一系列的DPI:Ldpi,mdpi,tvdpi,hdpi,xhdpi等等。

不过还好,常用的只是一小部分,有些甚至都已经废弃了。

第一件事,我们找一个跟iOS的1x一样的基准单位。Android上就是MDPI,见下图:


很多?还没完呢:

有五种有效的DPI正在被使用:MDPI,HDPI,XHDPI,XXHDPI和XXXHDPI。

下面让我们来归归类:







以Chrome为例来说说设计时需要的组件:

每个组件你需要提供4张图片,从MDPI到XXHDPI。

就像讨论iOS时,我建议你用100%或1x的倍数作为设计基准,这能让你再准备不同尺寸的组件时轻松一些,尤其是遇到Android上1.33和1.5这类的倍数。

如你所见,图片是32*32dp,而有的Android倍数是小数,比如当你乘以1.33时尺寸是42.56,这种情况下我们代之以43像素。

可拉伸组件

不论是给桌面还是移动做设计,你都始终需要可延展的组件。

可延展的组件,就是用代码来把图片放大到你想要的大小,同时不失真。这跟重复地平铺组件不同,尽管最后看上去差不多但是其内部的实现机制是不一样的。

以下面的Chrome为例,iOS的工具栏是用一根极细的条重复平铺在屏幕上。不过这种方式已经相对罕见。

对设计师来说,iOS上延展组件相对容易一些,因为代码里已经定义好了这类变换。需要做的就是提供一张基准图片,见下图:

Android的方式就不一样了。

对于这个平台,你要使用9宫格系统。9宫格包含4条线,

9宫格定义了两个事:伸缩的区域以及填充的区域。一旦这些都定义好,代码就只会去拉伸你定义的部分。下面是图示:

如你所见,9宫格是那个由4个黑条构成的组合。这些黑条的宽度应该是1像素;可延伸的区域不包含圆角。中间是填充区域。

使用9宫格需要在名字前加上.9,就和在iOS上加@2x一样。

上面的图片里,我为了说明白这个事就把9宫格放大了,在实际的应用里,你要优化它,只保留它需要的地方,同时把其他拉伸的部位降到最小。

触控以及触控区域

当你着手创建一个UI组件的时候,非常需要先理解一下触控和DPI之间的关系。

桌面-非触控

早先的计算机技术并不具有触控这样的概念。我们使用鼠标和键盘来精确地操作UI。鼠标的精确等级是1pt,理论上你创造一个1*1pt的按钮,人就可以点击。就像下面这张图:

这是20倍大小的ChromeOS光标。红点就是可点击的区域。你能用手精确地点一下吗?

手指尺寸

下面是两个最常用的手指尺寸,食指和拇指,以及他们占用的空间。实际的区域当然会更小一些也更精确一些。

设计触控操作的时候,最好把手指的尺寸估算地大一些。

不同平台上推荐使用的触控区域

尽管这样列出比较方便,但在应对设计工作时,还是要谨慎一些,并且多多自行试验。

苹果牢牢地控制着自己的硬件设备,他们明白触屏的质量,所以他们可以规定一个相对小一些的触控对象,而且他们的屏幕也相对小一些。

安卓和Windows,由于大量不同的OEM厂商有不同的硬件,设计时使用大一些的触控对象会更“安全”一些,留白也更大一些。

文章归档