2009 年 10 月 22 日
本文介绍了使用 Dojo 工具包中的 DojoX GFX 在网页上进行绘图操作的方法。通过简单的示例说明了使用 DojoX GFX 绘图的基本概念和相关方法。
Dojo 工具包和 DojoX GFX
在在几乎所有的页面都需要具备友好有丰富的用户体验的 Web2.0 时代,Ajax 几乎已经成为了每个网络应用的标准配置。但是 Ajax 应用并非是一件容易的事情,他带来了很多之前的 Web 应用所不具有的管理方式和挑战,主要包括:
- 庞大的代码量需要引入更有效的代码组织方式,比如面向对象的方法
- 浏览器之间的不兼容限制了开发的效率,直接导致程序员的头发数量和浏览器的种类数目成反比
- 越来越丰富的功能带需要更加强大的类库支持
所以,选择一个好的 JavaScript 框架来作为起点对于 Web 开发是一件重要的事情。现在有很多类似的 JavaScript 框架能够帮助你减轻压力。关于主要的 JavaScript 框架的比较,John Resig同学写了一个很好的 JavaScript 框架的综述,可以看到,Dojo是很出色的一个。Dojo 是一个开源的 JavaScript 工具包,开始于 2004 年。是 Dojo Foundation所赞助的项目之一,得到了很多企业的支持。
Dojo 经过了若干次升级,当前(写稿时)最新的版本是 1.3.2,代码的基本组织结构如下:
图 1.Dojo 基本组织结构
其中,Dojo base 和 Core 是整个框架的基础。Dijit 是 Dojo 开发出来的一套方便使用的 Widget,DojoX 是指 Dojo eXtensions,既包括了成熟稳定的扩展,也作为一些新的主意的孵化器和一些新功能的试验平台。和 Dijit 不同的是,DojoX 中的组件并不保证支持国际化和良好可达性。
GFX 是一套跨平台的图形生成包,底层模型大致参照了 SVG,展现层同时支持 SVG 和 VML。GFX 可以帮助用户生成基于网页的矢量图,能够做到动态生成以及和用户发生交互。能够支持的图形包括矩形(Rectangle),圆弧(Circle),椭圆(Ellipse),多边形(polygon),线(Line),路径(polygon),图片(Image),文本(Text),文本路径(TextPath)。
如果下载 Dojo 源代码的话,可以在其中找到全部的演示和测试文件,可以发现使用 GFX 可以创建出来非常漂亮的图形和交互效果。比如这个虎头:
图 2.Dojo GFX 示例
用户可以再 Dojo 的网站上下载相关的 源代码,来查看里面的演示和测试文件。
回页首
一个简单的例子
使用 GFX 绘图的时候,首先要保证在需要的页面当中加入 dojo.js 文件,并且加载 GFX 包,代码如下:
清单 1. 准备工作
<script type="text/javascript" src="dojopath/dojo.js" djConfig="isDebug: true"></script> <script type="text/javascript"> dojo.require("dojox.gfx"); </script>
之后需要创建一个 surface。surface 是 GFX 模型中的一个概念,表示了一个所有形状的矩形虚拟容器,每一个页面都可以有多个这样的容器,每一个容器都有一个本地的坐标系统,X 轴水平指向右侧,Y 周垂直指向下。
创建一个 surface 的代码如下:
清单 2. 创建 surface
var surfaceHolder = dojo.byId('container'); var surface = dojox.gfx.createSurface(surfaceHolder, 300, 300);
我们首先获取了页面中一个 DOM 节点的引用,这里试用了 Dojo 的 DOM 获取函数 dojo.byId()
。之后使用 dojox.gfx.createSurface()
创建 surface。其中需要三个参数,第一个是需要创建 surface 的 DOM 节点,第二个是这个 surface 的宽度,第三个是 surface 的高度。dojox.gfx.createSurface()
语句在执行的过程中会自动把相关的节点转换成 svg 节点或者 vml 节点(在 IE 下)。
需要指出的一点是,如果创建 surface 是基于一个使用 JavaScript 创建出来的 DOM 节点,需要在创建 surface 之前,把创建出来的 DOM 节点和现有页面的 DOM 节点绑定起来。
清单 3. 在动态创建的 DOM 节点上生成 surface
var surfaceHolder = document.createElement('div'); var testHolder = dojo.byId('container'); testHolder.appendChild(surfaceHolder); // 在 createSurface 之前执行 var surface = dojox.gfx.createSurface(surfaceHolder, 300, 300);
在上面的例子代码中,第三行 testHolder
.appendChild(surfaceHolder)
必须在 dojox.gfx.createSurface
方法调用之前执行,否则程序会出错。
创建好 surface 之后,我们就可以在上面绘制我们需要的形状了,surface 对象有一系列的方法创建不同的形状。如果我们绘制一个矩形,可以参照如下步骤:
清单 4. 在动态创建的 DOM 节点上生成 surface
var rect = { x: 0, y: 0, width: 100, height: 100 }; // 定义一个矩形 var red_rect = surface.createRect(rect);// 创建矩形 red_rect.setFill([255, 0, 0, 0.5]);// 设置填充 red_rect.setStroke({color: "blue", width: 10, join: "round" });// 设置外部填充的颜色 red_rect.setTransform({dx: 100, dy: 100});// 将矩形移动位置 red_rect.connect("onclick", function(){ alert("I am a red rectangle"); });// 绑定事件
对于首先我们定义了一个矩形,描述了矩形的位置和长宽,之后我们调用 surface.createRect()
方法来创建这个矩形。之后我们分别设置了这个矩形的填充(Fill)和边(Stroke),这是两个在 GFX 模型中的概念,分别表示一个形状的内部区域和外部轮廓。
在这个例子当中,我们将矩形的内部区域设置成红色(255,0,0),并且设置透明度为 0.5(透明度的取值在 0 到 1 之间,0 表示完全不透明,1 表示完全透明)。然后我们设置这个矩形的边为 10 个像素宽的蓝色,转角处为圆角(不设的话是默认的方角)。接着我们将我们创建出来的形状向 surface 的 x 和 y 方向分别移动 100 像素。在这个例子当中,我们还为这个长方形增加了一个事件响应函数,在点击到长方形的时候,会弹出一个提示框。程序运行的结果如图 3 所示,点击长方形之后的效果如图 4 所示:
图 3. 示例 1 代码的运行效果
图 4. 点击长方形后出现提示框
回页首
动态创建组织结构图
下面我们使用一个例子来介绍如何在我们的页面中加入动态的组织结构图。同时介绍如何使用 DojoX GFX 来画线和使用已有的图片。
在一个组织当中,由于经常会出现人事上的变化,所以很难使用静态的图片来描述相关的组织结构,这里我们使用 DojoX GFX 来绘制一个可以动态变化的结构图。
为了绘制出结构图,我们需要绘制两种类型的元素。首先是组织中的人物,比如领导,下属,以及当前用户,这些信息我们使用图片(Image)来显示:另外就是人物之间的关系,这些信息我们使用路径(Path)来描述。在 DojoX GFX 当中创建一个图片的方式如下:
清单 5. 创建图片
var image = surface.createImage({ width: 48, height: 48, src: "imagePath/imageName.jpg" });
创建图片和创建一个矩形的方式类似,直接调用 surface.createImage
方法,然后传入图片的宽度,高度和图片路径。
在 DojoX GFX 当中创建一个路径的方式如下:
清单 6. 创建路径
surface.createPath() .moveTo(0, 1) .curveTo(1, 0, 2, 1) .setStroke({ color: "blue", width: 1 });
清单 6 中首先使用创建了一个路径,之后使用 moveTo
方法将起点移动到 (0,
1
)
处,然后绘制曲线到 (
1
, 0)
和 (
2
,
1
)
处,之后再为整个路径设置颜色和宽度。
由于 DojoX 在实现路径(Path)的绘制是试用了 SVG 的模型,所以我们可以使用路径字符串创建路径:
清单 7. 使用字符串来创建路径
surface.createPath(M0 1 C1 0 2 1) .setStroke({ color: "blue", width: 1 });
清单 7 中的代码的运行效果和清单 6 中的一样。
程序运行的结果如图 5 所示:
图 5. 组织结构图示例代码的运行效果
点击增加经理或者增加员工的按钮,会增加经理或者员工的数量,然后根据当前的数据,重新绘制组织结构图,绘制组织结构图的代码 清单 8 所示:
清单 8. 结构图的绘制
function drawGraph(){ dojox.data.dom.removeChildren(dojo.byId("container")); surface = dojox.gfx.createSurface("container", surfaceWidth, surfaceHeight); drawIcons(); drawLinks(); }
在每一次重新绘制之前,都需要将上一次绘制的结果清空。由于 DojoX GFX 会创建对应的 SVG 或者 VML 节点来完成绘图,所以在每一次根据新的数据重绘之前,我们都需要清空当前 surface 所在的 DOM 节点(在这个例子当中,即 id 是 container 的 DIV 节点)下的所有子节点。否则,两次绘制的元素会重叠起来。在这里,我们试用了 DojoX Data 中的 dojox.data.dom.removeChildren(domNode)
方法,该方法可以删除指定 DOM 节点下的所有子节点。其他关于 DojoX Data 的信息可以参考 Using dojo.data。
然后我们调用 dojox.gfx.createSurface
方法创建一个 surface,这里可以看到我们可以直接传一个 DOM 节点的 id 给 dojox.gfx.createSurface
方法,效果和传递一个 Dom 节点是一致的。
然后分别执行 drawIcons
方法和 drawLinks
方法来绘制相应的图片和路径,在每一次更改了经理和下属的数量之后,都需要重新计算所有图片和连线的位置。其中,绘制经理和当前使用者之间连线的代码如下:
清单 9. 经理和使用者之间连线的绘制
0 function drawFromManagers(){ 1 var interspace = surfaceWidth/(managerNum + 1); 2 var endx = surfaceWidth/2 + iconWidth/2; 3 var endy = surfaceHeight/3; 4 for(var i=0;i<managerNum;i++){ 5 var startx = interspace + i*interspace + iconWidth/2; 6 var starty = iconHeight; 7 surface.createPath() 8 .moveTo(startx, starty) 9 .curveTo(startx, (starty + endy)/2, endx, (starty + endy)/2, endx, endy) 10 .setStroke({ color: "red", width: 1 }) 11 .setFill(null); 12 } 13 }
我们首先在第 1 行根据当前绘图区域的宽度和经理的数量,计算图片之间的间距。然后在第 2,3 行确定所有连线共同的终点(就是代表使用者的图片的位置)。然后使用一个循环,根据图片间距以及图片的宽度和高度,计算出每个图片所在的位置(第 5,6 行),接着以这个位置为起点,绘制一条连线。为了使线段美观,中间还绘制了曲线(第 9 行)。绘制曲线需要的两个中间节点根据起点和终点的位置共同确定。
关于其他具体的实现细节可以参考附件中的代码。
回页首
小结
GFX 是一个跨平台的交互图形工具包,基本上基于 SVG 作为底层模型,能够为用户提供图形渲染,图形操作,图形交互等诸多功能。更重要的是,GFX 为开发者屏蔽了浏览器之间的区别,良好的解决了在浏览器中进行动态图形操作的兼容性问题(在 IE 中使用 VML 进行渲染,其他浏览器中使用 SVG)。同时 GFX 借助 Dojo 中其他功能工具,能够帮助用户快捷的创建数据可视化,图形界面操作等功能。
在创建具体图形的过程中,用户需要了解 SVG 的很多具体细节,例如绘制连线的过程中,需要熟悉 Path的各种属性,才能够做出预想效果。而更加复杂的图形一般基于多个基本图形才能够完成。如果希望在图形上附加更多的功能,则需要进一步了解 Dojo 工具包的其他部分,例如事件,数据等等。
回页首
下载
参考资料
- 参考 Dylan Schiemann 关于 DojoX GFX 的介绍。
- 查看 在线 Dojo Toolkit API 参考中关于 DojoXGFX 的部分。
- 查看 The Book of Dojo中关于 DojoXGFX 的部分。
- 查看 其他关于 Dojo Toolkit 的在线文档。
- developerWorks 技术活动和网络广播:随时关注 developerWorks 技术活动和网络广播。
- developerWorks Web development 专区:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。
关于作者
任刚任教于华侨大学。曾经在 IBM CDL 从事 Web2.0 开发工作。毕业于清华大学计算机科学与技术系。
原文链接: http://www.ibm.com/developerwork...