九月 24th, 2009

视觉色彩习惯

5 Comments, 用户体验设计, by army8735.

“订阅按钮在哪里?”

这是昨天域名备好案之后,我和最终妄想-零聊天并告知他个人技术博客开通时,他问的第一句话。

仔细看站点底部,有着“Theme based on iStudio Designed by Xu.hel.”的字样。这套主题是下载Xu.hel的iStudio作品,当然在其基础上大肆修改了一番,比如宽度调到960,色调变成蓝色等等等等,几乎是面目全非了。且不说这些,iStudio主题还是蛮清馨简约的,不得不称赞作者的设计功力。然而文章开头的那句提问,却引发了对话者之间一个细节的讨论。

“我一般就找橙色图标来订阅。当你脑里想着‘橙色’的时候,都无视一切灰色物体了。”

04的这句话绝对可以当成金科玉律!的确,提到RSS订阅,人们第一印象就是想起那个橙色的、圆角矩形的图案来,于是乎每个人眼里第一目标就会去寻找它。当然,不乏有创意十足的设计师们设计出其它个性的图标,但他们也是在一定的原型基础上发挥天马行空的想象:譬如矩形变成圆形,颜色改成近色。很少有脱离原本模样完全让你完全认不出它是RSS的设计,除非制作得特别醒目,能引起人的好奇心把鼠标移上去看看是什么东西。而且这样做需冒一定的风险。google搜索rss图片,便能看到许多特别的的创意。

habit

可以看到,以前的RSS订阅图片是灰色的,鼠标移上去才会显示原本的橙色。现在已经改为默认橙色,鼠标移上去变为橙红色。

九月 22nd, 2009

Hello world!

1 Comment, 其它, by army8735.

第一篇系统默认博客,算了不删了,留个纪念吧。 ^_^

日期当然就是开通的时间,这里就用作专门的技术博客,生活博客则使用Qzone,链接见链接框。

紧接上篇,正太米想要八抬大轿明媒正娶回大家闺秀——Canon IXUS 95 IS,得先准备好聘礼。凑巧今日同住的一只loli(同学的GF,非闪光弹)要过生日,早先曾说过把淘宝上某个商店的泰迪熊送给她,可是却没有银行卡给支付宝付款。今日试了下拉卡拉,很是便捷,只是要付手续费……

充值之后,正太米兴奋地要登录淘宝,输入密码时突然发现页面提示大写锁定键打开了!幸好咱及时更正,要不然又要白输一遍了。记得以前英俊(EyesOnline)曾说过我们能不能也弄一个这样的提示功能,来提升用户体验?俺回答说淘宝人家是装了本地插件才能检测CapsLock,一般页面可没有办法。

时过境迁,如今正太米功力大增,思路扩展也今非昔比,于是乎一款网页版大写锁定键提示功能的想法跃然纸上……

为什么一般页面很难做到?

想要拥有大写锁定键提示功能,很显然,假如安装了本地插件那么键盘相应的状态自然很容易就侦测到了,做出提示功能水到渠成。而想基于网页的话,js还没有足够强大的能力来应付,只能做到一点点:在密码框按键或输入时侦测按键代码。凭此想要弄出相同功能的大写锁定键提示的话,明显是不可能的,只有另辟蹊径了。

capslock1

跳出局限思维

既然js不能胜任,那么flash呢?目前网页上可用的技术有很多,但普遍的无非这两种:js和as。as是有能力检测大写锁定键的(flash.ui.Keyboard类),那么我们便可借助它来完成。

不过这里有个难题,那就是flash player想要检测按键,必须在激活的状态下才行。也就是说密码输入框需要做成flash,这可是我们不愿意看到的,那么有其它的办法吗?

解决之道

问题的回答是:当然有!结合js和as来做的话,这一问题便迎刃而解!可是有人又要问了:假如用户没有安装flash player 9及以上的怎么办,或者版本太低怎么办(即使fp9的普及率已经相当高)?不要急,综合考虑的结果是为安装flash player 9及以上版本的人提供全面的大写锁定键检测功能,没有安装的提供部分检测功能,如此便完善了很多。>

详细思路
首先用户分为2种:安装flash player 9及以上版本的和没有安装的。

假如安装了的用户在密码输入框输入密码时,onfocus和每个按键都会被侦测,一旦按键的keyCode是20(CapsLock的键值),那么便会通过ExternalInterface调用as3的接口,判断Keyboard.capsLock的值true或false,再回调js函数来显示或隐藏提示框。当然做这一切之前需要将焦点移至flash上,做完之后焦点移回密码框。

假如没有安装的用户只能部分体验到它的功能。依旧我们会侦听按键的onkeydown和onkeypress,两者区别是前一个包括功能键,后一个只侦测输入。并且我们设置了一个mode变量以区分大写锁定键为打开、关闭以及未知的情况。这样在已知mode的情况下,按下CapsLock键就能知晓其是否被打开了;而未知mode的情况下就需判断输入的字母的大小写并且是否同时按下了shift键来判断是否给予大写锁定键提示。

/wp-content/web/2009/09/capslock/index.html

capslock2

具体代码

var bFlash = (function(){
	var version = ($try(function(){
		return navigator.plugins['Shockwave Flash'].description;
	}, function(){
		return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
	}) || '0 r0').match(/\d+/g);
	return (parseInt(version[0] || 0 + '.' + version[1], 10) || 0) >= 9;
})();

var oPass = document.getElementById("pass");
var oTips = document.getElementById("tips");
var oSwf = null;
var sLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var iMode = 0; //0未知,1小写,2大写

function $try(){
	for (var i = 0, l = arguments.length; i < l; i++){
		try {
			return arguments[i]();
		} catch(e){}
	}
	return null;
}
function getSwf() {
	if (navigator.appName.indexOf("Microsoft") != -1)
		oSwf = window["capslock"];
	else
		oSwf = document["capslock"];
	oPass.disabled = false;
}
function warning() {
	oTips.innerHTML = "请注意,您的大写锁定键已打开!";
	oTips.style.display = "block";
}
function hide() {
	oTips.innerHTML = "";
	oTips.style.display = "none";
}

var bFocus = false;
oPass.onfocus = function() {
	if(bFocus) {
		return;
	}
	if(oSwf) {
		oSwf.focus();
		oSwf.capslock();
		setTimeout(function(){
			bFocus = true;
			oPass.focus();
		},0);
	}
}
oPass.onblur = function() {
	bFocus = false;
}
oPass.onkeydown = function(event) {
	event = event || window.event;
	var keyCode = event.keyCode;
	if(keyCode == 20) {
		if(bFlash) {
			oSwf.focus();
			oSwf.capslock();
			oPass.focus();
		}
		else if(iMode == 1) {
			warning();
		}
		else if(iMode == 2) {
			hide();
		}
	}
}
oPass.onkeypress = function(event) {
	if(!oSwf) {
		event = event || window.event;
		var origin = oPass.value;
		var c;
		setTimeout(function() {
			var now = oPass.value;
			for(var i = 0, length = Math.min(origin.length, now.length); i < origin; i++) {
				if(origin.charAt(i) !== now.charAt(i)) {
					c = now.charAt(i);
					break;
				}
			}
			if(!c) {
				c = now.charAt(now.length - 1);
			}
			if(sLetters.indexOf(c) > -1) {
				if(event.shiftKey) {
					iMode = 1;
					hide();
				}
				else {
					iMode = 2;
					warning();
				}
			}
			else if(sLetters.toLowerCase().indexOf(c) > -1) {
				if(event.shiftKey) {
					iMode = 2;
					warning();
				}
				else {
					iMode = 1;
					hide();
				}
			}
		}, 0);
	}
}

if(!bFlash) {
	oPass.disabled = false;
}
package {
	import flash.display.Sprite;
	import flash.ui.Keyboard;
	import flash.external.ExternalInterface;

	public class Main extends Sprite {
		public function Main():void {
			if (ExternalInterface.available) {
				ExternalInterface.addCallback("capslock", onCapsLockHandler);
				ExternalInterface.call("getSwf");
			}
		}
		public function onCapsLockHandler():void {
			if (Keyboard.capsLock) {
				if (ExternalInterface.available) {
					ExternalInterface.call("warning");
				}
			}
			else {
				if (ExternalInterface.available) {
					ExternalInterface.call("hide");
				}
			}
		}
	}
}

后话

当然,其中不完善之处甚多,比如用以获取用户输入的字母是用了延迟侦听,而非获取索引范围之类的办法(因为这方面偶的标准化还不熟),希望你能提出更好的建议。而且fp9以后本地浏览会出现跨域安全性限制,这让测试非常麻烦,所以正太米一直使用maxthon浏览器,它能够在本地环境下跳过这个限制,我也不清楚这是为什么……

正太米最近想明媒正娶一个DC,几经对比后选中Canon IXUS 95 IS,于是立刻上淘宝上找既是低价又在上海有实体的店铺,准备择一良辰吉日将她迎回家中。在寻找的过程中偶然发现,淘宝的那个浮动工具条设计相当之性化!于是乎又择一良辰吉日下一喜帖,将她层层剥开细细品味之后写在了博客上。

在哪里能看到她呢?

很简单,随便到淘宝上搜索一件商品,出来的列表中就有她的身影。我将她的芳容截下,以便日后观赏。

tabao_bar_1

人性化在哪里?

众里寻她千百度,找到之后你可能会问:她与旁人相比究竟有何不同之处?温柔贤惠还是可爱迷人?假设你搜出来的列表很长,请将浏览器滚动条往下拉,随后你就会发现窗口上部犹抱琵琶半遮面的她了(红框标出,IE6及以下此功能无法看到)。假如滚动条再拉回去,你会发现她又回到原来的位置对你含情脉脉,观察着你对滚动条的一举一动。

tabao_bar_2

用户体验

为何要做这样一个功能呢?很显然,在列表过长用户浏览的时候,假设他想使用工具条中的某些功能(比如按价格排序),没有这种体验的话就需要将滚动条拉回顶部,然后一点一点在冗长的页面中寻找她的身影。这样实在是太麻烦了!我们能为用户考虑到的就是最最最方便懒惰的办法!现在是一切以用户为中心的时代,丢失了用户就等于丢失了一切!

于是乎工具条摇身一变,将用户服侍得舒舒服服。试问谁不会被这样一个细心体贴的人儿所吸引呢?

技术实现

好了,给诸位YY完了之后,该到正统的技术原理细节了。她的实现其实很简单,侦听window.onscroll事件,当拖动到一定程度时(这里是工具条看不见时,可以根据scrollTop值来确定),工具条的position变成fixed,如此实现了相对于窗口固定。拖回去的话再变回去,就这么简单。

底部的阴影使用了css3的功能,当然ie是用了滤镜:

-moz-box-shadow:rgba(0,0,0,0.2) 0 3px 3px;
-webkit-box-shadow:0 3px 3px rgba(0,0,0,0.2);
filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=0,OffY=3,Color=#16000000,Positive=true);

最后说下为什么IE6下不支持,很简单,那是因为IE6没有fixed定位。一般情况要么我们选择无视,要么使用absolute定位并通过侦听window.onscroll事件来不停地变化其top值(scrollTop + clientHeight)。淘宝选择了前者,不知是因为技术维护复杂或者用户体验不佳还是推动浏览器升级。

jssc5开发版本测试地址:

http://jssc.googlecode.com/svn/trunk/jssc5/test.html

基于as3和js的。

草草做了个输入框,放在了googlecode的svn上,所以ie下看这个反而会解析为html,其它的可能会只是文字。勉强一下了。

仅写了ecmascript4的lexer(即javascript和actionscript),在词法分析上多做了一些语法分析上的纠错功能,语法分析的内容本次将暂不涉及到。用mootools、jquery、dojo的代码测试了下,目前所有已知bug都已修正。

本次jssc5是基于词法分析的,功能比以前大大增强,性能也有显著增加。但缺点就是必须为每种语言编写不同的lexer,所以将考虑优先一些常用语言的编写。
(注:是每种不是每一个,像javascript和actionscript就是同种语言属于ecmascript,而c、java、c++也可用同种lexer解析)。

可以输入一些明显错误代码,如:var i = 0s; 词法处理上会告知出现的错误并且暂停分析,错误后面的代码会被格式化后保存。

代码折叠已有,深度也已经计算出来,只是没有让它显示出来。:P

源代码过几天会放到googlecode的svn上,lgpl协议。由于语种过多,我个人熟悉的语言也就那几个,因此有兴趣的人欢迎来编写其它语言的lexer,主页上参与编写的人员将会增加你的名字。

关于jssc的项目主页以及之前的老版本或者想使用它的,请看这里:

http://code.google.com/p/jssc/