在javascript中用command模式模拟做undo和redo

emu 2005-09-28 06:23:59
好像有很久没有回来讨论了。这帖可以看成是《在javascript中用command模式模拟多线程模拟多线程》的续篇吧。

早上看了水晶龙的一个undo的例子,其中虽然有一些command模式的思想,但是用的好像不够正宗。其实用command模式实现undo和redo应该还是比较容易的,做个例子练练手:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE> test command </TITLE>
<META NAME="Author" CONTENT="emu">

<SCRIPT LANGUAGE="JavaScript">
<!--
var actionStack = [];//操作栈
var actionIndex = 0;//操作栈中当前操作的指针

//----------------------- command对象基类 ------------------------------
function BaseAction(){
}
BaseAction.prototype.exec=function(){
actionStack[actionIndex++] = this;
}
BaseAction.prototype.undo=function(){
alert("此操作未定义相应的undo操作");
}
BaseAction.prototype.redo=function(){
alert("此操作未定义相应的redo操作");
}

//----------------------- move操作的command对象 ------------------------------
function MoveAction(elm){
this.oldX = elm.oldX;
this.oldY = elm.oldY;
this.newX = elm.newX;
this.newY = elm.newY;
this.sourceElement = elm;
this.status = "done";
}
MoveAction.prototype = new BaseAction();
MoveAction.prototype.undo=function(){
if (this.status != "done") return;
this.sourceElement.style.left = this.oldX;
this.sourceElement.style.top = this.oldY;
this.status = "undone";
}
MoveAction.prototype.redo=function(){
if (this.status != "undone") return;
this.sourceElement.style.left = this.newX;
this.sourceElement.style.top = this.newY;
this.status = "done";
}
//----------------------- change操作的command对象 ------------------------------
function ChangeAction(elm){
this.sourceElement = elm;
this.oldValue = elm.defaultValue;
this.newValue = elm.value;
elm.defaultValue = elm.value;
this.status = "done";
}
ChangeAction.prototype = new BaseAction();
ChangeAction.prototype.undo = function(){
if (this.status != "done") return;
this.sourceElement.value = this.sourceElement.defaultValue = this.oldValue;
this.status = "undone";
}

ChangeAction.prototype.redo = function(){
if (this.status != "undone") return;
this.sourceElement.value = this.newValue;
this.status = "done";
}
ChangeAction.prototype.exec=function(){
actionStack[actionIndex++] = this;
}

//--------------------- 全局函数 ----------------------------
function undo(){
if (actionIndex>0){
actionStack[--actionIndex].undo();
}
}
function redo(){
if (actionIndex<actionStack.length){
actionStack[actionIndex++].redo();
}
}

function checkMouseMove(){
if (window.activeElement){
var elm = window.activeElement;
elm.style.left = event.x-elm.innerX;
elm.style.top = event.y-elm.innerY;
}
}
function releaseMouse(){
if (window.activeElement){
activeElement.newX = event.x;
activeElement.newY = event.y;
new MoveAction(activeElement).exec();
window.activeElement = null;
}
}
function drag(){
if (event.button!=2) return;
var elm = event.srcElement;
window.activeElement = elm;
elm.oldX = elm.offsetLeft;
elm.oldY = elm.offsetTop;
elm.innerX = event.x - elm.oldX;
elm.innerY = event.y - elm.oldY;
}

function changeValue(){
new ChangeAction(event.srcElement).exec();
}
//-->
</SCRIPT>
</HEAD>

<BODY onmousemove="checkMouseMove()" onmouseup="releaseMouse()" oncontextmenu="return false">
<input type=button onclick=undo() value=undo>
<input type=button onclick=redo() value=redo>
<input value="drag me" onmousedown="drag()" onchange="changeValue()" style="position:absolute;left:150;color:blue">
<input value="drag me" onmousedown="drag()" onchange="changeValue()" style="position:absolute;left:350;color:green">
<input value="drag me" onmousedown="drag()" onchange="changeValue()" style="position:absolute;left:550;color:violet">
</BODY>
</HTML>

用鼠标右键拖动输入框,或者直接修改输入框的内容,可以一一 undo 再 redo 。

看来效果还不错。秋水说有bug,不过我没有重现出来呢?
...全文
434 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
emu 2006-01-10
  • 打赏
  • 举报
回复
没什么人捧场啊
cxz7531 2005-09-29
  • 打赏
  • 举报
回复
一般矢量图形都可以用vml来画。

偶在VML编辑器中做过undo和redo。
原理很简单:
把每一步操作的内容全部放进一个数组,按照数组一步步回退和前进就行了。
为了防止数组内容膨胀,限制10步
emu 2005-09-29
  • 打赏
  • 举报
回复
画饼图可以用vml或者mschart啊,光用js也不是不行,不过用的人比较少了。
oldmht 2005-09-28
  • 打赏
  • 举报
回复
好像不需要office支持,刚看了一个,画饼图的,是vml吧
oldmht 2005-09-28
  • 打赏
  • 举报
回复
vml也没研究过,真是高人啊,赫赫 做涂鸦板么,最好能不用office支持做出来
emu 2005-09-28
  • 打赏
  • 举报
回复
唉,我只是在谈redo和undo的实现,其实水晶龙想在vml里面做undo和redo……

改正后的代码在 http://www.blogjava.net/emu/archive/2005/09/28/14314.html
oldmht 2005-09-28
  • 打赏
  • 举报
回复
输入框移动的话,一般模式,系统的撤销不涉及到这个东西,但是编辑模式可以,不过我不知道移动有什么太大的用处,迫切需要的话,还不如写组件

光是输入的字符改来改去的恢复,用系统自带的也就可以了吧

没研究过这些,望LZ指教~~^_^
emu 2005-09-28
  • 打赏
  • 举报
回复
是的,不好意思坐标算错了。
activeElement.newX = event.x; -->> activeElement.newX = event.x-activeElement.innerX;
activeElement.newY = event.y; -->> activeElement.newX = event.x-activeElement.innerY;

oldmht 2005-09-28
  • 打赏
  • 举报
回复
第一个输入框拖到左上,假设坐标(0,0),放开,拖到左中,假设坐标(0,300),然后点"Redo","undo","redo",看看现在跑哪里去了
emu 2005-09-28
  • 打赏
  • 举报
回复
JK手脚真快,一下发现了两个bug:
1 从一个textbox把文字拖放到另一个textbox的情况下不能undo,因为ie没有触发两个onchange事件给我,不能全怪我啊。
2 修改值后我的undo功能和IE自带的ctrl-z有冲突,呵呵。

87,907

社区成员

发帖
与我相关
我的任务
社区描述
Web 开发 JavaScript
社区管理员
  • JavaScript
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧