HTML5 学习笔记之一 canvas

归类于CSS | HTML | JAVASCRIPT | WEB DESIGN 参与评论

学习笔记

基本结构

<canvas id="element" width="200" height="200"></canvas>

可以通过css对其设定样式

canvas {
    border: 1px solid blue
}

简单的画斜线过程

<script type="text/javascript">
function draw() {
    var canvas = document.getElementById('element');
    var context = canvas.getContext('2d'); // 这里表示获取该画布的上下文,所有的操作都通过上下文(context)来进行

    // 开始通知canvas要执行绘制操作
    context.beginPath();
    context.moveTo(70, 140);    // 将位置移动到点(70, 140)
    context.lineTo(140, 70);    // 终点位置
    context.stroke();           // 这个函数才是真正执行绘制的操作,上面的都不是
}
</script>

,

《瞬间之美》阅读心得

归类于WEB DESIGN 参与评论

前言

最近在阅读《瞬间之美》这本讲述用户体验设计的书,这里做一些总结和描述心得体会

本文主要包含两部分内容

  1. 内容概要

  2. 个人心得

内容概要

下面对该书的每个章节内容做一个概要的总结,便于后续了解

第一章,令人心动的第一映像

重点元素突出

布局需要突出当前页面的重点,比如你希望用户在这个页面最想做哪个事情,则对该部分的交互做视觉强化

比如wordpress首页左侧的GetStarted按钮

大号字体,独立的栏目区域,橙色的大按钮,第一眼吸引用户的眼球

img

视觉移动线

用户在浏览的时候的视觉移动线是从一个矩形的左上角到右下角的顺序

于是,可以考虑在这条线上做相应的顺序引导元素的放置

第二章,凸显个性

讲述的是站点级别的风格一致性,比如logo和icon之间的效果一致性

布局风格保持一致性

第三章,导航之道

导航要做到如下几点

  1. 归类,合理的对站点内容进行归类,可以让导航清晰有调理

  2. 用户是行为的触发者,产品是工具,要求导航名称采用动宾结构的词语,而不是简单的名词罗列

  3. 表述要正确

第二点终点描述一下

很多时候,导航都做了这样一个事情: 这里有什么,潜台词是产品让用户去这里头去找某个东西

这个观点背离的用户是使用者的原则,产品只是一个工具,用户要做某个行为的时候,才需要去care导航

应该想windows的office系列的菜单栏学习,比如word软件的菜单栏都是如下的一些词语

view, edit, print

它们基本都是动词,表示它是一个行为的入口,当用户要做某些操作的时候,才关心这个地方,并且是用户触发view/edit动作,让产品去做相应的事情

摘自原文: 创建产品导航时,请确保你是以用户为出发点,而不是以系统为出发点。用户不是工具,系统才是

第三点讲的是很多页面的登录框都使用来Login这样一个单词

殊不知Login是一个名词,表示登录的面板,但是你想要的是希望用户进行log in这个动作

所以,这里是一个很隐蔽的错误,可以改成Log in或者换成同义词Sign in

个人心得

第二点是像我这样的开发人员常犯的错误所在,我一直认为,系统应该告诉用户这里有什么

给出一个名词列表,明摆的去让用户了解

作者的这个观点,让我收益颇丰啊!

第四章,链接生来不平等

讲述可以通过字体大小,颜色深浅,位置等设置来突出某个特定信息在同类信息中的重要性

比如标签云的展现特点等

第五章,标签云里抬头看路

标签云对用户的理解成本是一个需要考虑的问题

并不是所有用户都了解为什么某个标签的字体大,颜色深,而其它的各自不同

用户会因此而困惑,而系统是不应该让用户去花费这样的时间思考和困惑

第六章,开门见山

不要有冗长,并且对用户使用没有意义的文字,几乎不会有人愿意去阅读它们

站在用户的角度考虑,他们进到这个页面,最想干什么?大家都很忙,没人有时间听你罗嗦,用简洁明了的几个动宾词语告诉用户

找到它,点击它,然后怎么样!

心得体会

看到这里,我想到了豆瓣的一个发布内容的输入框的那个按钮

大多数的提交按钮文字都是确定提交诸如此类的通用词汇

但是那个地方的按钮文字是好了,发布上去,非常通俗,并且非常切合作者的用户操作产品这样的一个观点

让使用者真正感觉到自己是在操作某个工具,而不是被产品带着走,非常人性化的设计

第七章,为页面做标记

系统需要用户提供一些信息的时候,一定要告诉用户:

  1. 系统要的是什么

  2. 它大体是什么样的

  3. 这是一个简单的例子

  4. 这样的是不能被接受的

心得体会

对于表单输入来讲,很多页面都重点做了你不能怎么样怎么样的提示

但是给一个合法的范例起到的正确引导作用要更加大的多!

第八章,生动传神的视频

当你无法用简介的文字或者图形来告诉用户怎么做的时候,视频动画是一个很好的选择!

心得体会

视频动画的成本还是相对太高,而且还是不宜把动画的时间弄的太长,否则也不见的有几个人愿意从头看到尾……

第九章,提出搜索词建议

简言之,就是suggestion的使用,避免用户输入一些系统已经存在的内容和出现多个词表示同一个意思的情况

同时也是对用户操作的一个简化!

不过它也有负面影响,比如会导致结果偏离用户本意之类的现象

心得体会

个人觉得suggestion真的是一个非常赞的应用,不过从技术实现的角度来讲,相对复杂一些,对后端而言涉及到数据的积累,挖掘和分析的相关工作

对于初期的产品而言,是一个比较大的挑战

第十章,列出搜索结果

讲述一下几个内容

  1. 对于分页等一些基本UI交互模式,尽量参考业界大佬的模式,它们就是一种隐式的标准

  2. 对于UI的交互操作,需要考虑是否足够易用,字体是不是太小?位置是否不容易发现?

  3. 不要依赖浏览器的后退按钮,系统应该能够提供给用户当前的位置和之前的位置,并提供快速返回的途径

心得体会

第三点提到的当从检索结果跳到详细的页面的时候,很多站点都没有提供一个返回检索结果页的入口

大部分都是依靠浏览器的后退按钮来实现

Gmail之类的一些国外比较牛X的站点这个地方做的不错

国内环境,很多网页都是新窗口打开,也就不存在这个问题……

第十一章,精炼你的搜索

不要一次提供用户太多的选项,可以在用户需要的时候,自行添加和删减

第十二章,视频播放器的标准化

讲述一个靠谱的视频播放器界面,一个原则: 照顾好老弱病残孕,让大家都能够操作起来很顺手!

第十三章,巩固你的表单布局

  1. 表单的文字和输入框的位置,从上到下是一个比较不错的方案

  2. 确定取消按钮的位置不平等

  3. 将首选的操作方式强化,弱化第二种选择地方交互方式

第十四章,驾驭Wizard向导

  1. 告诉用户你当前处在哪个步骤,总共有几个步骤

  2. 告诉用户接下来你会遇到什么怪物,让用户有心里准备

  3. 告诉用户你遇到的怪物都不可怕,都只是在有限制的范围内活动而已,不用太担心

心得体会

作者在文字提到了采用手风琴的面板控件来做Wizard,个人感觉不是太好

Accordion控件的一个很大特性是每个tab都是可以点击,并且无论你处在哪个tab,都可以随时切换到另外的一个tab上

但是作者又想做顺序引导,在第一个tab的时候把下面的tab禁用掉,这个就会显得不论不类,用户发现它是一个手风琴,但是操作起来又不像手风琴,难道不是更加怪异?

个人以为还是类似购物车之类的顺序引导是最合适的

将步骤页面的上部

Step1 >> Step2(Current) >> Step3 >> Step4

This is Step2 ...

高亮当前所在的步骤内容

第十五章,即时校验

讲述表单输入的时候,如果发生错误,给出即时提醒

如果用户输入的正确的,也要给提醒!

第十六章,简化长表单

很多时候,我们提供了一个很长,很复杂的表单,并且这些表单应为逻辑需要有相互依赖

比如当你填了某一项之后,才能再填写另外一些内容,这样和该项无关的其实是用户不需要care的,但是还是仍然会放在页面中吓人!

于是,我们可以通过技术手段去优化这样的表单,只提供用户关心的选项,减少用户的选择范围,比如yes/no,很多时候no是可以不需要的

另外,要让用户有预期,这样的表单变化到什么时候结束,还有大概多少内容,不要让用户感到恐慌

第十七章,让他们登录

帮用户记住用户名,如果他们忘记密码,可以直接使用当前记住的用户名发送邮件即可,不需要再让用户重新输入一次了

也就是说我们经常看到的那种忘记密码的相关流程是可以不必要的!

Username: xiaoqiang I am not xiaoqiang

Password:

第十八章,计算字符数

在用户进行表单输入的时候,如果有字数限制,应该提供一个即时的剩余字数提示

来告诉用户你能输入多少,当前已经输入多少,还能够输入多少!

Twitter在用户输入剩下20字的时候,会把相应的提示字体颜色发生变化来提醒用户的注意,一个可以借鉴的点!

第十九章,创建个人资料

用户的个人资料页面,尤其是对于一些社区的站点来说,不应该仅仅是用户的个人信息资料静态展示

还可以加入该用户的近期操作记录,比如ta今天干些什么事情,发表了什么文章,关注了哪些人等等动态信息

这样的动态信息能够更加吸引其它浏览用户的信息,提升可阅读的内容

第二十章,编辑

对于复杂的交互操作流程,需要对交互的过程进行归类和汇总,最便捷第提供用户最渴望得到的,并且简化起操作流程

对于不是经常用到的地方,可以通过高级选项等位置进行获取

对话框不是一个必须的交互载体!

第二十一章,开展社交活动

主要讲述Follow的作用

第二十二章,显而易见的博客

一篇好的博客页面应该具备如下内容:

  1. 便于浏览者扫描页面,以决定是否值得阅读这篇日志

  2. 博客应该鼓励交流,提供很好的评论功能,Traceback功能等

  3. 一个好的博客设计应该能够诱使人们阅读更多的相关日志

第二十三章,邀请讨论

不要对你的用户的评论进行审核和删除等等,就是不要有“河蟹”!

第二十四章,得到一个好的评分

很多时候,我们都力求简化用户的操作流程,但是带来的代价是交互更加的难以理解

因为简化的隐藏前提是我们认为用户知道其中的某一项是什么样了,于是我们就将它进行简化甚至忽略

但是这样的简化后的结果,对于网站的初级用户而言,门槛就会比较高,他们无法理解这样是为什么

所以在一些站点设计的时候,需要考虑面向的用户群,有些时候,效率不一定是所有问题的答案,我们的交互确实需要更多的步骤

但是我们的每个步骤都非常清晰,有反馈!

第二十五章,让RSS更有意义

很多人不知道RSS是个神码玩意,可以用Subscribe来替代它!

告诉用户,你可以复制这个地址到你的阅读器里头

第二十六章,自定义标签

标签是一个伟大的发明,但是也是一个高级的玩意

高级意味着不是每个用户都知道它,都了解它的作用和怎么用

第二十七章,通过拖放来组织信息

用户的交互过程可以细分成很多个步骤,每个步骤都需要系统去告诉用户,你当前处在什么状态,你可以干些什么事情!

比如拖拽的交互,有一个drag的动词提示,鼠标放上去会变成移动的光标

当开始拖动的时候,目的地会出现需框,表示移动的过程和预期目标

当拖动完成之后,需框消失,页面布局固定,这是一个结果的反馈状态

第二十八章,用系统通知来管理中断

当你的站点要发布更新和升级的时候,应该先发布消息给你的用户,让他们知道系统都在进展些什么

让用户对接下来的变化有心里准备

第二十九章,退出

对于封闭试的系统(指的是必须登录才能看到站点的内容)

用户推出之后,最好形成一个闭环,再跳回登录页面,并且在登录页面做一些网站的推广和营销的信息

吸引用户再次登录回归

第三十章,抹去那些尘封的用户

当用户已经很久没有登录,没有使用你的站点的时候

可以发一封邮件去提醒,邮件的内容尽量做到委婉,客气,幽默,不要有那些死板,推广,营销的语气

第三十一章,由他们去吧

当用户真的选择离开,销毁帐号的时候,也需要提供他们方便,并做最后的挽留

个人总结

这本书写的还是非常不错的,尽管书中提到的绝大部分的想法和方案都是我已经知晓并且付诸实践过一段时间了

但是尽管我知道这些解决方案,却仍然无法像作者那样提升到理论层次,并且能够举一反三地去思考同样类似的潜在影响

比如suggestion的负面作用,是我一直没有考虑到的

比如用户是使用者,产品是工具的这种意识,也是我这个技术开发人员所欠缺的

这本书给我带来的价值可能不是知识点和视野的开阔,更多的是梳理,让我可以将我一直依赖脑子中的想法和实践的方式进行归类整理

让我知道我为什么是这么认为的,我这样做到依据又是什么

至少我现在可以更加系统化的去思考我以前做过的产品,是否有书中提到的那些漏洞,又有哪些地方想的是和作者一样的

甚至,有哪些地方比作者想的还要周全

用户体验设计,是一个很复杂的东西,而却不是一个交互设计师,我是个码农!

在做一个产品的时候,不同的角色有不同的立场和观点

PM希望能够体现出更多的信息,一个很小的空间,希望能塞它个满满的,恨不得把我们所有的东西都告诉用户

交互设计师会告诉我们这样的信息组织会很乱,应该按重要和次要进行排列……

视觉设计师则抱怨说这样的页面会很ugly,我们需要留白!

我作为一个前端攻城师,心里只能默默的流泪,当内容超长的时候,是截断呢还是折行~~~

本文到此结束

后续会继续学习和交互设计相关的知识,再进行补充

,

Javascript正则表达式匹配换行

归类于JAVASCRIPT 参与评论

今天在coding的时候,遇到这样一个情况,读入一个css文件,然后找到body属性

在它的值里头添加一些内容,如下

body {
    background: #fff;
    color: #000;
}

变成如下内容

body {
    direction: rtl;
    background: #fff;
    color: #000;
}

这个功能用php,python什么的来说易如反掌,可偏偏我必须要js来实现

js怎么能够读写文件?

这个偏离主题了,俺是基于Rhino开发的一个玩意,可以读写文件

和其它后端语言一样,把整个文件以字符串的形式读入,然后用正则进行匹配和替换

于是我很自然的写了如下代码

function filter(source) {
    return source.replace(/(body\s+)?\{([.*]+)\}/, function(all, property, value) {
            return property + '{'+ "direction: rtl\n" + value +'}';
        });
}

结果什么事情都没发生,我于是立马想到了.*不能匹配换行符\n,像php遇到这种情况直接加正则表达式修饰符就搞定

javascript也有对应的修饰符m,于是变成这样

function filter(source) {
    return source.replace(/(body\s+)?\{([.*]+)\}/m, function(all, property, value) {
            return property + '{'+ "direction: rtl\n" + value +'}';
        });
}

还是不能用,为了避开其它影响,开始做test

var str = "body {\nxxx\n}";
alert(/body\s+\{.*\}/m.test(str));

结果为false

好吧,.*不能匹配换行符,我就加入换行符,继续修改

var str = "body {\nxxx\n}";
alert(/body\s+\{[.\n]*\}/m.test(str));

结果还是false

杯具了,在搜索引擎上搜了老半天,找到了一个类似的情况,于是也找到了解决方案

var str = "body {\nxxx\n}";
alert(/body\s+\{[\s\S]*\}/m.test(str));

可是原因呢?

后来问来其它同学,才得知.在[]里头不能表示任意字符……

看来得把《权威指南》翻出来重新看看了

, ,

VIM插件开发-语法篇

归类于VIM 参与评论

变量

有两种类型的变量:

数值      32 位带符号整数。
字符串     NUL 结尾的 8 位无符号字符串。

它们之间会根据使用的情况自动转换。

要强制从字符串转换到数值,给它加零:

:echo "0100" + 0

注意 在命令

:if "foo"

里,”foo” 被转换成 0,也就是假值。要测试字符串非空,应该使用 strlen():

:if strlen("foo")

列表字典函数是不会自动转换的

如果你需要知道变量或表达式的类型,使用 |type()| 函数。

变量的类型是不能改变的,必须先使用unlet函数先销毁该变量

列表

列表是一系列的内容有序组合,列表的每个项可以是任何类型,并通过列表的索引项获取

列表中的项在任何位置都可以被添加或者删除

创建列表

可以通过中括号[]来创建一个列表

let mylist = [1, two, 3, "four"]
let emptylist = []

列表中的项可以是任何表达式,也可以是列表,一下创建了一个二维的列表

let nestlist = [[11, 12], [21, 22], [31, 32]]

列表最后的一个逗号会被忽略,即下面的列表其实只有4项

let mylist = [1, two, 3, "four",]

索引列表

可以通过列表的下表索引获取其中的某一项,索引下表从0开始

let item = mylist[0]        " get the first item: 1
let item = mylist[2]        " get the third item: 3

如果是二维的列表,同样的

let item = nestlist[0][1]   " get the first list, second item: 12

如果索引是复数,则从列表的尾部开始往前索引

:let last = mylist[-1]      " get the last item: "four"

为了避免当索引的下标不在列表的索引范围内导致出错的问题,推荐使用get()函数来获取某一项的值

echo get(mylist, idx)
echo get(mylist, idx, "NONE")

如果索引项不存在,将返回0或者你设置的默认值,get方法的参数如下

get(列表,索引值[, 如果没有找到返回的默认内容])

列表拼接

列表可以通过+拼接在一起

let longlist = mylist + [5, 6]
let mylist += [7, 8]

列表裁剪

如果要获取列表的某一段内容,可以通过指定起始下标和终止下标,中间以冒号隔开,如下

let shortlist = mylist[2:-1]    " get List [3, "four"]

如果不指定第一个下标,则默认为0,不指定终止下标则默认表示到列表末尾

let endlist = mylist[2:]    " from item 2 to the end: [3, "four"]
let shortlist = mylist[2:2] " List with one item: [3]
let otherlist = mylist[:]   " make a copy of the List

如果起始下标大于终止下标,将返回一个空的列表,并不会发生错误

如果终止下标大于列表的最大长度,则返回列表的最后一个内容为止

let mylist = [0, 1, 2, 3]
echo mylist[2:8]        " result: [2, 3]

列表引用

如果把一个名称为aa的列表赋值到另外一个变量bb,则两个都指向同一个列表对象,即传址引用

因此,如果在bb中改变了列表中的一个值,aa中的内容也相应会被改变

let aa = [1, 2, 3]
let bb = aa
call add(aa, 4)
echo bb
//output [1, 2, 3, 4]

如果不想要这样的结果,仅仅是想拷贝一份列表的内容,可以使用copy()函数,也可以用[0:length]这样的方式

但是这个是一个浅复制过程,即只拷贝类型是字符串,数值等传值引用的类型内容,如果列表的某一项是还是列表,将又是另外一个引用

关于钱复制深复制的概念不在这里详述

于是一个保险的方法是使用深度复制的方法deepcopy()

要判断两个列表是否指向同一个列表对象,可以是用is操作符,isnot做相反的工作

可以用==比较两个列表的每项值是否相等

需要注意的是,在使用==比较两个列表的时候,当比较列表对应项时,不会做类型自动转换

列表unpack

主要是取出列表的对应项数据,采用如下方式

let [var1, var2] = mylist

如果变量的数量和列表的长度不符合,会发生错误,于是可以将剩余的内容复制到另外一个列表的做法

let [var1, var2; rest] = mylist

该行的作用等同于

let var1 = mylist[0]
let var2 = mylist[1]
let rest = mylist[2:]

修改列表内容

可以通过修改一个变量值的方式修改列表的某一项的值

let list[4] = "four"
let listlist[0][3] = item

如果要改变列表的某一段内容,通过索引来指定范围

let list[3:5] = [3, 4, 5]

如果要添加或者删除列表元素,必须通过相应的函数来完成,如下

call insert(list, 'a')      " 在列表前添加 'a'
call insert(list, 'a', 3)   " 在列表的下表3项前添加 'a'
call add(list, "new")       " 列表后面添加字符串
call add(list, [1, 2])      " 列表后面添加一个子列表
call extend(list, [1, 2])   " 将列表扩展出两项
let i = remove(list, 3)     " 删除第3项
:unlet list[3]              " 同上
let l = remove(list, 3, -1) " 从第3项一直删除到最后
:unlet list[3 : ]           " 同上
call filter(list, 'v:val !~ "x"')  " 删除一个包含'x'的项

列表排序

call sort(list)     " 按字母表排序
call reverse(list)      " 反转现有的列表顺序

循环输出

可以通过for来循环输出一个列表的每个项

for item in mylist
   call Doit(item)
endfor

待续…

, ,

将Ubuntu11.04安装到U盘上

归类于LINUX 参与评论

为了提前体验Ubuntu11.04的unity桌面环境,但是目前natty只发布到beta2

不敢轻易将主操作系统升级,于是打算将它装到u盘上先体验一下!

准备

  1. 官网上下载最新的iso文件

  2. 安装UNetbootin,可以从这里下载

  3. 准备两个U盘,一个至少1G,用于制作启动盘,一个至少4G用户安装系统

安装

  1. 插入制作启动用的U盘

  2. 打开UNetbootin,按照下图配置相应内容,注意选择你插入U盘盘符,点击确定

img

  1. 待完成之后,插入需要安装到的U盘,重新启动

  2. 设置bios,从U盘启动,则默认会出UNetbootin的引导选择界面,选择Install Ubuntu

  3. 之后的内容就和普通安装Ubuntu的过程一致了,只是注意在选择安装硬盘的地方一定记得选择后来插入的U盘的盘符,一般为/dev/sdc

  4. 安装的速度比普通硬盘的安装速度要慢,完成之后重新启动,就可以进入U盘中的系统了,迫于IO的速度,这样的系统还是不适合使用,哎

有关Ubuntu下akregator崩溃的问题

归类于LINUX 参与评论

背景介绍

我一直在用Ubuntu的akregator作为RSS的客户端,它基于KDE桌面环境

但是在Gnome下也是非常好用

但是不想,前一段时间突然就崩溃了,每次打开都报崩溃的bug,无法使用了

最后导致我去使用了一个叫Lifeera Read的阅读器作为替代,但是还是怀念Akregator

在网上搜了好久,终于找到了一个workround…

原文见这里

解决方案

  1. 进入akregator的文章目录

    ~/.kde/share/apps/akregator/Archive/archive
    
  2. 里头是一些mk4文件

  3. 把这些文件move走

  4. 重新启动akregator

  5. o了

不过这样会导致之前的内容丢失,如果你足够耐心的话,可以把刚才move走的文件一个一个弄回来,然后看是哪个文件引起的软件崩溃……

,

VIM插件开发实战–TangramComplete

归类于VIM | WEB DESIGN 参与评论

前言

本文介绍如何自行开发一个类似vim的autocomplete的插件,实现自己特定的自动补全提示

背景介绍

VIM的内置补全方式有很多种,更加详细的内容这篇文章讲述的非常清楚

需求

这里以我在开发Tangram开源JS框架的自动补全插件的过程为例子来描述整个插件的开发过程。

Tangram是baidu前端团队开发的一个优秀的前端Javascript基础库,更多它的内容请移步这里

要实现的内容当输入baidu或者T命名空间的时候,可以出对应的函数名和相关参数,示例图如下:

img

设计实现

总体思路

  1. 首先将所有Tangram的所有函数罗列出来,存到字典中去

  2. 监听输入事件,读取光标所在位置的前几个字符

  3. 判断该字符是否匹配baidu名称空间

  4. 如果匹配,则将该字符作为检索词,遍历字典,把符合匹配的结果作为下拉列表显示出来

实现

字典

定义一个数组,VIM的变量分好多种,这里用全局变量来存

let g:tangram_dictionary = []

数组的每个key是一个数据对象,大概包含如下内容

  1. 函数名称

  2. 参数

  3. 类型

大概如下

{
    'word' : 'baidu.g',
    'menu' : '(element)',
    'kind' : 'Function'
}

至于key的名称为什么要叫word/menu/kind后面会在讲述

将数据加入到数组当中,可以用VIM的内置函数add

在vim中调用一个函数用call来实现,于是

call add(g:tangram_dictionary, {
        'word' : 'baidu.g',
        'menu' : '(element)',
        'kind' : 'Function'
    })

把Tangram库的所有函数,都按照这种方式添加到该数组对象当中,这,完全是体力活……

好了,第一步完成

监听输入事件

要监听输入事件,有两种方式

  1. VIM内置有很多事件,比如有一个叫CursorMovedI的事件,它表示在Insert模式下光标发生移动的事件

  2. 监听所有的字符输入,然后映射到一个函数触发的函数或者事件

然而,无论采用哪种方式,都离不开VIM的autocmd

autocmd是VIM的自动命令表示,简单说就是绑定事件,具体内容可以看我的另外一篇blog

这里采用第2种方案

代码如下

let g:tangram_mapping_driven = [
    'a','b','c','d','e','f','g',
    'A','B','C','D','E','F','G',
    'h','i','j','k','l','m','n','o','p',
    'H','I','J','K','L','M','N','O','P',
    'q','r','s','t','u','v','w','x','y','z',
    'Q','R','S','T','U','V','W','X','Y','Z',
    '.','<BS>']
]
for key in g:tangram_mapping_driven
    execute printf('au FileType javascript inoremap <silent> <buffer> %s %s<C-x><C-o><C-r>=tangramcomplete#onPopupPost()<CR>',key,key)

上面的代码做了两件事,定义一个数组,每个项是一个字符。遍历该数组,给每个字符绑定一个事件,该事件部分如下

%s %s<C-x><C-o><C-r>...

就是说,把key映射为key<C-x><C-o><C-r>

这里要重点说一下映射后面的内容

全能补全

全能补全是VIM补全中的一种,通过omnifunction来实现,该补全通过命令

<C-x><C-o>

来实现,于是刚才上面的代码的含义就很明显了:

按字典里头的字符健,就映射为该字符输入的同时也输入<C-x><C-o><C-r>,于是就触发omnifunction…

omnifunc是VIM的内置函数,将它映射为我们自定义的函数

autocmd FileType javascript set omnifunc=tangramcomplete#CompleteTangram

这行代码的意思是当遇到文件类型是javascript的文件时,就把omnifunc函数映射为自定义的函数tangramcomplete#CompleteTangram

好了,到现在为止,当输入一个字符时候,就会触发omnifunc函数,我们的第2个步骤已经完成一半了

tangramcomplete CompleteTangram函数

omnifunc映射的函数和一般函数不大一样

它会被调用两次!它有两个参数

function tangramcomplete#CompleteTangram(findstart, base)
    // function body...
endfunction

当第一次调用的时

findstart = 1
base      = ''

此时返回的值必须是一个数值,表示补全的起始位置,必须是一个介于0到当前列的值之间的数字

于是这个返回值到当前列之间的字符将会被补全的第一条内容替换

当此函数被第2此调用的时候

findstart  = 0
base       = 第一次调用返回的值与当前列之间的字符内容

此时函数返回一个数组,该数组里头包含了需要在下拉列表中显示的每个项内容

了解了这个函数能实现的功能之后,我们的思路便是这样:

  1. 在第一次执行的时候,把需要补全替换的字符位置给算出来

  2. 在第二次执行的时候,把刚才的字符放到字典g:tangram_dictionary中去遍历,找到匹配的项放到数组中返回,即可实现需要的效果

函数大体如下

function tangramcomplete#CompleteTangram(findstart, base)
    // 如果a:findstart 不为0,即是第一次调用
    if a:findstart
        let s:line = getline('.')
        let s:start = col('.') - 1
        let s:compl_begin = col('.') - 2
        // 这里实现的是从当前光标位置不断的往前查找,直到遇到一个非单词字符为止
        // 于是返回这个非单词字符所在的位置
        while s:start >= 0 && s:line[s:start - 1] =~ '\%(\k\|-\|\.\)'
            let s:start -= 1
        endwhile
        let b:compl_context = s:line[0:s:compl_begin]
        return s:start
    endif

endfunction

当第2此调用此函数,base的内容即是第1次调用返回的数值与当前列之间的字符串

function tangramcomplete#CompleteTangram(findstart, base)
    // 如果a:findstart 不为0,即是第一次调用
    if a:findstart
        ....
    endif

    let s:line = a:base
    let s:result = []

    // 如果字符串中包含需要补全的关键词
    let s:haskeyword = match(s:line, 'baidu\|T')
    if s:haskeyword > -1
        // 把关键词部分匹配出来
        let s:tangram_keyword = matchstr(s:line, '\s*baidu\(\.\|[a-zA-Z]\)*$')
        // 遍历字典
        for m in g:tangram_dictionay
            // 如果字典的word中内容包含了需要匹配的字符串,就表示符合要求
            if m['word'] !~ '^'.s:tangram_keyword
                call add(s:result, m)
            endif
        endfor
        return s:result
    endif

endfunction

至此,步骤3和步骤4的功能都在此函数中实现了

通过上面的代码,就基本能够实现需求了,但是还不够完整,比如omnifunc匹配的结果会默认用第一条结果替换用户的输入部分

这产生的问题就是如果第一条其实不满足你的要求,你就必须按退格健来删除了~~

这个问题个人觉得是vim的omnifunc的实现不好,因为没有默认只显示结果而不执行替换的行为实现,所以我们得山寨一下

这里参考了acp.vim插件的实现,通过按键命令

<C-p>

来实现不自动替换的功能要求

我已经把代码芳到github中进行维护了,点击这里查看

参考资料

在开发过程中遇到了很多问题,第一次接触VIM的插件开发,基本不懂,从网络上其它文章中学到了很多

  1. VIM DOC; 自带的doc,不用说,最完整的参考手册

  2. vi/vim使用进阶: 自动补全

  3. 为 Vim 编辑器开发定制插件

,

VIM插件开发学习笔记

归类于VIM 一条评论

自动命令

定义一些自己需要的命令操作,格式如下

:autocmd [group] {events} {file_pattern} [nested] {command}

[group] 可选,用来管理和调用命令

{events} 是触发命令的事件,见下面的主题

{file_pattern} 文件命令,常用通配符表示,如*.txt;还可以是模式列表,以逗号隔开,如*.c,*.h

[nested] 运行自动命令的嵌套

{command} 是要被执行的命令

要删除一个自动命令,用如下格式

:autocmd! FileWritePre *

autocmd后面加!号,不要有{command}

是用来给多自动命令进行归类,然后可以用来删除某个组的所有自动命令

可以通过augroup命令来定义组

:augroup groupname
:   autocmd ...
:   autocmd ...
:augroup END

上面代码和下面效果是一致的

:autocmd groupname ...
:autocmd groupname ...

这样,如果要删除该组的所有命令,则

:autocmd! groupname

事件

事件是在vim编辑环境中设定的一系列状态,比如

BufReadPost

表示在文件被调入编辑环境的时候,会派发这样一个事件,于是就可以利用这些事件来进行一些相应的状态操作

比如为对应的文件设定正确的语法文件

:autocmd BufReadPost *.gsm set filetype=asm

上面的命令做了这样一个事情:

当开始编辑后缀是gsm的文件时,给它设定文件类型为asm

以下是一些常用的事件

|* 名称 | 描述 *| | BufNewFile | 开始编辑尚未存在的文件时。可用来读入骨架文件 | | BufReadPre | 开始编辑新的缓冲区并把文件读入缓冲区前。如果文件还不存在,不会有此事件。| | BufRead/BufReadPost | 开始编辑新的缓冲区并把文件读入缓冲区后,执行模式行之前。| | BufReadCmd | 开始编辑新的缓冲区前。应执行把文件读入缓冲区的操作。| | bufwritepost | 当文件被修改时生效 |

更多可以通过下面命令调出vim的help文档

help autocmd-events

基本语法

变量赋值

给某个变量赋值

let {变量名} = {值/表达式}

设定局部变量加上s:前缀

let s:count = 10

其它类型

b:name      缓冲的局部变量
w:name      窗口的局部变量
g:name      全局变量 (也用于函数中)
v:name      Vim 预定义的变量

删除变量用

unlet s:count

查询特殊变量

help expr-quote

循环

while {条件}
    语句
endwhile

表达式

$NAME           环境变量
&name           选项
@r              寄存器

条件

if {condition}
    语句
endif

逻辑操作

匹配

a =~ b  // 匹配
a !~ b  // 不匹配

例子

if str =~ " "
  echo "字符串包括空格"
endif
if str !~ '\.$'
  echo "字符串以句号结尾"
endif

在比较时,#表示大小写敏感,?表示忽略大小写

==?     // 两者是否相等,忽略大小写
!~#     // 是否被匹配,考虑大小写

更多字符串比较和匹配的,可以

help expr-==

函数

内置函数列表

help functions
help function-list

范围,函数定义时给出一个range关键字,该函数默认会两个参数

a:firstline
a:lastline

调用是给该函数传递起始行和结束行

function Count_words() range
  let n = a:firstline
  let count = 0
     while n <= a:lastline
        let count = count + Wordcount(getline(n))
     endwhile
   echo "found " . count . " words"
endfunction

你可以这样调用上面的函数:

    :10,30call Count_words()

这个函数将被调用一次并显示字数。

函数的参数支持可变

function show(start, ...)

通过a:1获取第一个参数,a:n获取第n个参数,a:0表示参数个数

删除函数用

delfunction name

提高PHP编程效率的53个要点

归类于PHP 参与评论

本文转载至网络,非原创

  1. 如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍。

  2. $row[’id’] 的速度是$row[id]的7倍。

  3. echo 比 print 快,并且使用echo的多重参数(译注:指用逗号而不是句点)代替字符串连接,比如echo $str1,$str2。

  4. 在执行for循环之前确定最大循环数,不要每循环一次都计算最大值,最好运用foreach代替。

  5. 注销那些不用的变量尤其是大数组,以便释放内存。

  6. 尽量避免使用__get,__set,__autoload。

  7. require_ once()代价昂贵。

  8. include文件时尽量使用绝对路径,因为它避免了PHP去include_path里查找文件的速度,解析操作系统路径所需的时间会更少。

  9. 如果你想知道脚本开始执行(译注:即服务器端收到客户端请求)的时刻,使用$_SERVER[‘REQUEST_TIME’]要好于time()。

  10. 函数代替正则表达式完成相同功能。

  11. str_replace函数比preg_replace函数快,但strtr函数的效率是str_replace函数的四倍。

  12. 如果一个字符串替换函数,可接受数组或字符作为参数,并且参数长度不太长,那么可以考虑额外写一段替换代码,使得每次传递参数是一个字符,而不是只写一行代码接受数组作为查询和替换的参数。

  13. 使用选择分支语句(译注:即switch case)好于使用多个if,else if语句。

  14. 用@屏蔽错误消息的做法非常低效,极其低效。

  15. 打开apache的mod_deflate模块,可以提高网页的浏览速度。

  16. 数据库连接当使用完毕时应关掉,不要用长连接。

  17. 错误消息代价昂贵。

  18. 在方法中递增局部变量,速度是最快的。几乎与在函数中调用局部变量的速度相当。

  19. 递增一个全局变量要比递增一个局部变量慢2倍。

  20. 递增一个对象属性(如:$this->prop++)要比递增一个局部变量慢3倍。

  21. 递增一个未预定义的局部变量要比递增一个预定义的局部变量慢9至10倍。

  22. 仅定义一个局部变量而没在函数中调用它,同样会减慢速度(其程度相当于递增一个局部变量)。PHP大概会检查看是否存在全局变量

  23. 方法调用看来与类中定义的方法的数量无关,因为我(在测试方法之前和之后都)添加了10个方法,但性能上没有变化。

  24. 派生类中的方法运行起来要快于在基类中定义的同样的方法。

  25. 调用带有一个参数的空函数,其花费的时间相当于执行7至8次的局部变量递增操作。类似的方法调用所花费的时间接近于15次的局部变量递增操作。

  26. Apache解析一个PHP脚本的时间要比解析一个静态HTML页面慢2至10倍。尽量多用静态HTML页面,少用脚本。

  27. 除非脚本可以缓存,否则每次调用时都会重新编译一次。引入一套PHP缓存机制通常可以提升25%至100%的性能,以免除编译开销。

  28. 尽量做缓存,可使用memcached。memcached是一款高性能的内存对象缓存系统,可用来加速动态Web应用程序,减轻数据库负载。对运算码 (OP code)的缓存很有用,使得脚本不必为每个请求做重新编译。

  29. 当操作字符串并需要检验其长度是否满足某种要求时,你想当然地会使用strlen()函数。此函数执行起来相当快,因为它不做任何计算,只返回在zval 结构(C的内置数据结构,用于存储PHP变量)中存储的已知字符串长度。但是,由于strlen()是函数,多多少少会有些慢,因为函数调用会经过诸多步骤,如字母小写化(译注:指函数名小写化,PHP不区分函数名大小写)、哈希查找,会跟随被调用的函数一起执行。在某些情况下,你可以使用isset() 技巧加速执行你的代码。

(举例如下)

if (strlen($foo) < 5) { echo “Foo is too short”$$ }

(与下面的技巧做比较)

if (!isset($foo{5})) { echo “Foo is too short”$$ }

调用isset()恰巧比strlen()快,因为与后者不同的是,isset()作为一种语言结构,意味着它的执行不需要函数查找和字母小写化。也就是说,实际上在检验字符串长度的顶层代码中你没有花太多开销。

  1. 当执行变量$i的递增或递减时,$i++会比++$i慢一些。这种差异是PHP特有的,并不适用于其他语言,所以请不要修改你的C或Java代码并指望它们能立即变快,没用的。++$i更快是因为它只需要3条指令(opcodes),$i++则需要4条指令。后置递增实际上会产生一个临时变量,这个临时变量随后被递增。而前置递增直接在原值上递增。这是最优化处理的一种,正如Zend的PHP优化器所作的那样。牢记这个优化处理不失为一个好主意,因为并不是所有的指令优化器都会做同样的优化处理,并且存在大量没有装配指令优化器的互联网服务提供商(ISPs)和服务器。

  2. 并不是事必面向对象(OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很多内存。

  3. 并非要用类实现所有的数据结构,数组也很有用。

  4. 不要把方法细分得过多,仔细想想你真正打算重用的是哪些代码?

38、当你需要时,你总能把代码分解成方法。

39、尽量采用大量的PHP内置函数。

40、如果在代码中存在大量耗时的函数,你可以考虑用C扩展的方式实现它们。

41、评估检验(profile)你的代码。检验器会告诉你,代码的哪些部分消耗了多少时间。Xdebug调试器包含了检验程序,评估检验总体上可以显示出代码的瓶颈。

42、mod_zip可作为Apache模块,用来即时压缩你的数据,并可让数据传输量降低80%。

43、在可以用file_get_contents替代file、fopen、feof、fgets等系列方法的情况下,尽量用file_get_contents,因为他的效率高得多!但是要注意file_get_contents在打开一个URL文件时候的PHP版本问题;

44、尽量的少进行文件操作,虽然PHP的文件操作效率也不低的;

45、优化Select SQL语句,在可能的情况下尽量少的进行Insert、Update操作(在update上,我被恶批过);

46、尽可能的使用PHP内部函数(但是我却为了找个PHP里面不存在的函数,浪费了本可以写出一个自定义函数的时间,经验问题啊!);

47、循环内部不要声明变量,尤其是大变量:对象(这好像不只是PHP里面要注意的问题吧?);

48、多维数组尽量不要循环嵌套赋值;

49、在可以用PHP内部字符串操作函数的情况下,不要用正则表达式;

50、foreach效率更高,尽量用foreach代替while和for循环;

51、用单引号替代双引号引用字符串;

52、“用i+=1代替i=i+1。符合c/c++的习惯,效率还高”;

53、对global变量,应该用完就unset()掉;

,

Ubuntu下通过samba命令挂载远程目录

归类于LINUX 参与评论

在没有FTP的情况,远程服务器如果开通了Samba服务

可以通过smb的命令进行远程目录操作

windows下,则通过资源管理器的

工具 => 映射网络驱动器

然后填入服务器的ip,路径,再是用户名和密码,就可以把远程目录作为本地的一个驱动器来存储了

如果是ubuntu,则执行如下命令

smbmount //192.168.100.53/共享目录/ ~/local/ -o username=smb,password=ice,iocharset=utf8

填入对应的ip,共享目录,用户明和密码

执行成功后就会在home目录下生成local目录,里头就是远程的文件了

非常方便!

顶部