屏幕尺寸和人机交互

电影

——尺寸: 百平米
人类接触的第一块会动屏幕应该是电影的幕布,当然准确的说是荧幕。大约是100年前,电影作为一种娱乐形式开始为人类带来无尽的乐趣。电影院的荧幕一般都非常大,200~600多平米,从而在视觉上给人以很强的冲击力。大屏幕也意味着,人基本上只能看和听,不太可能与电影有更多的交互。大屏幕也仅仅适合到专门的地方,例如电影院观看,也不太可能在家里或者路上观看。当然现在各种所谓4D电影干各种捅人屁股的事,算是一种有限的交互探索吧。

电视

——尺寸: 平米
交互有限就意味着:明显不个性化,看电影就属于大伙一块看,不自由。于是人类发明了一块小一点的屏幕,可以放在家里,挂在墙上,抱在怀里(如果你乐意的话)。电视通过频道和遥控器提供给人们特定的交互,让人们开始能够有所选择的收看自己喜欢的东西。特别是,你只需要按几个按钮,屏幕中就能出现一个人或者好多人在告诉你一些新闻、趣事、天气预报,这是一件多么科幻的事情啊!你仅仅动了动手指啊,也不需要想许多啊。

个人电脑

——尺寸: 平米
电视机的交互简单到难以置信,也就意味着,人们能干的太少了。Geek们说,我们要对着电视机干更多的事,于是,在同级别的屏幕尺寸上,人类有了电脑。然而电脑的交互过于复杂,硬件上要明白键盘、鼠标,开关、接口,软件上要弄明白xp、360、扣扣。强大的功能意味着学习成本,从而让一大部分用户没法使用电脑,还是去按几个键看电视吧。电脑给人类带来了交互上的伟大进步,但同时,不得不承认,过于复杂的交互让一部分熟练工产生了一种莫名其妙的优越感,这种优越感确正式科技不够发达所带来交互瓶颈的牺牲品。好像说的有点绕,简而言之就是,我居然要敲这么硬这么多键的键盘才能写文章,这么多按钮啊!一支笔上最多只有一个按钮啊!居然也能写文章啊!

移动终端

——尺寸: 平方厘米
其实就是手机。为了照顾一下PDA、mp4什么的,说成终端吧,互联网终端。电脑屏幕越做越小,本来用来打电话的手机屏幕(手机居然需要屏幕!电话就没有啊!)越做越大,于是,有了iphone。移动终端终于实现了,你在家里、路上、上厕所的时候都有一块屏幕了。比起来只能跑到电影院才能看到的大荧幕,这个明显更适合看苍老师。这种私密的、个性化的事,当然要在只有自己能看到的屏幕上看。如果说PC通过键盘、鼠标、图形界面的组合为人类提供了相当复杂而有效的交互,移动终端上能提供的交互就捉襟见肘了。所以大家喜欢用手机用来拍照片上传,这是操作最少、产生信息量最大的方式。

眼镜!?

——尺寸: 平方厘米
手机所提供的交互,在于你要低着头,手握手机,然后大拇指动来动去。时间长了手酸脖子算啊,走路还容易撞人,当然以此为由撞美女这事我会乱说么?不如放在眼镜上啊!从此可以在45度角仰望天空的时候边刷微薄边流泪了有木有!可是我居然不能用手指头在眼镜上戳来戳去,这可怎么办怎么办。不过人类不是已经发明了鼠标么,鼠标不连在屏幕上啊。所以45度角仰望天空的时候,手插在裤兜里,握着一个仓鼠标,就是仓鼠大小般的鼠标,摸来摸去,就看到微薄从第一条刷到了最后一条,然后又有25条新微薄。最终情景就是,路上的行人手都插在裤兜里卧仓鼠标,戴着一个厚框眼镜,眼睛正视前方,然后就不小心掉坑里了。

 

来,本文地址:http://nius.me/screen-size-and-human-computer-interaction

Leave a Comment

Filed under Uncategorized

关于校招这点事

最近腾讯来武汉招实习生,由于阿里今年修身养性去了,所以本来就火爆的腾讯校招就愈发的火爆,简历、笔试、1面、2面、3面,每一步都有人得意失意。不过说实话,对结果还是看开一点好,不要一个笔试没过面试被拒就好像人生失去了意义,毕竟是实习生啊,跟真正的就业不一样啊,很多实习生还想着要读研的读研、出国的出国呢,真正到就业的时候,又是另一番景象了吧。

校招没选上之后?

腾讯没招你,你首先要做的一件事就是证明腾讯错了。我要是你,就会继续钻研技术,不断的提升自己,然后自己创业也好,寻找更大的平台也好,在那里发挥自己的价值,然后冷眼看着腾讯帝国招了一群三流人才渐渐失控:瞧瞧,腾讯也不过如此嘛。多解气。
可很多人确是这样想的:哎呀,真受打击,尼码还不如去玩游戏,搞技术何用啊?那么多人沉迷游戏,最后不还是都就业了。抱有这种想法的人,无异于,我指着你的鼻子骂:你真是个猪!然后你满脸羞懑,想还嘴又不知道说什么好,然后转身就和一群猪玩泥巴去了。

为什么我能力强但还是没选上?

特别是,无论周围的人还是自己都觉得应该能上的时候。有两个思路供大家参考:

1. 公司招人的简单模型和overqualified

我们假设腾讯招100人,有1000人投了简历。那么腾讯应该怎么挑选?理想的情况,当然是给1000个人每一个人打分,从高分到低分依次排,然后最高分的100个人当选,这尼码不就是高考么。毫无疑问,这样太理想了,能考到的武大的人应该都会认同这简直是异想天开。
那么实际操作的时候,每个面试官实行的标准一定是:我觉得你达到我的要求了,那么you are in。这个要求可能每个面试官不同,大约浮动在某一个值吧,我们假设平均值是80。面试官认为你的能力值到80了,那么你就能进公司。
这个时候,如果1000人里面有200人能力值到了80或以上怎么办?甚至于,有150人都是95分,都是overqualified,腾讯的需求就只有100人,于是,那没能选上的只好回寝室感慨生不逢时腾讯不识货blabla。
那这100个不走运的人是怎么呗刷掉的呢?例如笔试的时候因为一些原因,批改笔试卷子的时候抄分出错啊、打分出错啊,连高考都经常出错,一个企业能做到有多好?更何况搞不好还是外包给招聘公司做的。面试的时候碰上了不对胃口的考官、不对胃口的部门,因为腾讯也不是所有部门都要人,公司内哪个部门缺人哪个部门就出来招人,很随机的有木有。

2. 一流人才和二流人才

一流人才会给公司带来一流人才,二流人才会给公司招来三流人才。大公司都有这个问题。理想情况当然都是一流人才,所以那仅仅是个理想。不是说面试官有意不招牛逼的人,而是牛逼的人在面试官眼里就是不牛逼。面试官也是人,是人就有局限性。伯乐难寻啊。

Leave a Comment

Filed under Essays

邮件入门手册

电子邮件算是互联网最古老的应用了,已经形成了礼仪甚至文化。不过在谈到礼仪之前,还是要先谈用法。国内由于qq强大的地位,以至于邮件的使用场合很多都被qq取代了,所以一部分童鞋很少使用邮件,也就不怎么会使用邮件。然而IM终究还是不能取代邮件,就像社交网络不能取代IM一样。最近国外有些新产品让邮件更易用,更像一款社交产品,算是历史的进步吧~:) 不过在更易用的邮件到来之前,先看看老古董要如何使用吧。

邮件的好处

先看邮件比IM优势的地方。

1. 自己不需要马上回复

多好的一件事啊!作为程序员,总算不需要总是被qq的提醒打断自己的思路了。我可以选择一个合适的时间,想清楚,然后再回复邮件。而不需要直接应付对方qq上发来一个问题,然后讨论来讨论区,窗口关掉了,之前在干什么也忘记了。打断是效率的大敌,为什么程序员喜欢晚上干活?因为不会有人打扰。不过我还是希望有规律的作息时间的,所以,最好白天不要打扰我,除非事情非常紧急,一般情况下给我发邮件吧~

2. 不要求对方马上回复

同理,我要找某人某件事,如果用qq或者电话,会直接打断他手头的工作,也许一个绝妙的idea就此消失了,多不好意思!于是我选择给他发一封邮件,他有时间就会来看一下,并回复我,于是我的心理就踏实多了。这样也十分礼貌。在qq上抖人是多么一件令人发指的事啊!

3. 历史记录可查,附件不会丢失

qq不提供服务器端的历史记录。也就是说我用手机就看不到历史记录了,而且qq的历史记录不方便搜索,注意是不方便,不方便是一件很可怕的事,比不能还可怕。邮件的记录是可以很容易的搜索的,这样我想起来什么东西,直接在邮件界面的搜索框搜索就好了,不需要找到那个人的头像,点开聊天记录,点击搜索,或者向前不断的翻页。
另外,很多人喜欢在qq上抖一下,然后对我说,给你传一个文件。我承认qq的发送文件、离线文件做的用户体验很好,但我接受到文件之后,常常过一阵子找不到那个文件了。我可以在操作系统中搜索,哦天哪,慢的要命,还需要找一些其他的工具才足够方便。如果用邮件的附件呢?我可以直接搜索附件,然后重新下载一次。那个文件永远在那里不会丢失,而我也可以看完附件之后就删掉,免的电脑里的文件乱七八糟。
除此之外,我可以轻易的把这个文件转发出去,这样其他人也变的很容易获得这个文件了。

4. 写邮件鼓励深度思考,想好了再说

qq上常常出现冗长的对话,说一句话,解释好多句,于是因为说的人没想清楚,听的人也被迫花同样的时间来等待对方把事情说清楚。当然用来交流感情挺不错,但是涉及到协作和效率,这时候发邮件就效率的多。说的人可以把事情从前到后想清楚在发出来,听的人一眼就看明白了,然后经过一番思考再回复过去。因为这一切都不是实时的,给了人更多的自由和空间。

邮件的基本使用

说了这么多好处,我想你一定打算改变你凡事都qq的习惯了。下面有一些基本技巧,或许你还不了解。

1. 抄送

什么是抄送!?我从前也一直不理解这个问题。直到我去实习,才深深体会到抄送的意义所在。我打算给A发送一个邮件,希望A回复我,可是A偏偏觉得我是一个实习生,爱理不理的,于是我就把发送给A的邮件抄送一份给他的主管B。主管B会收到这封邮件,同时也会看到收件人是A,抄送意味着B只需要看一眼这封邮件就好了,应该由A来处理。这时候B发现A居然偷懒没有处理这封邮件,于是他就回复邮件:请A尽快处理!然后我的事就给办了。
多微妙的一个功能啊!当然上面只是举了一个比较极端的例子。一般来说,收件人是希望对方回复这封邮件,或采取行动,而抄送的人则是让你看一眼,我们在谈论这件事,你也可以参与进来,或者直接删掉这封邮件。这样区分一下,邮件就会更有针对性。
Web邮件端在渐渐淡化这个功能,因为对于普通用户一般用不着,但如果打算用gmail来协同办公,这个概念还是比较重要的,在邮件的详细信息栏可以看到。

2. 过滤器

我常常看到童鞋的收件箱里塞了100多封未读邮件,然后觉得他居然可以活到现在,我还在给他发邮件,太牛逼了。事实上,各大邮箱都考虑到这个问题,并提供过滤器。你可以在设置里找到过滤器,并设置来自某个地址的邮件放置到某个文件夹,或者打上某个标签。对于一些不太重要的订阅邮件、广告邮件,可以选择跳过收件箱,这样有闲情逸致就去看看那个文件夹下是不是有新邮件了,忙的时候就可以放心的不理会,收件箱也不会被打扰。
过滤器设置的时候可以勾选上“应用到历史邮件”,就会把符合过滤规则的邮件都从收件箱过滤到对应的文件夹。
对于一些每次变换邮件地址的垃圾邮件,过滤器可以设置的更强一些,例如将包含”@some.com”关键词的邮件都过滤掉。很多为了防止spam识别,会不断变换邮件地址,例如从service+1212@some.com下次变成了service+1000@some.com,这样你把前一个邮件地址顾虑了,下一次又不能过滤掉了。所以干脆过滤所有的。

3. 邮件群组和邮件列表

经常需要给很多人发邮件。比较普遍的一种方式是,建立一个google group,然后向群组的邮件地址发送邮件,这样所有人就都可以收到了。邮件列表也可以,就是你在联系人里自己建立一个文件夹之类的东西,然后给这个文件夹下所有人发邮件,这样地址栏会出现很多人的名字。嗯……你说qq群邮件?那玩意不遵循邮件协议,用客户端居然收不到,如果你打算躺在腾讯的温室里过一辈子,就去用吧。不过qq邮箱本身做的还是很不错的,这点不可否认。
邮件列表就会遇到reply和reply to all 的问题。回复邮件如果选择reply,就会回复给发件人,这样其他人都收不到,如果是打算像跟帖一样回复的话,结果恐怕就变成私信了。私信的结果是,两个人发的很high,结果其他人啥都不知道,于是一些信息就莫名其妙miss掉了,这在讨论一些事情问题会很严重。那怎么跟帖呢?就是reply to all。
一般来讲在小团队里reply to all 问题不大,发来发去也就那么一些邮件。团队一大,reply to all就会涉及到一些邮件礼仪的问题了,因为那些你抄送的不想干的人也会被一起卷入到一个大讨论中,收件箱就一直被这个大讨论占据着。所以,回复邮件的时候细心一点,be considerate.

邮件与qq的关系

讨论了半天,我们有qq了,但是我们还是需要邮件,同时,我们也需要qq。因为一些急事emergency,qq还是要来的快一些。所以我整天挂在q上,纵然ubuntu下的web qq用起来有时候让我有砸电脑的冲动。邮件适合需要思考的讨论,IM适合快速的信息交换。要说区别看起来不大,也仅仅是“看起来”不大。为什么不用gtalk?嗯,我的gtalk也是整天在线的,而且gtalk的聊天记录和邮件放在一起,非常方便查找。可惜要说服别人放弃qq是一件很难的事,因为他也要说服他的好友一起放弃,impossible啊。

Leave a Comment

Filed under web

思考死亡

死亡恐怕是人类从小时候开始思考的,却一直也想不清楚的事。寒假老家中有老人去逝,让我又常常开始思考死亡。
我不知道什么时候死亡会到来。如果有所谓的命运,我大约属于芸芸众生中无法预测未来的一类人。明天会发生什么?我也不知道。听到一句话:意外和明天,不知道哪一个会先到来。

当死亡到来的时候,我能知道吗?
小时候,我曾经昏过去一次,大约是低血糖吧。醒来的时候正在被同学们抬着去校医院的路上,挣扎了一下,站了起来,把周围的小伙伴吓坏了。醒来的时候,我才意识打我昏过去了。也就是说,如果没有醒过来,我大约是不知道我已经昏过去了。这好像是一句废话。换到死亡上来,也是一样的吧。恐怕只有我醒来之后,我才能知道我死了。这当然很荒唐,都已经死了,难道还诈尸不成。然而,之前对于死亡想不明白,可能就是纠缠在这一点上。常见的思考是,死亡之后世界会是什么样子?可是我连我死了这件事都不知道!我只知道我现在活着。
电影中,常常用第三者的视角来描写主角的死亡,给人以这样的幻觉,人死之后大约还能俯瞰一下战场,或者看到因为自己的死亡造成的巨大影响。但是主角都已经死了,就什么都不知道了。如果电影是第一人称视角,那么应该戛然而止,可以出字幕了。

这下好了。我不知道自己什么时候会死,而且我也不会知道我已经死了!于是,在上帝面前,我成了对死亡一无所知的人。
那就好好活着吧,至少,我知道我现在还活着。 ;)

Leave a Comment

Filed under literature

搭建个人博客的通用步骤

周围很多同学渐渐想要搭建自己的博客,但是拜僵化的教育所赐,很多软院的同学到大三甚至毕业了都还不是十分清楚该如何搭建一个个人博客。一般来说,wordpress本身已经做的十分简单易用,甚至其目的是给非程序员一个搭建博客的渠道,讽刺意味的是,除了一些爱折腾的同学,搭建一个简单的个人博客常常让人感到很迷茫。我自己也曾经是这样。这里给大家简要描述一下总体要经历的步骤吧。

注册域名

既然是个人博客,与新浪博客的区别就在于:高度的自定义。自定义包括彰显个人审美品位的页面,代表个人的域名(而不是yourid.sina.com这种二级域名),还有很多其他你希望的特定功能,例如广告、流量统计等等。域名是自定义的第一步。

免费域名

现在比较流行的是.tk,你可以注册一个yourname.tk,而不用付费。链接:http://www.dot.tk/zh/index.html?lang=zh

付费域名

如果你想要yourname.com或者yourname.me这样的域名,就必须找一个域名注册商去购买。一般是按年付费。国内的域名注册商可以注册,但是你必须进行个人备案才能使用,一般都不用。一般使用的域名注册商:

Godaddy: http://www.godaddy.com/ 老牌注册商,支持支付宝付款。如果你在支付的阶段停住,过几天godaddy就会给你邮件各种优惠吗,最高的是7折左右,所以如果不着急,可以试试。

Namecheap: http://www.namecheap.com/  这两年起来的注册商。由于前一阵子godaddy犯了一个和政府同流合污的错误(支持SOAP法案),namecheap成了最大的受益者。如果你有visa,在这里也可以,便宜不到哪去。

购买服务器(php空间)

注册完域名暂时不要配置。去买服务器。就是你的网站的后台。Wordpress是php写的,而php空间(英文是hosting)是搭建php网站最方便的方式,一般会有一个很用户友好的控制面板(相对于命令行比起来)。这里你去google搜索php空间,一堆一堆的。在天猫的时候一个同事给我推荐了一些,都很便宜,放在下面:

Hellohost: http://hellohostnet.com/hosting.html 我用的新手套餐#3,绰绰有余

Linost

HomeZZ

购买空间的时候一般会让你设置域名,填你刚注册的域名就好了。购买完之后会给你一个ip,就是你服务器的地址,和你的后台访问的地址,浏览器访问这个地址会显示后台的控制面板,常见的是CPanel 和 DirectAdmin,用起来差不多,无非是包括:

  • 数据库Mysql管理
  • 文件Files管理
  • 域名、子域名管理
  • 如果你明白ftp是怎么回事,一般有ftp服务,可以试试filezilla

配置域名

回到你的域名注册商那,例如godaddy,给域名添加一条A记录,将@指向你买的服务器的ip。@的意思就是yourname.com,是一个代词,相当于将yourname.com绑定到了服务器的ip上。同时也可以设置www的子域名指向同样的ip,这样大家用www.yourname.com和yourname.com都能访问你的博客了。

wordpress

去百度wordpress,然后下一个最新版的wordpress压缩包。这里有整个后台的代码。

按照wordpress网站上的的要求修改wp-config-example文件,包括重命名成wp-config、设置数据库名、数据库用户和密码。

这三个值都和你的服务器有关。访问你的服务器的控制面板,前面提到的cpanel或者directadmin中,进入Mysql管理,创建一个数据库,button很小,仔细找找。名字随便起,这个名字就是要填入config文件的DB_NAME。而数据库的用户名和密码一般和你后台面板密码一样,如果不一样应该也会提示你,或者你在面板上能设置。

修改完config文件将wordpress所有文件重新打包。这时打包最好不要用原来的打包方式(解压后有一个wordpress文件夹)。选中wordpress文件夹中的所有文件,用右键打包成zip,这样解压后是所有文件。另外,rar是windows下的格式,忘掉rar吧,不够通用。

都搞定后,在服务器的控制面板上的文件管理中,找到public_html文件夹或者www文件夹,进入,并上传刚才压缩好的文件夹。你要相信面板一定给你提供了上传的地方,所以仔细找找,directadmin在文件目录显示页面的最下方。如果实在找不到,用ftp上传吧,百度弄个filezilla。

注:在传到服务器上之前可以自己先装一个wamp,就是本机上的服务器试一下,把wordpress跑起来了,有信心了再上传~当然,如果你用的不是windows,我想你大约不需要我这样指导。

开始使用

上传完了,在服务器的文件目录中解压。一般有extract按钮。千万别抱怨按钮小、难找,有就不错了,要不然你得一个个文件上传,累死你。如果你按照我刚才说的方法打包,那么public_html或者www的文件夹下面就会有一堆文件。否则,就只有一个wordpress文件夹。这两种情况都能运行。

删除掉public_html下原先有的index.html文件。一定要删哦~要不然你一定认为你的wordpress坏了。

访问yourname.com,这时,如果一切正常,那么你就能看到wordpress的用户指导页面了,后面就一步步配置就完了。如果是那种解压完之后有个wordpress文件夹,那你得访问yourname.com/wordpress/,这样就可以了。如果觉得这样不爽,控制面板的文件管理提供了移动文件的功能,你把文件选中,copy to clipboard,类似于“复制”按钮,然后到你想要的位置,点击move clipboard to here之类的按钮就行了,类似于“粘贴”按钮。

按照wordpress用户指导配置完之后,会进入wordpress的管理页面(dashboard)。这时,你就可以在这个页面编辑、发表文章,修改主题,安装插件啥的。

再访问yourname.com,就会发现你的博客已经显示出来了。

喝杯咖啡吧。;)

3 Comments

Filed under Varkrs

State Pattern in JavaScript

In recent coding I encounter a situation that in different contexts, the button’s action should change accordingly. In other words, the page has several states, and in different states, the action of a same button should be different. I know in Java we have State Pattern in hand to deal with this situation, which take the advantage of Java’s OO features like inheritance and polymorphism. I don’t want to use JavaScript’s class which would definitely raise the complexity of the codes. With the help of the realization of Strategy Pattern in Book JavaScript Patterns, I write something like this.

Say, we have two buttons in the page, which are add and del. SPAGE is the namespace.

var SPAGE = {
	btn: {},  //collection of all btn's click event handlers
		  //it has different states
	          //in this example there are two buttons: add, del

	states:{ //state set of btn
		full: {}, //state 1: full
		half: {}  //state 2: half
	},

	setState: function(s){ //switch state
				  this.btn = this.states[s];
			  }
}

SPAGE.states.full = (function(){ //realize event handlers of state 'full'
	var add = function(){
		print('full add click');
	}
	var del = function(){
		print('full delete click');
	}
	return {
		add: add,
		del: del
	}

})();
SPAGE.states.half = (function(){ //realize the methods of state 'half'
	var add = function(){
		print('half add click');
	}
	var del = function(){
		print('half delete click');
	}
	return {
		add: add,
		del: del
	}
})();

//set state and call
SPAGE.setState('full');
SPAGE.btn.add();    //click the button add
					//output: full add click
SPAGE.setState('half');
SPAGE.btn.del();    //click the button del
					//output: half delete click

So, I think I take advantages of JavaScript’s nice features of objects. I don’t know how you guys solve the similar situation. Any discussion is welcomed warmly~:)

Leave a Comment

Filed under Tech, Varkrs

Jquery Animation’s Synchronization and Nested Animation Using a Counter

Animation In Jquery is quite simple and easy to use. But things come complicated when you want to create a series of animations. Recently I am building an app which heavily relies on the animation, and get really exhausted sereral times. I learn a lot from this.

Animation Sync

Here is what I write at the very beginning.

$('#left').animate({left: '300px'}, 300, function(){
    $('#left #left-node').attr('id', 'center-node');
    $(this).attr('id', 'center');
    //...
});
$('#center').animate({left: '600px'}, 300, function(){
    $('#center #center-node').attr('id', 'right-node');
    $(this).attr('id', 'right');
    //...
});
$('#right').animate({left: '900px'}, 300, function(){$(this).remove();})

Three blocks will move right at the same time. Then left and center block will change their id to fit the new position. Then here comes the problem: although the three animations in different elements seem to finish at the same time, they finish one after another indeed. And I even don’t know which one! So the operations of changing the id will result in a mess. Needless to say other animation I want to do after.

I need a function that can be called after all these animations finish. I resort to the Queue in the Jquery but find the queue is binding to a particular element. Then a school mate tells me to use a counter and a shared callback to synchronize the animation. That’s quite a good idea. Then the code will be like this.

var sync = 3;
function callback(){
    if(--sync > 0) return;
    //...
}
$('#left').animate({left: '300px'}, 300, callback);
$('#center').animate({left: '600px'}, 300,callback);
$('#right').animate({left: '900px'}, 300, callback);

Nested Animation

As I dynamically generate the html, so I want to make the animation more beautiful. So I write this.

//...generate the html
$("#left-node .node, #center-node .node").hide().fadeIn(300, function(){
    $("#left-node").animate({left: '300px'}, 300);
    $("#center-node").animate({left: '600px'}, 300);
});

I want all the nodes to fade in and after that move the right position. Then the disaster occurred. Since the $() selector has selected more than one element, so the callback will be triggered more than once! Say 15 nodes have been selected, the left-node and the center-node will animate for 15 times! And the default animation queue in both element will function, that is to say, they will animate for 15*300 = 4500 ms. The worst part is, if any event trigger other animation on the two element, the upcoming animation will be held up till the repeated animation finish. The users will see a blocked response in their eyes.
This can also be fixed by the counter mentioned above.

var $nodes = $("#left-node .node, #center-node .node");
var sync = $nodes.size();
$nodes.hide().fadeIn(300, function(){
    if(--sync > 0) return;
    //... further animation
});

OK! All animations go as expected now. But this is really a hard work for a novice of JavaScript like me.

Well, maybe the best way to avoid these complicated cases is not to design so many animations in your web page.

Leave a Comment

Filed under Tech, web

UMD 互联网开发模型(基于REST)

前言

最近一直在做Web前端开发,学习js,写着写着,发现越来越像之前做过的android开发了。这倒不是说我把js当成java来写了,而是从一些基本思路上来看,互联网产品都或多或少有着相似之处。当然,都是基于REST风格的前后台交互,统一,简单。在这里总结成模型。外国人也常常提出各种模型,主要是方便理清思路,所用到的概念和技术不一定是新提出来的,而UMD模型提到的技术则更是毫无新意可言,如果你是Web开发老手,就不太需要了,我想你心中一定有自己的一套模型。这个模型可能更多是帮助新手认识互联网开发的所需考虑的各个方面。

假设:

用户使用 Web app是希望通过一系列交互来提供或找到某些信息

这个假设很多情况下都成立,因为其实啥都没说。不过游戏能不能算Web app呢,看如何理解了。个人没开发过游戏,没发言权,所以暂时不算好了。

UMD: UI, Memory, Database

把假设引申一下就可以得出,整个Web其实就是信息流的交互,因此,我们可以从数据的角度来观察任何一个Web app的架构,数据存在于哪些位置,做怎样的流动,产生怎样的后果。数据一般来说存在于以下位置:

  1. UI 用户界面,数据按照一定格式直接呈现给用户,提供信息
  2. Memory 内存,客户端或者浏览器里暂存的数据,在app生命周期中存在,与UI对应
  3. Database 数据库,存在于网站后台,用于数据的持久化存储,静态的html应该可以包括其中,如果通过资源(resource)这个词来描述可能更适合。
  4. Cache 缓存,缓存可能存在于客户端的本地数据库或者后台的数据库中,可选项,其与Memory作用类似,可以用相同的思路讨论,或者成为UMCD模型进一步讨论,这里不多说了。

UMD Consistency and Update

既然我们可以把数据分成三块,这三块数据最基本要保证:一致性。即三部分数据所表达的信息一致,换句话套用一下就是:所见即所得。

因此,三处数据则至少有一下三类方法来更新数据:

  • updateUI()
  • updateMemory()
  • updateDatabase()

这里的update不是指狭义上的CRUD中的update,我们可以看成是一种对数据的操控,包含了CRUD,让这个数据变的更有效,并得到相应的返回。

另一方面,用户界面需要提供用户交互,让用户找到自己想要的东西。因此,在UI上还会至少有一类方法,处理界面上出现的用户交互:

  • handleEvent()

handleEvent()应该不仅包括从网页上进行的交互,用户输入的url,以及在别的网页点击链接进入这个网页,都应该包括其中,不过这部分浏览器替我们做了。从计算机一出生到现在,其做的最基本的一件事应该就是接受input,提供output,这两点在Web app中可以对应成handleEvent()和updateUI(),因此这个模型我想至少不是错误的。有了这四个方法,我们就可以绘制一般的Web app的程序运行模型。

一致性原则告诉我们:

三处数据任何一处的数据更新,另外两处数据都要同时也得到更新。

在图中看起来UI和Database是不能直接通信并互相更新的,Memory起到了一个枢纽的作用。但其实现实的Web开发中,很多时候是没有Memory这一层的,甚至在几年前,认真对待内存中的数据可能都是一件非常滑稽的事,因为那是浏览器要考虑的。但随着Web app的发展,趋势是其越来越像一个客户端,需要有自己的固定的内存分配来存放数据。因此我们讨论的时候就把Memory带上,来强调在实现前台的时候需要考虑Memory。我最近写的代码中就确实需要维护内存中的数据,通过Memory作为桥梁来更新UI数据事实上可以认为或者实现成updateMemory()和updateUI()需要同时被调用。

UMD Realization

模型是用来指导实践的,即在具体的Web app实现场合,我们如何利用这个模型帮助我们理清思路,并完成各个部分的开发。

User Interface

在web app的场合,UI主要由HTML和CSS来呈现,通过JavaScript来动态控制。

  • handleEvent()

Jquery是很经典的事件驱动的Js库,能够很好的绑定事件到回调函数来处理事件。原生的JavaScript当然更是可以绑定事件了,只是写起来很罗嗦,并且有各种兼容性问题。

  • updateUI()

Jquery库也可以进行DOM、CSS操作来更新用户界面,以及各种实用的动画。同时,用JavaScript来控制DOM总是一件从编码到浏览器调用都很不开心的一件事,很容易写的效率很低,相应的,就有一些前端模板出现,例如mustache,写一定的模板之后,mustache能够为你生成相应的html代码。HTML5标准中提供了更多的帮助你改善UI的工具。

根据一致性原则,updateUI()的原因可能是另外两处数据发生了变化,而在模型中,鼓励内存的变化来调用updateUI(),而不是后台的数据返回后直接更新。注意,是鼓励,但不是所有场合。

Memory

Web应用发展到现在,很多应用仍然不需要特别注重存在于内存中的数据,只是单纯的把后台的页面呈现出来就可以了。但随着Web app的复杂和功能性越来越强,在内存中存储数据能够有效降低网络请求数量和页面响应速度。HTML5中规定了一些在浏览器端的数据存储功能,解决问题的思路和放在内存中类似,在处理大量数据的场合更适合。

内存中的数据内容一般是UI中呈现数据在内存中的映射,首要问题是数据结构,数据如何进行存储,Js中提供的object普通对象和function函数对象能够用一种很简单、基本的结构来存储数据:键值对和数组的集合。这个也可以在JSON的数据格式和object literal的声明方式来得到体现。即

{

    key: value,

    key_array: [v1, v2, …],

    key_obj: { obj_key: obj_value},

}

考虑将数据和业务逻辑分开是一种符合趋势的思路。显式、明确的考虑updateMemory()函数能够让人对数据把握的更清楚,特别是在Js这种灵活性太高的语言中,也使得代码更容易维护。虽然Js的函数式语言特性让内存中的数据可以封装在各种函数的闭包中,但没有组织的零散数据的可维护性不太好,虽然写起来很快。

updateMemory()方法的调用者可能来自UI或者后台数据库,既然Memory是一个信息的桥梁,那么Memory既然是数据的集中地。例如用户输入的handleEvent()需要修改内存中的数据,而后台数据更新后的返回,也需要修改内存中的数据,或者至少看看内存中数据是不是已经过时了。

Database

后台的数据一般都存在数据库中,辅以文件系统。使用REST风格的结果是,统一认定后台的数据位资源,后台所要做的就是接受前台的请求并CRUD特定的资源。对于前台来说,后台就是一个黑盒子,只有提供API接口和数据协议来交互。REST风格的API接口其实就是一段特定的URL,数据协议基本上通过JSON格式规定,从而保证简单、易用。

updateDatabase()的调用者一般来说是Memory,即当Memory需要发生改变的时候,去改变后台的数据。不过Web程序中很多时候不用涉及到Memory,所以也可能是直接的用户事件调用的,例如提交一个表单。

在ajax大量广泛的如今,很多时候连表单和上传文件都希望能够异步实现,提升用户体验。异步通信的好处就在于,可以很快的响应用户界面而不必等待后台的操作完成。但是与此同时,这将可能产生UI、Memory两者和Database的数据不一致。在后台操作完成之后,及时的updateMemory()并通过其updateUI()是很有必要的,特别是当后台操作失败的时候,需要及时修正Memory中的数据,并提示用户。

结语

暂且写成这样吧,主要解释了三种数据:UI、Memory和Database,以及相应的四种方法:updateUI(), updateMemory();updateDatabase和handleEvent()。具体实现的时候,脑袋里带着这几样东西去写东西,应该比啥都不想一个事件一个事件的去写来的高效,鲁棒性强。不过我个人写前端也没多久,后台也只用django写过一个学期,认识或许还不是很到位。接着code一阵子,看需不需要把这个模型弄成前端框架啥的。模型中所有的概念现有的框架和语言都已经够用了,只是不是那么明确而已。

1 Comment

Filed under Tech, web

JOSSO原理探索和使用

互联网app多了,老要登录注册是个大麻烦,各大SNS都提供自己的profile来支持第三方的身份认证,免去注册和登录,是一个不错的方案。一个品牌旗下的多个网站,也可以一个账号多处登录,典型的是taobao.com和tmall.com,跨域登录大约是通过专门的session管理服务实现。前一阵子flacro开始研究JOSSO,Java Open Single Sign-On,一个开源的跨域登录解决方案。后来flacro抱怨文档太少,用起来摸不着头脑,这两天和他一起研究了一下,大约能够跑通了。

JOSSO的跨域原理

跨域登录就是在taobao.com登录了,在tmall.com也登录了,首先浏览器的cookie就不能跨域访问,只能依赖session,所以需要独立的session管理服务器。JOSSO通过一个单独的authentication服务器(简称josso服务器),和一个和app放在一起的agent服务来实现session的共享管理。

JOSSO把资源(就是html等)分为两类,一类是不能随意访问的,需要登录,是protected资源,另一类是可以随便访问的。而其原理则是用agent的来判断,请求的资源是否是protected,如果是,就转向authentication服务器让用户登录,不是就正常返回。所以,JOSSO只解决认证即authentication的问题,而不能解决授权即authorization的问题(例如我不能访问你的数据),这部分应用自己去处理。


跑起来JOSSO的示例程序研究之后,发现过程如图所示。

  1. 用户用浏览器访问app.com/user/1/content/,这是一个protected的资源,注意,这是在app.com这个域
  2. Agent拒绝直接访问,转发给JOSSO Authentication服务器,这是在auth.com这个域
  3. JOSSO服务器返回一个登录页面,auth.com/josso/login.html
  4. 这时用户填写登录表单,然后提交POST,注意,提交给auth.com
  5. auth.com收到POST数据,验证用户有效性,无论真假,都会将请求转发给app.com的agent,并把结果附在转发的请求中
  6. agent接受到转发的请求后,如果用户已经登录了,服务器上就会建立session信息,同时agent把原始的请求(/user/1/content/)发给真正的应用服务
  7. 应用服务返回相应的页面给浏览器,此时,用户状态已经登录。

实际情境的使用

一般的应用情境其实用上面的方法已经能够cover了,即登录后的页面认为是受保护的,登录前是公共的就可以了。问题在于,用户就是单纯的登录,然后建立session怎么办?这种情况在ajax遍地跑的今天很常见,常常用户在一个页面上已经回答了一个问题,或者修改了某个内容,要提交时,再提示用户登录。这个时候,即页面本身就不是protected,上面的情境就不太适用了。特别是,现在登录往往都做在右上角,搭配吊顶导航条,点login按钮出一个弹窗,登录完了页面不变(youku就是类似的),这连跳转都没有哦亲。

解决方案

方案1:(失败)

既然在4步要提交表单,那就不要前面那些步骤,直接在app.com的页面写表单,然后提交给auth.com,得到结果后页面更新(不是刷新),即异步提交跨域表单。这样做首先的问题是,这是跨域ajax啊!一跨域就悲剧啊!跨域ajax有一些解决方案,例如用一个代理服务器,额,太笨重;还有JSONP,动态向app.com的页面上写auth.com的js,然后得到数据,但是,只有GET。唯一能够把表单提交给auth.com的方法是,表单的action,这玩意是可以跨域提交的。可是这是同步的,提交完了,页面就刷新吧……

从第4步开始还有一个问题,那就是,本来是要访问一个protected资源的,现在没访问了,然后JOSSON的行为就变的莫名其妙了!它仍然继续走到第7步,但是链接进行了奇怪的拼接,然后直接404。我觉得应该是可以配置的,但是完全找不到地方(糟糕的文档啊)。这个方案就先放下了。

方案2:(成功)

然后就想到了iframe。

在app.com的页面里嵌入一个专门负责登录的iframe页面,这个页面可以请求一个protected的资源,即仍然从1开始走,不过外围的页面仍然是公共可以直接访问的。这样可以在iframe里面走一通,让用户登录,修改auth.com提供的表单的样式就好了。接下来的问题是,登录之后外围的页面怎知道登录了呢?这里用了两个trick

  • 那个protected资源,我们可以设置成一个特殊的资源,例如一个login.jsp,因为已经登录了,这个jsp可以拿到用户信息,例如id, username等等,这时它不返回新的页面,而是返回json数据
  • Iframe接受到了返回的数据,拼接到了内部,这时外部页面通过绑定iframe的onload事件,因为iframe得到数据后进行了一次刷新,所以onload得以调用,这时,进入iframe内部拿数据,更新页面,然后把iframe删掉。这里还有一个安全策略的问题,iframe是不能拿到跨域的网站的内容的,但是此时因为一系列跳转,返回给浏览器的json仍然在app.com的域下,所以可以拿到。好惊险= =

总算有了一个大致可用的解决方案,还没有具体实现,估计实现起来还会有新的苦难,吼吼。欢迎有用过JOSSO的讨论下。

Leave a Comment

Filed under Tech

长杯子和用户需求层次

前几天向同学抱怨,说没法在淘宝上搜索到一个“长”杯子。同学Y怪异的看着我说,我都没明白你想要什么,更何况淘宝的搜索引擎。这倒是提醒我思考。假设我是用户,Y是产品经理,那么Y就需要考虑,我真正的需求是什么?

backpack

一个足够长/高的水壶

于是我开始叙述:去年买了一个瑞士军刀的电脑包,随身背,但是两侧放水壶的地方的设计很奇怪,没有松紧带,拉链只有一侧有,又很短,半个瓶子都套不住,但是上面有一条带子,如果水壶足够长或者高,就能用带子拴住。之前买的普通水壶,因为不够长,里面有水的时候,就很容易摔出来,然后就变形了。

听到这里,Y产品经理就明白了,原来你是要一个尺寸上很长的水壶,刚才说“杯子”明显是没描述对。此时Y产品经理可以去跟开发说,开发出来一个足够长的水壶吧~

一个能够放在包上的水壶

但Y转念一想,用户真正想要的是什么呢?其实用户是希望水壶能够放在包上,方便携带。那这样的话,就不一定要弄一个长水壶了。只要能够放在包上,我们为什么不做一个有扣子的,可以扣在书包的那条带子上的水壶呢?这样更稳固,就算书包倒过来放,都不会摔出去。还可以在扣子上动动脑筋,让水壶很容易从扣子上取下来,不要因为扣子降低了用户体验。这个时候,Y已经觉得差不多了,给出一个更好的解决方案,并且问题解决的更彻底。于是Y可以跟开发说,开发一个带扣子的水壶吧,扣子要牢固,而且容易解开和扣上。

随身带水

但Y吃完饭的时候突然又想到,其实用户真正想要的是能够把水随身携带,从而在外面或者不容易喝道水的地方,能够有水喝。水壶是一种很传统的解决方案,那有没有更好的呢?或许可以设计一种袋子,能够把水装进袋子里,然后有一个壶嘴,用户可以方便喝,也方便用户灌水。看西域的那些羊奶,不就是装在那种牛皮袋里嘛,我们的袋子可以设计的更小更好看。袋子是可以放在书包里的,因为形状不是很固定,塞在任何一种包里都可以,还减轻了总重量。于是Y大喜,这是颠覆性的产品,我要改变世界了。于是Y跟开发说:不要开发水壶了,开发一个不会破的水袋吧,弄一个壶嘴,方便用户喝也方便用户灌水。

喝水

受到水袋的激励,Y开始进一步思考,用户到底要的是什么?其实用户就是想在口渴的时候能喝水,那怎样才能让用户不会口渴呢?可不可以发明一种糖果,让用户吃了一天就不用喝水!?没错,就这样!半个月后,Y拿出一本《糖果水》的科幻小说,寄给了出版社。

总结

经过这一番思考,我想产品经理的思路已经比较明白了。只有通过不断的追问,才能够给出真正优秀的解决方案。福特说,如果我当年去问顾客他们想要什么,他们肯定会告诉我:“一匹更快的马”。然后福特发明了汽车,让顾客不用骑马了。从文中可以看出,无论把需求分析到哪一层,都可以给出对应的解决方案,最终使用哪一个,就得按照很多其他因素来考虑了,开发实力,投入资源什么的。本文这个喝水的例子,可以说明一个广为流传的断言:所有的需求的背后,都会有一个马斯洛需求。解读用户的需求有一些模型,Y型大约是比较公认的一种,有兴趣可以找找看。

So, what do you want ?

3 Comments

Filed under PM