使用 DojoX GFX 进行绘图

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); 

在上面的例子代码中,第三行 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.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.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,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,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 工具包的其他部分,例如事件,数据等等。





回页首


下载

描述 名字 大小 下载方法
示例代码 sample.zip 6KB HTTP
关于下载方法的信息


参考资料



关于作者

任刚任教于华侨大学。曾经在 IBM CDL 从事 Web2.0 开发工作。毕业于清华大学计算机科学与技术系。




原文链接: http://www.ibm.com/developerwork...

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


我有一个网格,可以根据更大的树结构编辑小块数据.为了更容易知道用户保存了什么,我希望当用户第一次看到网格时,网格处于不可编辑状态.当用户准备好后,他们可以单击编辑按钮,这将使网格的某些部分可编辑.然后,有一个保存或取消按钮可以保存更改或还原.在大多数情况下它是有效的.但
我即将开始开发一款教育性的视频游戏.我已经决定以一种我可以轻松打包为Web,Mobiles和可能的Standalone版本的方式来实现这一目标.我不想使用Flash.因此,我确信(无论如何我会听取建议)使用JavaScript和SVG.我正在对这个问题进行大量研究,但我很难把各个部分放在一起.我知道Raphae
我正在使用带有Grails2.3.9的Dojo1.9.DojoNumberTextBox小部件–我在表单中使用–将固定格式(JavaScript基本格式)的实数值(例如:12.56)设置为HTML表单输入字段(但根据浏览器区域设置显示/编辑它们,所以用户总是看到格式正确的数字).另一方面,Grails期望输入字段根据浏览器
1.引言鉴于个人需求的转变,本系列将记录自学arcgisapiforjavaScript的学习历程,本篇将从最开始的arcgisapiforjavaScript部署开始,个人声明:博文不在传道受业解惑,旨在方便日后复习查阅。由于是自学,文章中可能会出现一些纰漏,请留言指出,不必留有情面哦!2.下载ArcGISforDe
我正在阅读使用dojo’sdeclare进行类创建的语法.描述令人困惑:Thedeclarefunctionisdefinedinthedojo/_base/declaremodule.declareacceptsthreearguments:className,superClass,andproperties.ClassNameTheclassNameargumentrepresentsthenameofthec
我的团队由更多的java人员和JavaScript经验丰富组成.我知道这个问题曾多次被问到,但为了弄清楚我的事实,我需要澄清一些事情,因为我在客户端技术方面的经验非常有限.我们决定使用GWT而不是纯JavaScript框架构建我们的解决方案(假设有更多的Java经验).这些是支持我的决定的事实.>
路由dojo/framework/srcouting/README.mdcommitb682b06ace25eea86d190e56dd81042565b35ed1Dojo应用程序的路由路由FeaturesRoute配置路径参数RouterHistoryManagersHashHistoryStateHistoryMemoryHistoryOutletEventRouterContextInjectionOutl
请原谅我的无知,因为我对jquery并不熟悉.是否有dojo.connect()的等价物?我找到了这个解决方案:http:/hink-robot.com/2009/06/hitch-object-oriented-event-handlers-with-jquery/但是没有断开功能!你知道jquery的其他解决方案吗?有jquery.connect但这个插件在我的测试中不起作用.
与java类一样,在dojo里也可以定义constructor 构造函数,在创建一个实例时可以对需要的属性进行初始化。//定义一个类mqsy_yjvar mqsy_yj=declare(null,{     //thedefaultusername    username: "yanjun",          //theconstructor   
我一直在寻找一些最佳实践,并想知道Dojo是否具有框架特定的最佳实践,还是最好只使用通用的Javascript标准?特别是我主要是寻找一些功能和类评论的指导方针?解决方法:对于初学者来说,这是项目的风格指南:DojoStyleGuide
我有’05/17/2010’的价值我想通过使用dojo.date.locale将其作为“2010年5月17日”.我尝试过使用dojo.date.locale.parse,如下所示:x='05/17/2010'varx=dojo.date.locale.parse(x,{datePattern:"MM/dd/yyyy",selector:"date"});alert(x)这并没有给我所需的日期
我正在尝试创建一个包含函数的dojo类,这些函数又调用此类中的其他函数,如下所示:dojo.provide("my.drawing");dojo.declare("my.drawing",null,{constructor:function(/*Object*/args){dojo.safeMixin(this,args);this.container=args[0];
我知道你可以使用jQuery.noConflict为jQuery做这件事.有没有办法与Dojo做类似的事情?解决方法:我相信你可以.有关在页面上运行多个版本的Dojo,请参阅thispage.它很繁琐,但似乎是你正在寻找的东西.一般来说,Dojo和jQuery都非常小心,不会破坏彼此或其他任何人的变量名.
我有一个EnhancedGrid,用户经常使用复杂的过滤器.有没有办法允许用户保存或标记过滤器,以便将来可以轻松地重新应用它?我知道我可以通过编程方式设置过滤器,但我无法预测用户想要的过滤器.谢谢!编辑:自己做了一些进展…使用grid.getFilter()返回过滤器的JSON表示,然后使用json.strin
我有这个代码:dojo.declare("City",null,{constructor:function(cityid,cityinfo){}});dojo.declare("TPolyline",GPolyline,{constructor:function(points,color){},initialize:function(map){});应该是什
我遇到的问题是我的所有javascript错误似乎来自dojo.xd.js或子模块.我正在使用chrome调试器和许多dijit功能,如dijit.declaration和dojo.parser.这有点烦人,因为它很难找到简单的错误或滑倒.我希望我可以添加一个选项,允许我的调试器在我的非dojo代码中显示选项会发生的位置.我是
我正在使用DojoToolkit数字/解析函数来处理格式化和使用ICU模式语法解析字符串.有没有人知道有可能采取任意ICU模式字符串并以某种方式使用Dojo(或其他)库将其分解为它的部分(例如,对于数字模式,它可以被分解为小数位数,数千个分组等…).我希望这样做,而不需要让我的代码密切了
我有两个看似相关的问题,访问在不同的地方定义的javascript函数.我遇到的第一个问题是调用我在firgbug或safari控制台中定义的函数.我定义了一个名为getRed的函数,如下所示:functiongetRed(row,col){//dosomethingstuffandreturntheredvalueasa
我想添加一个在Ajax调用中指定的外部样式表.我已经找到了一种方法来使用jQuery(参见下面的示例),但是我需要使该方法适应dojoJavaScript框架.JQuery示例$('head').append('<linkrel="stylesheet"type="text/css"href="lightbox_stylesheet.css">');谢谢.解决方法:一旦你
我正在尝试使用dojo.connect将onMouseDown事件连接到图像,如:dojo.connect(dojo.byId("workpic"),"onMouseDown",workpicDown);functionworkpicDown(){alert("mousedown");}类似的代码几行后,我将onMouse*事件连接到dojo.body确实完全正常工作.但是当我点击图像时