很久以前做过这个,最近有用到,在硬盘堆里翻了一下,终于找到了,记录在这里。
基于jquery的undo/redo设计思路:
1、建立一个命令管理类,里面有两个数组保存undo和redo的命令队列,有几个方法实现命令管理的操作,代码如下:
var commandManager=function(){
this.undoList=[];
this.redoList=[];
this.undoCount=20; //-1为无限
this.changed=null;
var that=this;
this.executeCommand=function(cmd){
cmd.execute();
that.undoList.unshift(cmd);
if (that.undoCount != -1 && that.undoList.length> that.undoCount) {
that.undoList.splice(that.undoList.length-1,1);
}
that.redoList=[];
if(that.changed!=null){
that.changed();
}
}
this.undo=function(){
if (that.undoList.length <= 0) {
return;
}
var cmd = that.undoList[0];
cmd.undo();
that.undoList.splice(0,1);
that.redoList.unshift(cmd);
if(that.changed!=null){
that.changed();
}
}
this.redo=function(){
if (that.redoList.length <= 0) {
return;
}
var cmd = that.redoList[0];
cmd.redo();
that.redoList.splice(0,1);
that.undoList.unshift(cmd);
if(that.changed!=null){
that.changed();
}
}
this.canUndo=function(){
return that.undoList.length>0;
}
this.canRedo=function(){
return that.redoList.length>0;
}
};
2、建立一个命令的基类,用于产生基本的命令操作,代码如下:
var baseCommand=function(){
var options=null;
this.setOptions=function(newOptions){
options=newOptions;
}
this.execute=function(){
console.log("未指定执行方法.");
}
this.undo=function(){
console.log("未指定撤销方法.");
}
this.redo=function(){
console.log("未指定重做方法.");
}
}
注意:里面有个 setOptions的方法,主要是大部分操作都需要临时保存一些信息用于定位组件和相关的操作信息,这里传一个json对象,里面放这些东西。
3、实例化一下这个命令管理类:
var commander=new commandManager();
4、做一个全局变量,保存是否是从undo/redo里面发送的命令,防止一些判断变更啊、刷新什么的导致的命令发送进入队列。
var isUndoRedoCommand=false;
5、用一些有变更事件的框架或者组件,会直接传递newCValue和oldValue什么的进来,比较容易管理值变化,当然如果没有就要在befor和after什么的事件里面写了,比较麻烦。我这里用了jquery easyUI里面的部件大部分都有change事件,里面会传递这些值过来。
<input class="easyui-textbox" type="text" name="seriesName" id="seriesName" value="" style="width:250px" data-options="prompt:'请输入系列名称...',label:'名称:',labelWidth:60,labelPosition:'before',labelAlign:'right',onChange:sentUpdateStyleCommand">
6、组件变化,写一个响应它的事件,并且用管理器执行它,进入队列。
function sentUpdateStyleCommand(newValue,oldValue){
if(!drawFlag || isUndoRedoCommand) return;
var updateStyleCommand=new baseCommand();
updateStyleCommand.execute=function(){
//这里执行变更导致的结果
}
var options={};
options.source=$(this)
options.newValue=newValue;
options.oldValue=oldValue;
updateStyleCommand.setOptions(options);
updateStyleCommand.undo=function(){
options.source.textbox("setValue",options.oldValue);
//这里写撤销操作
}
updateStyleCommand.redo=function(){
options.source.textbox("setValue",options.newValue);
//这里写重做操作
}
//在管理器中执行
commander.executeCommand(updateStyleCommand);
}
7、菜单上做两个按钮,响应事件
//撤销
function undo(){
isUndoRedoCommand=true;
commander.undo();
isUndoRedoCommand=false;
}
//重做
function redo(){
isUndoRedoCommand=true;
commander.redo();
isUndoRedoCommand=false;
}
8、页面初始化,加个事件侦听,控制按钮启用和禁用
commander.changed=function(){
if(commander.canRedo()){
$('#redoButton').linkbutton('enable');
}else{
$('#redoButton').linkbutton('disable');
}
if(commander.canUndo()){
$('#undoButton').linkbutton('enable');
}else{
$('#undoButton').linkbutton('disable');
}
}