UI设计规范说明书

发布网友 发布时间: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组件。

1,矩形按钮LButtonSample1

首先来看看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);

效果

2,圆角矩形按钮LButtonSample2

有了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);

效果

3,单选框LRadio

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);

效果




4,多选框LCheckBox

多选框比较简单

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);

效果



5,组合框LComboBox

这个相对复杂一些,因为不想单选或多选框,只是点击改变状态而已,它需要根据点击动作不同,让它内部的列表上下滚动,先看构造器。

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);

效果


6,滚动条LScrollbar

最后是滚动条,这个实现起来就有点难度了,还好以前我用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:读者的优先级由很多因素决定。要有预期:工程师和设计师的需求会有冲突,并尽可能地优化和处理这些冲突。如果实在不行,就要偏向于距离最终产品最近的那一方,通常是工程师。这就意味着工程师优先,设计师其次。



文档内容

规范文档是连接读者与内容的媒介。内容会有不同的格式或模块,因此成本也各有差异,而你需要最终把它们编织在一起。


文档内容模块:简介和案例文档内容模块:设计参考和代码参考


抽象地来看,规范文档的内容通常包含以下四种模块:

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com