发布网友 发布时间:2022-04-20 09:01
共2个回答
懂视网 时间:2022-04-20 07:32
HTML5开源引擎lufylegend-1.7.1版的下载包内包含了lufylegend.ui-0.1.0.js文件,它是一个lufylegend.js引擎的专用UI组件,我在api的介绍里也说了,这个UI组件是专门为懒人准备的,包含按钮,单选框,多选框,组合框,滚动条等UI。
下面我来具体说一说这些UI的绘制过程,也方便大家更好的理解和扩展新的UI组件。
首先来看看LButtonSample1按钮的绘制。
在lufylegend.js引擎中可以利用LButton类来添加一个按钮,但是你需要传入按钮弹起和按钮按下的两个状态的可视对象,可以是LSprite,也可以是LBitmap,想要漂亮一点的按钮的朋友们可以使用一张漂亮的图片,一般做法如下
btn01 = new LButton(new LBitmap(new LBitmapData(imglist["replay_button_up"])), new LBitmap(new LBitmapData(imglist["replay_button_over"])));
当然,也可以使用LSprite对象的graphics属性绘制一个图形,一般做法如下
var up = new LSprite(); up.graphics.drawRect(1,"black",[0, 0, 100, 30],true,"#999999"); var txt = new LTextField(); txt.x = 10; txt.y = 5; txt.text = "测试按钮"; up.addChild(txt); var over = new LSprite(); over.graphics.drawRect(1,"black",[0, 0, 100, 30],true,"#cccccc"); var txt1 = new LTextField(); txt1.x = 10; txt1.y = 5; txt1.text = "测试按钮"; over.addChild(txt1); var btn = new LButton(up,over);
上面的代码只是绘制了两个不同颜色的矩形而已,当然不够美观,LButtonSample1对象就是在这种方法的基础上来制作的。
看LButtonSample1的构造器代码
function LButtonSample1(name,size,font,color){ var s = this; if(!size)size=16; if(!color)color = "white"; if(!font)font = "黑体"; s.backgroundCorl = "black"; var btn_up = new LSprite(); btn_up.shadow = new LSprite(); btn_up.back = new LSprite(); btn_up.addChild(btn_up.shadow); btn_up.addChild(btn_up.back); labelText = new LTextField(); labelText.color = color; labelText.font = font; labelText.size = size; labelText.x = size*0.5; labelText.y = size*0.5; labelText.text = name; btn_up.back.addChild(labelText); var shadow = new LDropShadowFilter(4,45,"#000000",10); btn_up.shadow.filters = [shadow]; var btn_down = new LSprite(); btn_down.x = btn_down.y = 1; labelText = new LTextField(); labelText.color = color; labelText.font = font; labelText.size = size; labelText.x = size*0.5; labelText.y = size*0.5; labelText.text = name; btn_down.addChild(labelText); base(s,LButton,[btn_up,btn_down]); s.width = labelText.getWidth() + size; s.height = 2.2*size; s.backgroundSet = null; btn_up.shadow.graphics.drawRoundRect(0,"#000000",[1,1,s.width-2,s.height-2,s.height*0.1],true,"#000000"); s.addEventListener(LEvent.ENTER_FRAME,s._onDraw); }
可以看到它继承自LButton,所以它有LButton的所有方法和属性,同时利用了btn_up和btn_down作为按钮的两个状态,传给了它的父类LButton。
btn_up作为按钮的弹起的状态,它包含了两个LSprite对象(shadow和back)和一个LTextField对象,shadow用来给按钮设置阴影效果,LTextField对象用来显示按钮文字。
按钮的绘制过程是在_onDraw函数中,如下。
LButtonSample1.prototype._onDraw = function(s){ if(s.backgroundSet == s.backgroundCorl)return; s.backgroundSet = s.backgroundCorl; var grd=LGlobal.canvas.createLinearGradient(0,s.y-s.height*0.5,0,s.y+s.height*2); grd.addColorStop(0,"white"); grd.addColorStop(1,s.backgroundCorl); var grd2=LGlobal.canvas.createLinearGradient(0,s.y-s.height,0,s.y+s.height*2); grd2.addColorStop(0,"white"); grd2.addColorStop(1,s.backgroundCorl); s.bitmap_up.back.graphics.clear(); s.bitmap_over.graphics.clear(); s.bitmap_up.back.graphics.drawRect(1,s.backgroundCorl,[0,0,s.width,s.height],true,grd); s.bitmap_up.back.graphics.drawRect(0,s.backgroundCorl,[1,s.height*0.5,s.width-2,s.height*0.5-1],true,grd2); s.bitmap_over.graphics.drawRect(1,s.backgroundCorl,[0,0,s.width,s.height],true,grd); s.bitmap_over.graphics.drawRect(0,s.backgroundCorl,[1,s.height*0.5,s.width-2,s.height*0.5-1],true,grd2); };
在_onDraw函数中,显示新建了两个渐变的颜色,然后分别在按钮的两个状态中绘制了两个普通的矩形,这样一个按钮就绘制成功了,使用方法如下。
var button02 = new LButtonSample1("测试按钮2"); button02.backgroundCorl = "#008800"; button02.x = 150; button02.y = 10; layer.addChild(button02);
效果
有了LButtonSample1,圆角矩形LButtonSample2就简单了,把绘制矩形部分换成圆角矩形就行了,但是构造器,我们也不需要再多写一遍了,直接让LButtonSample2继承LButtonSample1就可以了,如下
function LButtonSample2(name,size,font,color){ var s = this; base(s,LButtonSample1,[name,size,font,color]); }
然后就是_onDraw函数,如下。
LButtonSample2.prototype._onDraw = function(s){ if(s.backgroundSet == s.backgroundCorl)return; s.backgroundSet = s.backgroundCorl; var grd=LGlobal.canvas.createLinearGradient(0,s.y-s.height*0.5,0,s.y+s.height*2); grd.addColorStop(0,"white"); grd.addColorStop(1,s.backgroundCorl); var grd2=LGlobal.canvas.createLinearGradient(0,s.y-s.height,0,s.y+s.height*2); grd2.addColorStop(0,"white"); grd2.addColorStop(1,s.backgroundCorl); s.bitmap_up.back.graphics.clear(); s.bitmap_over.graphics.clear(); s.bitmap_up.back.graphics.drawRoundRect(1,s.backgroundCorl,[0,0,s.width,s.height,s.height*0.1],true,grd); s.bitmap_up.back.graphics.drawRoundRect(0,s.backgroundCorl,[1,s.height*0.5,s.width-2,s.height*0.5-1,s.height*0.1],true,grd2); s.bitmap_over.graphics.drawRoundRect(1,s.backgroundCorl,[0,0,s.width,s.height,s.height*0.1],true,grd); s.bitmap_over.graphics.drawRoundRect(0,s.backgroundCorl,[1,s.height*0.5,s.width-2,s.height*0.5-1,s.height*0.1],true,grd2); };
区别就在于drawRoundRect函数,它是绘制圆角矩形,使用方法如下。
var button04 = new LButtonSample2("测试按钮4"); button04.backgroundCorl = "blue"; button04.x = 10; button04.y = 70; layer.addChild(button04);
效果
LRadio是由一个或多个LRadioChild对象组成的。
function LRadioChild(value,layer,layerSelect){ var s = this; base(s,LSprite,[]); s.value = value; if(!layer){ layer = new LSprite(); layer.graphics.drawArc(2,"#000000",[0,0,10,0,2*Math.PI],true,"#D3D3D3"); } if(!layerSelect){ layerSelect = new LSprite(); layerSelect.graphics.drawArc(0,"#000000",[0,0,4,0,2*Math.PI],true,"#000000"); } s.layer = layer; s.layerSelect = layerSelect; s.addChild(s.layer); s.addChild(s.layerSelect); s.layerSelect.visible = false; s.checked = false; s.addEventListener(LMouseEvent.MOUSE_UP,s._onChange); } LRadioChild.prototype._onChange = function(e,s){ s.parent.setValue(s.value); }; LRadioChild.prototype.setChecked = function(v){ this.layerSelect.visible = this.checked = v; };
LRadioChild其实就是由两个重叠的可视对象layer和layerSelect组成的,通过setChecked设定layerSelect对象是否显示,从而改变它的外观,当点击LRadioChild对象时,调用它父级即上一级对象的setValue方法,再来看看LRadio代码。
function LRadio(){ base(this,LSprite,[]); } LRadio.prototype.setChildRadio = function(value,x,y,layer,layerSelect){ var s = this; var child = new LRadioChild(value,layer,layerSelect); child.x = x; child.y = y; s.addChild(child); }; LRadio.prototype.push = function(value){ this.addChild(value); }; LRadio.prototype.setValue = function(value){ var s=this,child,k; for(k in s.childList){ child = s.childList[k]; child.setChecked(false); if(child.value == value){ s.value = value; child.setChecked(true); } } };
通过setChildRadio或者push来添加子LRadioChild对象,然后当setValue函数被调用时,改变所有子LRadioChild对象的状态,将点击的子对象设为选中。
使用方法如下:
var radio = new LRadio(); radio.x = 50; radio.y = 130; radio.setChildRadio(1,0,0); radio.setChildRadio(2,50,0); radio.setChildRadio(3,100,0); layer.addChild(radio);
效果
多选框比较简单
function LCheckBox(layer,layerSelect){ var s = this; base(s,LSprite,[]); if(!layer){ layer = new LSprite(); layer.graphics.drawRect(2,"#000000",[0,0,20,20],true,"#D3D3D3"); } if(!layerSelect){ layerSelect = new LSprite(); layerSelect.graphics.drawLine(5,"#000000",[2,10,10,18]); layerSelect.graphics.drawLine(5,"#000000",[10,18,18,2]); } s.layer = layer; s.layerSelect = layerSelect; s.addChild(s.layer); s.addChild(s.layerSelect); s.layerSelect.visible = s.checked = false; s.addEventListener(LMouseEvent.MOUSE_UP,s._onChange); } LCheckBox.prototype._onChange = function(e,s){ s.checked = !s.checked; s.layerSelect.visible = s.checked; }; LCheckBox.prototype.setChecked = function(value){ s.checked = value; s.layerSelect.visible = s.checked; };
可以看到,它的原理和LRadioChild是一样的,同样通过两个重叠的可视对象来控制多选框的状态。
使用方法如下:
var check1 = new LCheckBox(); check1.x = 50; check1.y= 160; layer.addChild(check1); var check2 = new LCheckBox(); check2.x = 100; check2.y= 160; layer.addChild(check2);
效果
这个相对复杂一些,因为不想单选或多选框,只是点击改变状态而已,它需要根据点击动作不同,让它内部的列表上下滚动,先看构造器。
function LComboBox(size,color,font,layer,layerUp,layerDown){ var s = this; base(s,LSprite,[]); s.list = []; s.selectIndex = 0; s.value = null; s.selectWidth = 100; if(!size)size=16; if(!color)color = "black"; if(!font)font = "黑体"; s.size = size; s.color = color; s.font = font; s.refreshFlag = false; if(!layer){ s.refreshFlag = true; layer = new LSprite(); layerUp = new LSprite(); layerDown = new LSprite(); s.layer = layer; s.layerUp = layerUp; s.layerDown = layerDown; s.refresh(); } s.addChild(layer); s.addChild(layerUp); s.addChild(layerDown); s.layer = layer; s.layerUp = layerUp; s.layerDown = layerDown; s.runing = false; s.textLayer = new LSprite(); s.textLayer.x = 5; s.textLayer.y = s.size * 0.4; s.addChild(s.textLayer); s.layerUp.addEventListener(LMouseEvent.MOUSE_UP,s._onChangeUp); s.layerDown.addEventListener(LMouseEvent.MOUSE_UP,s._onChangeDown); }
layer就是组合框的样式了,而layerUp和layerDown分别是它的向上和向下的控制按钮,通过点击这两个按钮,分别调用_onChangeUp和_onChangeDown函数,另外组合框的内部列表会添加到textLayer层上。
看一下,setChild函数
LComboBox.prototype.setChild = function(child){ var s = this; if(!child || !child.value || !child.label)trace("the child must be an object like:{label:a,value:b}"); var text = new LTextField(); text.size = s.size; text.color = s.color; text.font = s.font; text.text = child.label; text.y = (s.size * 1.5 >>> 0) * s.list.length; s.textLayer.addChild(text); if(s.list.length == 0){ s.value = child.value; } s.list.push(child); s.selectWidth = 100; s.refresh(); };
这个函数为组合框的列表添加一个元素,使用LTextField对象来显示。
接着看_onChangeUp和_onChangeDown函数。
LComboBox.prototype._onChangeDown = function(e,b){ var s = b.parent; if(s.runing)return; if(s.selectIndex >= s.list.length - 1)return; s.runing = true; for(k in s.list){ s.textLayer.childList[k].visible = true; } s.selectIndex++; s.value = s.list[s.selectIndex].value; var mask = new LSprite(); mask.graphics.drawRect(2,"#000000",[0,0,s.selectWidth,s.size*2]); s.textLayer.mask = mask; var my = s.textLayer.y - (s.size * 1.5 >>> 0); var fun = function(layer){ var s = layer.parent; layer.mask = null; s.runing = false; s.refresh(); }; LTweenLite.to(s.textLayer,0.3, { y:my, onComplete:fun, ease:Strong.easeOut }); }; LComboBox.prototype._onChangeUp = function(e,b){ var s = b.parent; if(s.runing)return; if(s.selectIndex <= 0)return; s.runing = true; for(k in s.list){ s.textLayer.childList[k].visible = true; } s.selectIndex--; s.value = s.list[s.selectIndex].value; var mask = new LSprite(); mask.graphics.drawRect(2,"#000000",[0,0,s.selectWidth,s.size*2]); s.textLayer.mask = mask; var my = s.textLayer.y + (s.size * 1.5 >>> 0); var fun = function(layer){ var s = layer.parent; layer.mask = null; s.runing = false; s.refresh(); }; LTweenLite.to(s.textLayer,0.3, { y:my, onComplete:fun, ease:Strong.easeOut }); };
这两个函数,通过LTweenLite来让组合框的textLayer层进行向上或者向下的缓动。
无论是setChild,还是_onChangeUp和_onChangeDown,里面都调用了refresh函数,看一下这个函数吧
LComboBox.prototype.refresh = function(){ var s = this,k; for(k in s.list){ s.textLayer.childList[k].visible = false; if(s.value == s.list[k].value)s.textLayer.childList[k].visible = true; if(s.selectWidth < s.textLayer.childList[k].getWidth() + s.size){ s.selectWidth = s.textLayer.childList[k].getWidth() + s.size; } } s.layer.graphics.clear(); s.layerUp.graphics.clear(); s.layerDown.graphics.clear(); s.layer.graphics.drawRect(2,"#000000",[0,0,s.selectWidth,s.size*2],true,"#D3D3D3"); s.layerUp.x = s.selectWidth; s.layerUp.graphics.drawRect(2,"#000000",[0,0,s.size*2,s.size]); s.layerUp.graphics.drawVertices(2,"#000000",[[s.size*0.5*2,s.size*0.2],[s.size*0.2*2,s.size*0.8],[s.size*0.8*2,s.size*0.8]],true,"#000000"); s.layerDown.x = s.selectWidth; s.layerDown.y = s.size; s.layerDown.graphics.drawRect(2,"#000000",[0,0,s.size*2,s.size]); s.layerDown.graphics.drawVertices(2,"#000000",[[s.size*0.5*2,s.size*0.8],[s.size*0.2*2,s.size*0.2],[s.size*0.8*2,s.size*0.2]],true,"#000000"); };
可以看到,这个函数其实就是对组合框做了一个重绘,利用drawRect绘制矩形,利用drawVertices绘制三角形。
组合框的用法如下:
var com = new LComboBox(20); com.x = 50; com.y= 210; com.setChild({label:"测试一",value:"aaa"}); com.setChild({label:"测试二",value:"bbb"}); com.setChild({label:"测试三",value:"ccc"}); com.setChild({label:"测试四",value:"ddd"}); layer.addChild(com);
效果
最后是滚动条,这个实现起来就有点难度了,还好以前我用AS3写过一个滚动条,直接copy移植过来了,移植过程中,我再次感叹,lufylegend.js的语法模仿AS3还是比较成功的。
这个比较麻烦,所以我在这里只说一下它的用法,感兴趣的朋友可以看一下代码,自己了解一下。
看一下官方API介绍
LScrollbar(showObject,maskW,maskH,scrollWidth,wVisible) ■作用: 带有滚动条的可视对象。 ■参数: showObject:需要加入滚动条的对象。 maskW:滚动条对象的可视大小的宽。 maskH:滚动条对象的可视大小的高。 scrollWidth:滚动条的宽。 wVisible:是否显示横向滚动条,未设定则为默认。
具体用法如下:
var showObject = new LSprite(); showObject.graphics.drawRect(2,"#ff0000",[0,0,500,500],true,"#ff0000"); var t = new LTextField(); t.color = "#000000"; t.text = "あいうえおかきくけこさしすせそたちつてとあいうえおかきくけこさしすせそたちつてとあいうえおかきくけこさしすせそたちつてとあいうえおかきくけこさしすせそたちつてとあいうえおかきくけこさしすせそたちつてとあいうえおかきくけこさしすせそたちつてとあいうえおかきくけこさしすせそたちつてとあいうえおかきくけこさしすせそたちつてと"; t.width = 300; t.stroke = true; t.size = 30; t.setWordWrap(true,35); showObject.addChild(t); var sc = new LScrollbar(showObject,200,200); sc.x = 450; sc.y = 20; layer.addChild(sc);
效果
在下美工设计极差,所以本篇纯属抛砖引玉,大家可以试着自己写几组漂亮的UI,或者有什么好的意见或想法的,可以联系我。
HTML5开源游戏引擎lufylegend1.7.1发布贴
http://blog.csdn.net/lufy_legend/article/details/8780821
lufylegend.js引擎官网
http://lufylegend.com/lufylegend
lufylegend.js引擎在线API文档链接
http://lufylegend.com/lufylegend/api
热心网友 时间:2022-04-20 04:40
高质量的规范文档是一个优秀设计系统的代表物。我们详实地描述每个 UI 组件的设计与代码规范,来帮助设计师高效地作出决策,推动开发速度。编写高质量的文档需要前期规划和一系列合理的流程来辅助,付出的成本相当高。
这个系列由六篇文章组成,致力于描述编写组件规范文档的过程。本篇我会从目标读者、文档内容、文档结构开始。然后会涉及案例,设计与代码指南。这些内容来自于我自己这些年的实践经验以及社区里大家所分享的知识。
那么我以一个问题开始今天的主题:文档的目标读者是谁,他们需要什么样的内容,作为编写者我们该怎样组织文档结构来作出清晰的表达?
文档的目标读者
首先:你要弄清楚谁是你的文档的主要读者。
工程师,设计师,还有公司里的所有人!
当一个设计系统包含了代码指南,工程师们显然会是读者。那么一个只包含了代码指南的设计系统应该服务于设计师吗?如果文档里只包含了设计规范而没有代码(如 Material Design),工程师还是读者吗?
在我看来,两个问题的答案都是肯定的。规范文档是从不同的角度来服务于多种角色的。
除了设计与工程,它还服务于其他人吗?很有可能,特别是当文档所在的设计系统已经成为产品的基石时。简短有效的介绍对于 PM(产品经理) 很有价值,QA(测试) 则比较关注案例部分…等等。
规范文档是从不同的角度来服务于多种角色的
很多设计系统团队还会把自己的系统公开出来,在体现共享精神的同时也能起到吸引行业人才的作用。所以文档应该能够体现团队的专业与严谨。
文档的主要目标是:为设计师、工程师和团队里的其他角色服务,让他们能够高效地做决策。
Takeaway:设计系统的效应和影响力不只覆盖设计与工程,一个成长中的系统必将会服务于更多的角色。
工程师,接着是设计师,然后才是其他人
为所有角色服务并不意味着平等地服务所有角色。工程师每天会查阅 10 次或更多次文档,他们甚至会把文档与代码编辑器窗口并排排列!设计师的访问次数应该是少于工程师的,其他角色则会更少。
所以谁是最重要的?以我的经验来看,设计系统最初就是为了工程与设计之便,由工程师和设计师建立的。即使其他角色也对其有所贡献,但他们仍是偏次要的。因此我们首先需要确保工程师与设计师的需求能够得到满足。
设计师与工程师优先级最高
那么,工程师与设计师孰轻孰重呢?我最近参与设计的设计系统项目中都需要同时服务于两者,为设计和代码制作规范指南。我也在一些企业的文档中看到了对其中一方的过多偏见,或者是有将他们的目标完全分离开的倾向(稍后我会解释)。有很*度需要考量:设计系统的目标,他们的使用频率,内容深度、质量、生产成本,以及和他们日常工作的相关度。
设计师 vs 工程师
Takeaway:读者的优先级由很多因素决定。要有预期:工程师和设计师的需求会有冲突,并尽可能地优化和处理这些冲突。如果实在不行,就要偏向于距离最终产品最近的那一方,通常是工程师。这就意味着工程师优先,设计师其次。
文档内容
规范文档是连接读者与内容的媒介。内容会有不同的格式或模块,因此成本也各有差异,而你需要最终把它们编织在一起。
文档内容模块:简介和案例文档内容模块:设计参考和代码参考
抽象地来看,规范文档的内容通常包含以下四种模块:
介绍:组件的名称,以及一段简明扼要的介绍。(必要)
案例:这个组件的各种形式,状态,尺寸等等其他要素,比较好的做法是用代码直接把这些展示出来,而不是不可以交互的静态图片。(必要)
设计参考:比如什么时候应该用这个组件,允许的做法与不允许的做法,以及视觉、交互、文案方面的指南。(推荐)
代码参考:包含 API 和其他实施及部署方面的指南。(必要)
不同的模块会有不同的制作成本
「介绍」写起来当然非常的短平快。结构优秀的「案例」也是值得投入成本的,并且写起来会越来越顺手。工程师也需要一个合理清晰的「代码参考」。但是,真正有效的「设计参考」可能会非常耗费成本。
横轴:细节的丰富程度由浅到深。纵轴:制作成本由低到高
Takeaway:规范文档可以包含很多内容模块。所以需要团队在前期就进行充分的讨论,对每种内容模块做出符合自己团队和产品价值的判断,再投入成本去制作。
文档的信息结构
设计与代码:分开还是合并?
在实践中,设计师往往会自顾自的发布或更新和自己相关的内容,工程师也一样。这样的惯性行为会在无意中增加设计与工程的距离。所以大家需要在前期就对文档的信息结构达成共识。
谷歌的 Material 文档生态就是这种距离感的代表。 Material’s design foundation 优先服务于设计实践, 而 Material Design Lite,Polymer Project,Android Developer’s,Material UI (built for React) 都是服务于代码,和设计规范绑定的并不紧密。
这种分离的状态其实是有意义和理由的。因为 Material 是一个操作系统的底层系统,跨越了许多框架,团队,平台。它的复杂度在某种意义上超越了目前世界上所有的设计系统。但你要知道大多数的设计系统并不是服务于一个操作系统的,因此不会发展至如此复杂的形态。
对于像我们一样的产品团队来说,设计和代码分开是符合共识的。这种做法能够给分别为两种角色设计符合他们需求的体验。
组件设计规范与 API 和代码规范分别放在两个网站上。来自:Atlassian
这种做法也有风险。随着时间推移,两个网站可能出现不同步的现象:
设计与代码的分类逻辑出现差异(最简单的例子就是 Loader 和 Spinner 的命名:代码里叫 Loader,而设计里则叫 Spinner)
功能差异:设计规范里出现了代码不能实现的功能,或者代码里加入了设计里没有考虑的功能。
你可能会觉得这样也挺好,毕竟设计和代码本身就是两个领域。至少对于文档的写作者来说这种分离还是挺方便的(只用考虑自己的需求,管理自己的进度)。
但真正的读者需要的是一个「*的唯一来源(Single source of truth)」。如果你是一个对设计和代码都有需求的读者,你会发现自己不停在两个网站间切换,两个地方都有对你有价值的内容,这感觉就像是在打网球时陷入了拉锯战。
Takeaway:要慎重地看待设计与代码的分离。虽然一开始方便了内容作者和发布者,但之后会有风险。这种做法也可能会在潜移默化中造成设计与工程的距离扩大。
合并内容的两种方案:堆叠还是切换?
例如 Morningstar Design System 是把设计和代码放在一个页面里,读者就能找到完全统一的命名,指南,功能描述。
一个页面之堆叠式:把设计和代码放在一个页面中,纵向滚动来查看。
堆叠式的布局方式会使得页面变得冗长。当然还有一种方式是使用 Tab 来切换内容。
一个页面之切换时:把设计和代码放在一个页面中,通过 Tab 来切换内容。
Takeaway:将设计和代码混合在一起是有可能的,大家可以按自己的需求来选择以上两种布局方式。
按类型来为内容做排列和编组
不论选择那种布局方式,文档内容的模块结构和顺序应该是保持一致的:
简介
案例
设计参考
代码参考
其实只要把「案例」放到读者一进来就能看到的地方,把设计和代码参考放在一步点击就能达到的地方,就是一个不错的设计了。下面是几种行业内比较有代表性的模式:
左:IBM Carbon 模式 中:Hudl's Uniform System 模式 右:Lightning Design System 模式
IBM Carbon 认为代码更应该被优先展示,将交互用法和样式分别放在其他的 Tab 中。Hudl’s Uniform system 把顺序反了过来,设计优先于代码。 Salesforce’s Lightning Design System 把代码和组件案例放在 Tab 上方,默认选中开发者指南这个 Tab,而后两个 Tab 则被奇怪地留空了。
Takeaway:把简介和案例放在一开始最重要的位置,接下来的模块则没有唯一的方案,需要大家自己做出符合自己团队情况的判断。
若页面很长,则为读者提供定位导航
你的文档页面越长,越需要给读者清晰的认知,要让他们知道这个页面里会包含哪些内容以及当前所处的位置。纵向的定位导航栏是个不错的方案:一直固定存在于页面右侧,在滚动时同步追踪位置,并且可以包含子标题。
Morningstar Design System 在文档页面右侧设计了一个两级的定位导航栏
Takeaway:不论选择哪种形式,最重要的是在整个系统中保持逻辑一致,符合读者的预期与心理模型。
展示设计?展示代码?还是都展示?
把设计和代码融合,就会有读者只对其中一个方面感兴趣,他们会提出自己的意见:
设计负责人可能会问到:我能把这些代码案例和指南隐藏掉嘛?
工程师可能会问:我能把这些和设计规范有关的文字隐藏掉嘛?
可以考虑加一个选项或按钮来允许隐藏设计/代码内容。比如:
Design Only:把代码指南、代码片段和属性表等等都隐藏起来
Code Only:把视觉样式指南和文案指南都隐藏,但还是要把一部分交互用法指南保留着,这对工程师们也有用。