编辑器撤销(undo)与重做(redo)插件的设计与实现
,涉及三个方面:
1. 编辑器状态定义
不仅要包含当前编辑器的内容(body.innerHTML),还要考虑编辑区域用户的选中状态,用户选中每个区域进行高亮等操作,则操作前要把当前选中区域以及内容html都要保存下来:

/**
* 当前编辑区域状态,包括html与选择区域
* @param editor
*/
function Snapshot(editor) {
var contents = editor.getData(),selection = contents && editor.getSelection();
//内容html
this.contents = contents;
//选择区域书签标志
this.bookmarks = selection && selection.createBookmarks2(true);
}
2.编辑状态历史的管理
历史管理器维护着编辑状态的栈数据, 其他插件通过触发编辑器的 save 与 restore 事件来通知历史管理器更新内部的状态。
当 save 操作时,历史管理器将当前状态添加到编辑状态栈即可,并更新内部游标。

restore 操作时,需要利用管理器内部的游标来在状态栈间前(undo)后(redo)游走,用游标处的状态替换当前状态。

注意点在于,当进行了re/undo操作后,再进行save状态保存,需要对状态栈进行清理,将当前游标以后的历史状态删掉,防止出现历史分支:

另一点在于:对于键盘的连续输入,可buffer处理,只有当空闲一定时间后才进行保存,避免细微冗余信息。
/**
* 通过编辑器的save与restore事件,编辑器实例的历史栈管理,与键盘监控
* @param editor
*/
function UndoManager(editor) {
//redo undo history stack
/**
* 编辑器状态历史保存
*/
this.history = [];
this.index = 0;
this.editor = editor;
this.bufferTimer = new BufferTimer(500, this.save, this);
this._init();
}
S.augment(UndoManager, {
/**
* 监控键盘输入,buffer处理
* @param ev
*/
_keyMonitor: function (ev) {
//ctrl+z,撤销
//ctrl+y,重做
//其他可见字符buffer处理
},
_init: function () {
var self = this,
editor = self.editor;
//外部通过editor触发save|restore,管理器捕获事件处理
editor.on("save", function (ev) {
if (ev.buffer)
//键盘操作需要缓存
self.bufferTimer.run();
else {
//其他立即save
self.save();
}
});
//un/re do
editor.on("restore", this.restore, this);
self._keyMonitor();
//先save一下
self.save();
},
/**
* 保存历史
*/
save: function () {
//游标后面的历史抛弃
//超过占最大容量,shift队列出列
//当前状态和栈顶状态不同,入栈
//更新游标
//触发afterSave事件
},
/**
*
* @param ev
* ev.d :1.向前撤销 ,-1.向后重做
*/
restore: function (ev) {
//更新游标,用游标所在状态替换当前状态
}
});
3.工具栏 ui 与功能调用
RestoreUI 封装工具栏撤销与重做的功能与表现,使用 attribute 抽象出三状态按钮(可用并选中,可用,禁用),监听通过2触发的编辑器 afterSave,afterRestore 事件,通过游标位置和历史栈大小的比较,来决定redo ,undo按钮的禁用与可用状态.
/**
* save,restore完,更新工具栏状态
*/
editor.on("afterSave", this._respond, this);
editor.on("afterRestore", this._respond, this);
undo:当游标不在状态栈底部时可用(index>0&&history.length>0)
redo:当游标不在状态栈顶部时可用(index < history.length-1)
当点击工具栏按钮时,触发editor的restore事件,注意参数,redo为向后restore,undo为向前restore。
/**
* 触发重做或撤销动作,都是restore,方向不同
*/
self.el.on("click", function() {
editor.fire("restore", {
d:RedoMap[self.text]
});
});
4.整合
当编辑器实例生成后,全局空间事件通知undo/redo插件,插件对每个编辑器生成对应的历史管理器与工具栏按钮:
KE.on("instanceCreated", function(ev) {
var editor = ev.editor;
/**
* 编辑器历史中央管理
*/
new UndoManager(editor);
/**
* 撤销工具栏按钮
*/
new RestoreUI(editor, "undo");
/**
* 重做工具栏按钮
*/
new RestoreUI(editor, "redo");
});
本文详细阐述了编辑器撤销(undo)与重做(redo)插件的设计与实现过程,包括编辑器状态定义、编辑状态历史管理、工具栏UI与功能调用,以及整合步骤。重点介绍了如何通过事件监听和状态栈管理来实现撤销和重做功能,同时讨论了优化策略,如键盘输入的缓冲处理和历史状态的清理。
4251

被折叠的 条评论
为什么被折叠?



