时间:2015-03-17 作者:剧中人
近来小剧又在发疯似的造一个轮子:mditor一款轻量的markdown编辑器。既然是编辑器,肯定少不了撤销Ctrl+Z
和 重做Ctrl+Y
功能。今天小剧就来说一说我是如何处理撤销、重做功能的。
其实撤销与重做,只是围绕历史记录在做的两个常用功能,下面小剧用比较逗比科幻的角度来解释与历史记录相关的几个概念。
历史记录 ,其自身是一组数据,用于记录一定时间内的操作,你可以把它理解为你自己最近几年主要活动的时空轨迹。
撤销,回到到上一步的操作,一款现实生活绝对买不到的后悔药。是穿越时空的超能力,撤销可以带你回到你最想去更改的某个时间点,也可以抹去刚刚不小心犯下的错误。
重做,也称之为恢复,假如你只是想回到过去看一眼,那你还有机会选择重做立即回到未来,继续现在的生活。
最大历史记录,最多可允许恢复的历史记录步数。每时空旅行者的超能力都有限度,只能在一定范围内的时空游走。
这四个概念给了时空穿梭极大的自由度,但有一点需要注意,历史记录仅仅是一条单行的线,你可以在时间线上前后穿越。一旦你回到某一个过去,改变了某一件小事,之后的时空便不复存在。相信看过《蝴蝶效应》的小伙伴都会了解,这里没有平行世界的概念呦!
小剧的胡言乱语了这么多,用这个疯癫的逻辑建立一个历史记录的模型就不是难事了,代码祭出。
/**
* 历史记录
**/
function LOG(max){
//历史记录存储字段
this.data = [];
//当前所处在的时间点
this.current = -1;
//最大历史记录
this.maxLength = max || 20;
}
LOG.prototype = {
//新的一步
push: function(item){
//已经有新的改变,若有时间线后的操作全部作废
if(this.data.length > this.current + 1){
this.data.length = this.current + 1;
}
this.current++;
this.data.push(item);
//超出最大历史纪录数,删除最早的历史记录
if(this.data.length > this.maxLength){
this.data = this.data.slice( -this.maxLength);
this.current = this.maxLength - 1;
}
},
//撤销
undo: function(){
if(this.current < 0){
return null;
}
return this.data[--this.current];
},
//重做
redo: function (){
if(this.data.length > this.current + 1){
return this.data[++this.current];
}else{
return null;
}
}
};
有了这么一个简单的历史操作的类,就可以在自己的功能里包装历史记录功能了。规划好每次存储历史记录需要记下的字段,以及撤销、重做的时候,如何把存储的数据重新渲染到视图里。
/**
* getPosition 和 setPosition为获取、设置光标位置的方法,细节不具体展开
**/
var log = new LOG(20),
$textarea = $('textarea');
$textarea.on('change',function(){
//向历史记录列表追加记录
log.push({
selection : getPosition($textarea),
content: $(this).val()
});
});
$('.undo').click(function(){
//撤销
var step = log.undo();
writeStep(step);
});
$('.redo').click(function(){
//重做
var step = log.redo();
writeStep(step);
});
//绘制当前步骤
function writeStep(step){
if(!step || !step.content){
return
}
$textarea.val(step.content);
setPosition($textarea,step.selection[0],step.selection[1]);
}
PS:为什么要有最大历史记录的限制?
这个限制一般软件都会有,特别因为这里是JS,完全是内存存储,自然不允许无限制存储历史记录。