8.3.3 扩展撤销栈以支持更复杂的用户操作(1)
现在,我们已经看到了一个实现撤销栈的简单示例,我们将再接再厉来处理更加复杂的例子。让我们往下进行吧,同时在服务器端引入Ajax和撤销功能。
1.问题
你需要为用户操作定制撤销功能,这远比简单文本输入复杂。和客户端一样,重设应用程序的状态将需要服务器的输入。
2.解决方案
我们在本例将重用之前实现的基本撤销栈对象,并为一个简单的图片编辑处理应用提供撤销功能。图片操作处理将在服务器发生,服务器把前一个状态下的图片快照、操作对象的引用和其他相关操作数据存储。图片编辑器的用户界面如图8-4所示。当前状态下的图片在页面中间,左侧是可用的操作列表,右侧是撤销堆栈对象的形象显示。
首先我们需要定义图片编辑器应用中的可用操作类型。这些操作将采用唯一整型值作为标识并将被服务端的代码引用。接下来要展示的例子的可用的操作列表如下所示:
|
| 图8-4 一个功能完整的图像编辑器,包含一组图像复制工具、 图像预览工具,还有一个可视化的撤销栈展现器 |
NO_OP(无操作)--用于初始状态。
INVERT--对图像的颜色值采取反色处理 。
POSTER (色调分离)--减少图像中的色阶(color level)数值。
BLUR--使用3x3卷积矩阵进行图像模糊 。
EDGE--采用高强度对比的图像区域检测。
SHARPEN--减弱图像的模糊度。
FLIP_H (水平翻转)--按Y轴翻转。
FLIP_V (垂直翻转)--按X轴翻转。
ROTATE--图片旋转(以度数)。
STRING--在指定的坐标处添加文本字符串。
CONVOLVE--允许用自定义的3x3卷积矩阵对图像进行卷积处理。
本示例需要编写大量客户端代码,因此我们先来分析介绍这部分内容。首先定义图片编辑处理操作的常量值,如下所示:
|
接下来对上个示例中的一般性的撤销栈对象进行初始化。还记得该栈对象接受一个回调函数作为参数吗?我们提供一个回调函数作为参数,该函数将负责更新屏幕右侧浏览历史面板的数据和从服务器加载处理过的图片。代码清单8-9显示了我们如何实现该部分程序。
代码清单8-9 创建复杂的撤销队列
|
|
代码清单8-9新添加的initialize()函数 需要实现几个功能。首先,我们必须初始化客户端的撤销栈 并传入自定义的回调处理函数引用。其次,我们需要通知栈对象忽略添加新撤销操作对象执行等价性检查 。该设置允许同一类型的图片操作同时执行两次--例如,多次对图片进行模糊处理。最后,我们需要在服务端初始化撤销栈并设置应用程序的初始状态 。
图片编辑处理函数 比前一个示例略显复杂。当一个实际操作执行时,我们不仅需要处理撤销/恢复,还不得不处理历史记录面板的状态 。此外,由于初步介绍了浏览历史面板,我们需要在没有遍历新选中的操作和前一个选中操作之间的情况下,能够激活任意条目 。
当转到一个新条目时,我们希望执行一个Ajax请求,使服务器更新服务端的撤销栈状态。我们定义了一个用于响应Ajax请求的回调函数,该函数简单地更新主要图片资源 。尽管这是简单的回调函数,通常可以将它内嵌到其他函数主体,但我们还是在此单独定义了一个函数以便在其他地方重用。
代码中还有另一个预定义的回调函数,在初始化撤销栈函数内发起Ajax请求过程中我们用到了它。我们将简要介绍addActionCallback()函数,它更棘手。
我们继续前进,然后介绍表单左侧的控件支持的几个操作背后的JavaScript代码。所有这些操作遵循类似的操作流程,散列化任意必需的参数,然后发起一个Ajax请求到服务器,由服务器执行图片操作。创建Ajax.Request的函数addAction(),如代码清单8-10所示。
代码清单8-10 addAction()函数
|
|
|
该函数的第一部分是一个case语句,它根据给定的操作类型提取所有相关的参数并拼装到一个叫做paramString的变量。注意有些操作不需要任何参数,例如invert和flip等。其他操作则相反,诸如convolve和rotate等操作需要若干参数。装配完参数,我们更新客户端撤销栈,然后发起一个请求到服务器,连同操作类型和所有参数都传入到该请求中。这将更新服务端的撤销栈。
| 回书目 上一节 下一节 |