掌握 Dojo 工具包,第 8 部分: 明日之星 - DojoX

DojoX 是 Dojo 主功能的一個擴展區,可以說是新功能和新想法孵化器。在這裏,可以找到很多最新奇的功能組件。本文將和讀者一起來探索其中幾個比較成熟的組件。

 

 

Dojo 作爲最著名的 Ajax 開源項目之一,不僅讓 Web 程序員可以免費獲得和使用其框架進行 Web 應用的開發,更吸引了大量的開發者對其不斷的擴充,開發新的組件。DojoX 就是在這樣的開發社區中產生的。DojoX 是一組基於 dojo 的開源項目的集合,這些開源項目具有很好的創意和很高的實用性。這些 DojoX 項目有可能成長爲一個穩定的版本保留在 DojoX 中,也有些可能會遷移到 Dojo Core 或者 Dijit 中。本文將對 DojoX 中的項目進行一個總體的概述,並結合實例介紹其中較爲有特色的項目,本文主要會介紹 DataGrid,Charting,Gfx/Gfx 3D 和 DojoX Widget。

認識 DojoX

目前 DojoX 項目主要擴展了數據結構與算法、數據處理與通信、實用工具、圖形 API 以及 Web UI 等。

涉及到數據結構與算法的項目包括了 DojoX Collections、DojoX Encoding 等。Collections 定義了很多非常有用的數據集合,包括了數組(ArrayList)、二叉樹(BinaryTree)、字典(Dictionary)、迭代器(Iterator)、隊列(Queue)、有序列表(SortedList)、堆棧(Stack)。這些集合的使用將大大提高程序開發的效率以及程序的質量。Encoding 不僅提供了字符串與字符編碼的轉換,還提供了對稱算法河豚(Blowfish)和 MD5 數字摘要算法等。

DojoX Data、Embed、I/O、JSON、XML、RPC 等擴展了 Dojo 的數據處理與通信能力。其中,Data 項目提供了對更多數據格式的支持,包括了對 csv 文件以及 Google、Picasa 等提供的 API 的支持等等。

DojoX 的圖形 API 擴展了 Dojo 的動畫效果,並提供了 2D、3D 繪圖的支持。DojoX Fx 通過對 dojo core 以及 dojo fx 的擴展提供了多種動畫效果;gfx 提供了一系列矢量繪圖的方法;而 gfx3d 則提供了一些簡單的 3D 繪圖 API。

而更加豐富的 Web UI 以及 Web 小部件也是 DojoX 的一大亮點。功能強大的 Grid、實用的 Charting、以及 DojoX Image 和 DojoX Layout 使得基於 dojo 開發的 Web UI 更加豐富。DojoX Widgets 中還提供了更加豐富的小部件可以滿足大部分應用開發的需求。

除以上介紹的項目外,DojoX 還收集了很多實用工具,讀者可以在 dojo API 網站上獲得更多的信息。http://api.dojotoolkit.org/

接下來我們就來體驗一下 DojoX 給我們帶來的精彩吧

注意:本教程使用的 dojo 版本爲 1.2.1,由於 1.2.x 版本里 dojo 以及 dojoX 的部分組件有較大變化,因此本文僅適用於 dojo1.2.x,對於 dojo1.0 的開發者本文僅供參考,部分代碼不能正確運行

回頁首

DojoX DataGrid

Grid 可能是 DojoX 中最受歡迎的部件,比起普通的 Web 表格部件,Grid 更像一個基於 Web 的 Excel 組件。這使得 Grid 足可以應付較爲複雜的數據展示及數據操作。在 dojox1.2 中,dojox.grid 包中新增了 DataGrid 類,該類是對原 Grid 類的強化和替代,之所以叫做 DataGrid,是由於該類與 dojo 的數據操作類 store 無縫整合在一起。而之前的 Grid 需要將 store 對象包裝爲 model 對象才能使用。下文如果沒有特殊聲明,所有 Gird 或是 DataGrid 均指新版 DataGrid,而不是 Grid1.0。

圖 1 .DojoX DataGrid

圖 1 .DojoX DataGrid

我們爲什麼需要 Grid 呢?下面列出了 Grid 的特性:

  • 用戶只需向下拖動滾動條,Grid 即可加載延遲的記錄,省去了翻頁操作,減少 Web 與服務器交互,提高了性能;
  • 可以任意的增加和刪除單元格、行、或者列;
  • 對行進行統計摘要,Grid 可以生成類似於 OLAP 分析的報表;
  • Grid 超越了二維表格的功能,它可以跨行或跨列合併單元格以滿足不同的數據填充的需求;
  • 行列凍結功能,使得瀏覽數據更加靈活方便;
  • Grid 事件採用了鉤子機制,我們可以通過 onStyle 鉤子完成對樣式的更改;
  • 單元格具備富操作,所有的 dijit 部件都可以在單元格中使用,並且單元格可以通過單擊轉換爲編輯狀態;
  • 可以爲不同的單元格設置不同的上下文菜單;
  • Grid 嵌套,也就是說 Grid 可以在單元格中嵌套其他的 Grid,從而組成更爲複雜的應用;

除此之外,Grid 還有具有其他很多特性,例如,非常實用的偶數行上色、靈活的選取功能、自動調整列寬、數據的展開 / 合閉等。

DataGrid 基礎

要創建一個 DojoX DataGrid,就需要對 DataGrid 的基本工作過程有一個大致的瞭解。一個 DataGrid 實例的組成結構如下圖所示,DojoX DataGrid 是使用 DataGrid 的基礎,因此在使用 Grid 的時候需要加載相關的 dojox 包;一個小部件通常由框架和樣式組成,因此,我們需要指定 DataGrid 的樣式表並且聲明 DataGrid 實例。DataGrid 實例會組合一個 Structure 和一個 Store。Structure 是一個表頭及數據模型的定義,而 Store 用於承載數據。

圖 2 .DataGrid 組成結構

圖 2 .DataGrid 組成結構

下面開始我們的第一個 DataGrid 應用爲了在 Web 頁面上創建一個 DataGrid 小部件,我們從最基本的二維表格開始。首先我們需要加載一些樣式,來保證 DataGrid 能夠正常顯示,清單 1

清單 1. 加載樣式

 <style type="text/css"> 
 @import "dojox/grid/resources/tundraGrid.css"; 
 @import "dojo/resources/dojo.css"; 
 </style>

此處的 css 文件路徑爲相對於測試頁面的相對路徑。

在開始創建 Grid 之前,我們還要引入 Dojo 的基礎包 dojo.js,以用來加載其他需要的 dojo 類,並加載 dojo.data.ItemFileReadStore 類以及 dojox.grid.DataGrid 類。接下來我們就可以着手開發第一個 DataGrid 了。首先是佈局的定義,如 清單 2

清單 2. 定義佈局

 var layout = [ 
		 {field: 'pro_no', name: 'Product Number' }, 
		 {field: 'pro', name: 'Product' }, 
		 {field: 'min_amount', name: 'Minimum Amount' }, 
		 {field: 'max_amount', name: 'Maximum Amount' }, 
		 {field: 'avg_amount', name: 'Average Amount' } 
 ];

這裏定義了一個數組 layout,其中每一個成員表示一個列的定義,其中 field 指定了使用的數據項,該取值需要遵循 javascript 變量定義規則;name 爲該列顯示的名稱。接下來是 store 的開發,代碼如 清單 3

清單 3. 開發 store

 var sampleData = { 
 identifier: 'pro_no', 
 label: 'pro_no', 
 items: [ 
 {pro_no:'2100', pro:'A Series', min_amount:346, max_amount:931, avg_amount:647}, 
 {pro_no:'2200', pro:'B Series', min_amount:301, max_amount:894, avg_amount:608}, 
 {pro_no:'2300', pro:'C Series', min_amount:456, max_amount:791, avg_amount:532}, 
 {pro_no:'2400', pro:'D Series', min_amount:859, max_amount:2433, avg_amount:1840}, 
 {pro_no:'2500', pro:'E Series', min_amount:459, max_amount:1433, avg_amount:1040} 
 ] 
 }; 
 var jsonStore = new dojo.data.ItemFileReadStore({ data: sampleData });

在這裏,我們首先定義一個 JSON 數據 sampleData,這裏 identifier 是對於整行的唯一標識,因此在數據中不能出現重複;數組 items 是這個表格所顯示的數據,其中數據必須完全符合 JSON 的語法,字符串兩端必須使用引號,否則會出現語法錯誤,保險的辦法是所有的值均用引號括住。

接下來,我們就要在網頁的 Body 元素中定義 DataGrid 實例了,如 清單 4

清單 4. 定義 DataGrid 實例

 <div id="grid" dojoType="dojox.grid.DataGrid" store="jsonStore"
			 structure="layout" autoWidth="true" ></div>

dojoType 指定了該 Web 部件爲 dojox.grid.DataGrid,數據使用 jsonStore,結構爲 layout,自動調整寬度。到此,第一個 Grid 就已開發完畢,完整代碼如 清單 5

清單 5. 完整代碼

 <html> 
 <head> 
 <title>first Grid</title> 
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta> 
 <style type="text/css"> 
 @import "dojox/grid/resources/tundraGrid.css"; 
 @import "dojo/resources/dojo.css"; 
 </style> 
 <script type="text/javascript" src="dojo/dojo.js" djConfig="parseOnLoad: true"></script> 
 <script type="text/javascript"> 
 dojo.require("dojo.data.ItemFileReadStore"); 
 dojo.require("dojox.grid.DataGrid"); 
 var sampleData = {identifier: 'pro_no',label: 'pro_no', 
 items: [ 
 {pro_no:'2100', pro:'A Series', min_amount:346, max_amount:931, avg_amount:647}, 
 {pro_no:'2200', pro:'B Series', min_amount:301, max_amount:894, avg_amount:608}, 
 {pro_no:'2300', pro:'C Series', min_amount:456, max_amount:791, avg_amount:532}, 
 {pro_no:'2400', pro:'D Series', min_amount:859, max_amount:2433, avg_amount:1840}, 
 {pro_no:'2500', pro:'E Series', min_amount:459, max_amount:1433, avg_amount:1040} 
 ]}; 
 var jsonStore = new dojo.data.ItemFileReadStore({ data: sampleData }); 
 var layout = [ 
 {field: 'pro_no', name: 'Product Number' }, 
 {field: 'pro', name: 'Product' }, 
 {field: 'min_amount', name: 'Minimum Amount' }, 
 {field: 'max_amount', name: 'Maximum Amount' }, 
 {field: 'avg_amount', name: 'Average Amount' }]; 
 </script> 
 </head> 
 <body class="tundra"> 
 <div class="heading">First Grid</div> 
 <div id="grid" dojoType="dojox.grid.DataGrid" store="jsonStore"
 structure="layout" autoWidth="true"></div> 
 </body> 
 </html>

在瀏覽器中運行,效果如下:

圖 3. 第一個 Grid 運行結果

圖 3. 第一個 Grid 運行結果

DataGrid 開發詳解

DataGrid 的創建

在 DataGrid 的開發中,有三種方法創建 DataGrid 實例,第一種是 javascript 創建結構,html 代碼創建實例,我們第一個例子就是使用這種方式實現的;

第二種是由 html 代碼創建結構及實例,在這種方法中,我們使用 table 標籤,定義 Grid 的結構,而省去了在 javascript 中定義 structure 的部分。具體定義方式與標準的 html 書寫方式非常類似,定義方式如 清單 6

清單 6. 由 html 代碼創建結構及實例

 <table id="gridNode" jsid="grid" dojoType="dojox.grid.DataGrid"
 store="jsonStore" rowsPerPage="20" region="center"> 
 <thead> 
   <tr> 
     <th field="pro_no" >Product Number</th> 
     <th field="pro" >Product</th> 
     <th field="min_amount" >Minimum Amount</th> 
     <th field="max_amount" >Maximum Amount</th> 
     <th field="avg_amount" >Average Amount</th> 
   </tr> 
 </thead> 
 </table>

第三種方式就是採用純 javascript 的方式定義 DataGrid 實例,清單 7聲明網頁加載完成後就在 id 爲 gridNode 的頁面結點上創建一個 DataGrid 實例。

清單 7. 純 javascript 的方式定義 DataGrid 實例

 dojo.addOnLoad(function(){  // 指定頁面加載完畢後執行
 var grid = new dojox.grid.DataGrid({ 
	 query: { pro_no: '*' }, 
	 id: 'grid2', 
	 store: jsonStore, 
	 structure: [ 
		 {field: 'pro_no', name: 'Product Number' }, 
		 {field: 'pro', name: 'Product' }, 
		 {field: 'min_amount', name: 'Minimum Amount' }, 
		 {field: 'max_amount', name: 'Maximum Amount' }, 
		 {field: 'avg_amount', name: 'Average Amount' } 
 ],rowsPerPage: 20 
 	 }, 'gridNode');  // 設置 grid 顯示在 id 爲 gridNode 的節點下
 grid.startup();  // 啓動 grid 
 });

Grid1.2 可以通過這種方式很方便的與 dojo 容器結合在一起,動態創建頁面佈局。

Structure 詳解

DataGrid 不僅可以創建簡單的二維表格,還可以通過對 structure 的設計創建複雜的表格應用,同時還可以爲每一列進行格式化或是取值。我們將 First Grid 進行簡單的修改,得到 清單 8的代碼。

清單 8. 修改 First Grid

 function formatAmount(value){ 
	 return '$ ' + value; 
 } 
 function getRange(rowIndex, item){ 
	 if(!item){return '--';} 
	 var grid = dijit.byId('grid'); 
	 var max = grid.store.getValue(item, "max_amount"); 
	 var min = grid.store.getValue(item, "min_amount"); 
	 return max - min; 
 } 
 var subrow1 = [ 
 {field: 'pro_no', name: 'Product Number', rowSpan: 2 }, 
 {field: 'pro', name: 'Product', rowSpan: 2 }, 
 {field: 'min_amount', name: 'Min. Amount',formatter: formatAmount,width: '80px' }, 
 {field: 'avg_amount', name: 'Average Amount',formatter: formatAmount, rowSpan: 2 }, 
 {field: 'range', name: 'Range',get:getRange, rowSpan: 2 } 
 ]; 
 var subrow2 = [ 
	 {field: 'max_amount', name: 'Max. Amount',formatter: formatAmount}, 
 ]; 
 var layout = [subrow1,subrow2];

這裏,我們從新定義了 layout,將 layout 分爲兩個子行,其中子行 1 包含了五個字段,其中 pro_no、pro、avg_amount、range 具有值爲 2 的 rowSpan 屬性,也就表明這三列跨越了兩行。第二行僅有 max_amount 一個字段。同時,我們爲三個 amount 字段指定了 formatter 函數,在其數值前添加美元符號。爲 range 字段指定了 get 方法來自動獲取最大值與最小值的差。

顯示效果如下:

圖 4. DataGrid 佈局示例 1

圖 4. DataGrid 佈局示例 1

除了 rowSpan 屬性外我們還可以使用 colSpan 屬性,這兩個屬性的用法與 html 中的用法一致,並且可以在 html 定義表結構中使用,我們再看這個表頭的例子來理解一下 colSpan 的用法。

清單 9. colSpan 的應用

 var structure = [[ 
	 {field: 'type', name: 'Type', rowSpan: 2}, 
	 {field: 'pro', name: 'Product', rowSpan: 2}, 
	 {field: 'Q20071', name: 'Q1',formatter: formatAmount }, 
	 {field: 'Q20072', name: 'Q2',formatter: formatAmount }, 
	 {field: 'Q20073', name: 'Q3',formatter: formatAmount }, 
	 {field: 'Q20074', name: 'Q4',formatter: formatAmount } 
  ],[ 
	 {field: 'Y2007', name: 'Year 2007',formatter: formatAmount, colSpan: 4 } 
 ]];

清單 9的顯示效果如下:

圖 5. DataGrid 佈局示例 2

圖 5 . DataGrid 佈局示例 2

Store 的使用

DataGrid 使用了 Store 作爲數據源,在以上的例子中,我們都是將數據寫在 javascript 中然後作爲 data 參數值傳給 Store 的構造方法。但是在大多數情況下,數據是要動態的通過 Ajax 請求從服務器端獲取的,這同樣可以通過 Store 來實現。我們僅需要將聲明 Store 對象時傳入請求的 url 地址即可,如:new dojo.data.ItemFileReadStore({url: 'jsondata.txt' }) 。Store 包括 dojo.data.ItemFileReadStore 和 dojo.data.ItemFileWriteStore 兩個類。我們在使用 DataGrid 的編輯功能時需要使用 ItemFileWriteStore 來作爲數據源。下面就演示了一個多功能的 DataGrid,這個 Grid 使用外部數據源,可以對單元格進行編輯,並且可以通過右擊表頭彈出列選菜單。爲了頁面能夠正確,清單 10載入了所需的 CSS。

清單 10. 加載所需 CSS

 <style type="text/css"> 
 @import "dojox/grid/resources/tundraGrid.css"; 
 @import "dijit/themes/tundra/tundra.css"; 
 @import "dojo/resources/dojo.css"; 
 </style>

清單 11的代碼引入了所需的 dojo 包,並創建了可編輯的 DataGrid,將其添加到了 id 爲 gridNode 的頁面節點中。爲了使列具有編輯功能只需要在 structure 定義中的表示該列的 JSON 定義中添加值爲 true 的 editable 屬性。

清單 11. 引入所需 Dojo 包

 <script type="text/javascript" src="dojo/dojo.js" djConfig="parseOnLoad: true"></script> 
 <script type="text/javascript"> 
 dojo.require("dojo.data.ItemFileWriteStore"); 
 dojo.require("dojox.grid.DataGrid"); 
 // 下面兩個包用來創建右鍵彈出菜單
 dojo.require("dijit.Menu"); 
 dojo.require("dojox.widget.PlaceholderMenuItem"); 
 // 使用 dataGrid.txt 中的數據作爲填充 DataGrid 的數據
 var jsonStore = new dojo.data.ItemFileWriteStore({ url:'dataGrid.txt'}); 
 var layout = [ 
 {feld: 'emp_no', name: 'Employee Number'}, 
 {field: 'name', name: 'Name', editable: true },  // 該列可編輯
 {field: 'gender', name: 'Gender', editable:true, 
 type:dojox.grid.cells.Select, options:['F','M'] }, // 編輯該列時使用下拉菜單
 {field: 'dept_no', name: 'Deptment Number', editable:true, 
 type:dojox.grid.cells.Select, options:['730','731','732','733','734','735']}, 
 {field: 'bonus', name: 'Bonus', editable: true } 
 ]; 
 dojo.addOnLoad(function(){ 
 var grid = new dojox.grid.DataGrid({ 
 query: { emp_no: '*' },  // 查詢字符串
 id: 'grid2',            //DataGrid 的 id 
 autoWidth:true,        // 自動調整寬度
 store: jsonStore,       // 使用 jsonStore 對象
 structure: layout,       // 使用 layout 對象定義的結構
 rowsPerPage: 20,       // 每頁讀取 20 條記錄,保證 Web 瀏覽器的性能
 headerMenu: gridMenu  // 指定頭菜單爲 gridMenu 
			 }, 'gridNode'); 
			 grid.startup(); //grid 生效
			 }); 
			 </script>

清單 12定義了菜單 gridMenu 以及承載 DataGrid 的 DIV。

清單 12. 定義菜單

 <body class="tundra"> 
 <div class="heading">Data Grid</div> 
 <div dojoType="dijit.Menu" jsid="gridMenu" id="gridMenu" style="display: none;"> 
 <div dojoType="dojox.widget.PlaceholderMenuItem" label="GridColumns" ></div> 
 </div> 
 <div id="gridNode" style="height:350px;" /> 
 </body>

本例使用了外部數據源 dataGrid.txt,該文件的內容類似於 清單 13

清單 13. dataGrid.txt

 { 	 identifier: 'emp_no', 
	 label: 'emp_no', 
	 items: [ 
     {emp_no:'2100', name:'Matt', gender:'M', dept_no:730, bonus:647}, 
	 {emp_no:'2200', name:'Lisa', gender:'F', dept_no:731, bonus:608}, 
	 {emp_no:'2300', name:'Mick', gender:'M', dept_no:732, bonus:532}, 
	 {emp_no:'2400', name:'John', gender:'M', dept_no:733, bonus:1840}, 
	 {emp_no:'2500', name:'Jan', gender:'M', dept_no:734, bonus:1040}, 
	 {emp_no:'2101', name:'Jeff', gender:'M', dept_no:730, bonus:647}, 
	 {emp_no:'2202', name:'Frank', gender:'M', dept_no:731, bonus:608}, 
	 {emp_no:'2303', name:'Fred', gender:'M', dept_no:732, bonus:532} 
 ]}

運行,結果如下圖所示。

圖 6. 可編輯的 DataGrid

圖 6. 可編輯的 DataGrid

回頁首

DojoX Charting

Charting 是基於 DojoX 繪圖包的數據可視化組件,包括了 Chart2D 和 Chart3D 來分別繪製 2D 和 3D 的圖表。Chart2D 提供多種樣式的餅圖、柱狀圖、折線圖、面積圖、網格等圖表。Chart3D 目前僅提供了 3D 柱狀圖和 3D 圓柱圖,並且從社區獲取的信息表明由於 IE 上的性能問題導致 Chart3D 的開發暫時擱置。Charting 的應用主要分爲如下幾個步驟:

  1. 首先引入所需要的 dojox 類,如:
    dojo.require("dojox.charting.Chart2D"); //Chart2D 所需要的 2D 類 
    dojo.require("dojox.charting.Chart3D"); //Chart3D 所需要的 3D 類 
    dojo.require("dojox.charting.themes.PlotKit.blue"); // 樣式主題
  2. 第二,聲明 Chart 對象,包括了 Chart2D 或 Char3D,
    如:var chart1=new dojox.charting.Charting.Chart2D('chart1');
    這裏傳入的參數爲要在頁面中載入 chart1 的元素的 ID,也就是 chart1 顯示後的上層標籤的 ID;
  3. 使用 Chart 對象的 setTheme 爲 Chart 對象設置主題,來保證準確的繪製圖表;
  4. 使用 Chart 對象的 addPlot 方法爲 Chart 對象添加部件,可以添加多個部件;
  5. 使用 Chart 對象的 addSeries 方法爲 Chart 對象添加數據;
  6. 最後,調用 render 方法將 chart 對象添加到頁面節點中;

下面我們來看幾個應用實例。

2D 餅圖

清單 14的代碼爲 2D 餅圖,我們可以看到該實例加載了類 Chart2D 和 themes.PlotKit.blue 對象。chart1 對象聲明在 ID 爲 char1 的元素下,並被添加了一個 Pie 部件作爲默認部件,數據爲 3、2、5、1、6、4。

清單 14.2D 餅圖

 <script type="text/javascript"> 
 dojo.require("dojox.charting.Chart2D"); 
 dojo.require("dojox.charting.themes.PlotKit.blue"); 
 makeObjects = function(){ 
 var chart1 = new dojox.charting.Chart2D("chart1"); 
 chart1.setTheme(dojox.charting.themes.PlotKit.blue); 
 chart1.addPlot("default", { 
    type: "Pie", 
     font: "normal normal bold 12pt Tahoma", 
     fontColor: "black", 
	 labelOffset: -25, 
	 precision: 0 
	 }); 
  chart1.addSeries("Series A", [3, 2, 5, 1,6,4]); 
  chart1.render(); 
	 }; 
 dojo.addOnLoad(makeObjects); 
 </script> 
……
 <div id="chart1" style="width: 300px; height: 300px;"></div>

運行結果如下:

圖 7. 二維餅圖示例

圖 7. 二維餅圖示例

帶網格的 2D 面積圖

這個例子中我們爲 Chart2D 對象添加了兩個部件,網格和麪積圖,如 清單 15。值得注意的是,我們爲 chart 添加了兩個部件,Plot1 和 Plot2,其中 Plot1 的類型爲 Areas,Plot2 的類型爲 Grid,我們給 Plot1 添加了三組數據,並沒有給 Plot2 添加數據。

清單 15. 網格和麪積圖

 chart = new dojox.charting.Chart2D("chart2"); 
 chart.setTheme(dojox.charting.themes.PlotKit.orange); 
 chart.addAxis("x", {origin:"max"}); 
 chart.addAxis("y", {vertical: true, leftBottom: true, min: 5000, max: 8000, 
 majorTickStep: 500, minorTickStep: 100}); 
 chart.addPlot("plot1", {type: "Areas", hAxis:"x", vAxis:"y"}); 
 chart.addPlot("plot2", {type: "Grid", hAxis:"x", vAxis:"y"}); 
 data1 = [{x:10,y:7200}, {x:20,y:6800}, {x:30,y:7000}, {x:40,y:6600}, {x:50,y:7000}, 
 {x:60,y:6800}, {x:70,y:7200}, {x:80,y:6600}, {x:90,y:6800}, {x:100,y:7000}]; 
 data2 = [{x:10,y:6800}, {x:20,y:5800}, {x:30,y:6400}, {x:40,y:5600}, {x:50,y:6000}, 
 {x:60,y:6200}, {x:70,y:6600}, {x:80,y:7200}, {x:90,y:6300}, {x:100,y:6000}]; 
 data3 = [{x:10,y:6000}, {x:20,y:6300}, {x:30,y:6800}, {x:40,y:6200}, {x:50,y:6200}, 
 {x:60,y:6600}, {x:70,y:6300}, {x:80,y:6200}, {x:90,y:6000}, {x:100,y:5900}]; 
 chart.addSeries("series B", data2, {plot: "plot1"}); 
 chart.addSeries("series C", data3, {plot: "plot1"}); 
 chart.addSeries("series A", data1, {plot: "plot1"}); 
 chart.render();

運行結果如下:

圖 8. 二維面積圖示例

圖 8 二維面積圖示例

巧用折線圖進行函數圖像的繪製

我們可以使用折線圖或者面積圖完成函數圖像的繪製,原理就是按照一定的步長循環將定義域中將 x,y 值計算出來組成一組數據添加的 Chart2D 對象中,下面這個例子就是使用了折線圖繪製了正弦餘弦曲線。

清單16繪製正弦餘弦曲線

清單 16. 繪製正弦餘弦曲線

 dojo.require("dojox.charting.Chart2D"); 
 dojo.require("dojox.charting.themes.PlotKit.blue"); 
 dojo.addOnLoad(function() { 
   var period = 2 * Math.PI; 
   var tick = Math.PI / 180.0; 
   var step = 5*Math.PI / 180.0; 
   var chart = new dojox.charting.Chart2D('chart_area'); 
   chart.setTheme(dojox.charting.themes.PlotKit.blue); 
   chart.addAxis("x", {min: 0, max: period, majorTickStep: tick*30, 
                   minorTickStep: tick*10, minorLabels: false, font: '40px bold'}); 
   chart.addAxis("y", {vertical: true, min: -1.01, max: 1, majorTickStep: 0.5, 
                   minorTickStep: 0.1, minorLabels: false, font: '40px bold'}); 
   chart.addPlot("default", {type: 'Lines'}); 
   chart.addPlot("grid", {type: "Grid", vMinorLines: true}); 
   var series = {'sin' : [], 'cos' : []}; 
   for(var i = 0; i < period; i+=step) { 
      series.sin.push({'x' : i, 'y' : Math.sin(i)}); 
      series.cos.push({'x' : i, 'y' : Math.cos(i)}); 
   } 
 chart.addSeries('sin', series.sin); 
 chart.addSeries('cos', series.cos); 
 chart.render(); 
 });

運行結果如下:

圖 9. 正餘弦曲線圖示例

圖 9. 正餘弦曲線圖示例

3D 柱狀圖的繪製

使用 Chart3D 與 Chart2D 略有不同,這主要是因爲 3D 繪圖比 2D 繪圖要複雜一些,3D 繪圖一個很重要的過程就是座標變換,以及光照和渲染都是很重要的考慮要點。不過 Chart3D 已經對 gfx3D 進行了封裝,我們只需要通過對簡單的幾個參數的設置就可以完成一個 3D 圖表。我們可以從 清單 17中看出 Chart3D 對象的聲明比 Chart2D 多了一些參數,這主要是兩類,光照與攝像機。光照指的是 3D 物體的周圍的光環境,其中 lights 是光源數組,也就是說 3D 物體可以接受多個光源的光照;ambient 爲環境光,影響物體的各個立體面;specular 是鏡面反射光,這是光源照射物體特定位置發生鏡面反射所產生的高光。當然了,你甚至可以不用理解這些參數的意義,在實際使用中多次調整參數以滿足自己的使用需求。

清單 17. 3D 繪圖

 dojo.require("dojox.charting.Chart3D"); 
 dojo.require("dojox.charting.plot3d.Bars"); 
 dojo.require("dojox.charting.plot3d.Cylinders"); 

 makeObjects = function(){ 
 var m = dojox.gfx3d.matrix; 
 var chart = new dojox.charting.Chart3D("test", 
 {lights:   [{direction: {x: 5, y: 5, z: -5}, color: "white"}], 
	 ambient:  {color:"white", intensity: 2}, 
	 specular: "white"}, 
 [m.cameraRotateXg(10), m.cameraRotateYg(-10), m.scale(0.8), 
 m.cameraTranslate(-50, -50, 0)] 
 ); 
 var plot1 = new dojox.charting.plot3d.Bars(500, 500, {gap: 10, material: "yellow"}); 
 plot1.setData([2,1,2,1,1,1,2,3,5]); 
 chart.addPlot(plot1); 
 var plot2 = new dojox.charting.plot3d.Bars(500, 500, {gap: 10, material: "red"}); 
 plot2.setData([1,2,3,2,1,2,3,4,5]); 
 chart.addPlot(plot2); 
 var plot3 = new dojox.charting.plot3d.Cylinders (500, 500, {gap: 10, material: "#66F"}); 
 plot3.setData([2,3,4,3,2,3,4,5,5]); 
 chart.addPlot(plot3); 
 var plot4 = new dojox.charting.plot3d.Cylinders (500, 500, {gap: 10, material: "#E6F"}); 
 plot4.setData([3,4,5,4,3,4,5,5,5]); 
 chart.addPlot(plot4); 
 chart.generate().render(); 
 }; 
 dojo.addOnLoad(makeObjects);

運行這段代碼,顯示效果如下:

圖 10. 三維柱狀圖示例

圖 10. 三維柱狀圖示例

這個 3D 柱狀圖結合了長方體和圓柱體,dojox.charting.plot3d.Bars 是長方體的聲明類,new dojox.charting.plot3d. Cylinders 是圓柱體的聲明類。

回頁首

DojoX Gfx 和 Gfx3D

DojoX Gfx 和 Gfx3D 是 DojoX 中進行繪圖的兩個包,分別提供了 2D 和 3D 的繪圖 API。前面介紹的 DojoX Charting 就是在這兩個包的基礎上開發的。Gfx 以及 Gfx 3D 是一組矢量繪圖 API。對於原生的矢量圖形,Gfx 能夠支持 SVG、Canvas 和 VML,新版本中又增加了對 Silverlight 的支持。首先,我們來看看 DojoX Gfx 可以做什麼。在 dojo 官方網站公佈的開發包中有這麼幾個 Gfx 例子,見下圖。可以看出,Gfx 對於簡單的 2D 繪圖已經綽綽有餘了。

圖 11.Gfx 繪圖

圖 11.Gfx 繪圖

下面我們就來使用 Gfx 開始繪圖。Gfx 繪圖的基本步驟可以簡單的歸爲兩步:打開一個畫布(surface),然後「畫」。在繪畫之前,我們要例行公事,引入 dojox.gfx 包。然後通過 dojox.gfx 對象的 createSurface 方法建立畫布。Surface 提供了很多畫筆來進行作畫,這裏我們介紹幾個比較常用的。

  • 線性畫筆:使用方法爲 surface.createLine(opt),參數包含 x1、y1、x2、y2 四個屬性,分別表示起點橫縱座標和終點橫縱座標;
  • 矩形畫筆:使用方法爲 surface.createRect(opt),參數 opt 是一個 JSON 對象,包括了屬性 x、y、width、height,分別表示矩形左上角的橫縱座標以及舉行的寬和高;
  • 圓形畫筆:使用方法爲 surface.createCircle(opt),參數 opt 具有三個屬性,分別是圓心橫座標 cx、圓心縱座標 cy、圓半徑 r;
  • 路徑畫筆:這個是最強大的畫筆,可以繪製任意曲線和圖形,當然也是使用最複雜的畫筆,使用方法爲 surface.createPath(path),path 是描繪一組路徑的字符串,描繪規則可以參考 SVG 中關於矢量路徑的介紹。

除以上介紹的幾種外還有點、折線、文字等畫筆,並且還提供了組功能,可以使一組圖形一起響應事件等。下面就看個簡單的例子來加深理解。

清單 18. Gfx 繪圖代碼

 dojo.require("dojox.gfx"); 
 surface = dojox.gfx.createSurface(dojo.byId("gfx_holder"), 700, 700); 
 surface.createRect({x: 260, y: 260, width: 50, height: 50}).setFill("#AAF"); 
 surface.createLine({x1: 100, y1: 400, x2: 400, y2: 350}) 
	 .setStroke({color:"#9F3",width:5}); 
 surface.createCircle({cx: 200, cy: 200, r: 50}) 
	 .setFill("#FEF").setStroke({color: "#F9A", width: 3}); 
 var path="M153 334 " + 
		"C153 334 151 334 151 334 C151 339 153 344 156 344 " + 
		"C164 344 171 339 171 334 C171 322 164 314 156 314 " + 
		"C142 314 131 322 131 334 C131 350 142 364 156 364 " + 
		"C175 364 191 350 191 334 C191 311 175 294 156 294 " + 
		"C131 294 111 311 111 334 C111 361 131 384 156 384 " + 
		"C186 384 211 361 211 334 C211 300 186 274 156 274"
 surface.createPath(path).setFill("rgb(FF,FF,FF)").setStroke({color:"red",width:3});

清單 18中的代碼將在 ID 爲 gfx_holder 的 html 標記內添加這個繪圖。這段代碼中的 setFill 方法和 setStroke 方法分別用來設置填充效果和筆觸效果。代碼運行結果如下圖所示。

圖 12. Gfx 簡單繪圖示例

圖 12. Gfx 簡單繪圖示例

Gfx3D 的工作原理是採用計算機圖形學的原理將三維空間中的物體按照透視規則從三維座標系轉換成二維的座標系然後通過 SVG 等矢量圖顯示出來。限於瀏覽器和 javascript 的性能,目前 Gfx3D 僅能繪製較爲簡單的 3D 物體和空間曲線,還不能繪製複雜的空間曲面以及進行紋理等渲染工作,但是 Gfx3D 足以滿足大部分 Web 應用的需要了。Gfx3D 的使用大致分三個步驟:建立畫布,在畫布上建立視圖,在視圖上建立 3D 物體。視圖上必不可少的要設置光源以及攝像機方位。設置光源可以通過 setLights 方法來設定,攝像機方位則需要使用 dojox.gf3d.maxtrix 對象的 cameraRotateX、cameraRotateY、cameraRotateZ、cameraTranslate 等方法來確定。清單 19在畫布上繪製了一個立方體和一個圓柱體。

清單 19. Gfx 3D 繪圖

 dojo.require("dojox.gfx3d"); 

 makeObjects = function(){ 
 var surface = dojox.gfx.createSurface("test", 500, 500); 
 var view = surface.createViewport();  // 建立視圖
 view.setLights([{direction: {x: 0, y: 0, z: -10}, color: "white"}, 
			 {direction: {x: 10, y: 0, z: -10}, color: "#444"}], 
 {color: "white", intensity: 2}, "white"); 
 var m = dojox.gfx3d.matrix;  // 建立空間
 var l = view.createCube({bottom: {x: 0, y: 0, z: 0}, top: {x: 100, y: 100, z: 100}}) 
		 .setFill({type: "plastic", finish: "dull", color: "lime"});  // 繪製立方體
 view.createCylinder({})   // 繪製圓柱體
    .setTransform([m.translate(200, 100,200), m.rotateZg(60), m.rotateXg(-60)]) 
	 .setStroke("black") 
	 .setFill({type: "plastic", finish: "glossy", color: "red"}); 
 var camera = [m.cameraRotateXg(20), m.cameraRotateYg(20), 
 m.cameraTranslate(-200, -200, 0)]; // 設置攝像機方位
 view.applyCameraTransform(camera); 
 view.render(); 
 }; 
 dojo.addOnLoad(makeObjects);

運行,結果如下圖所示。

圖 13. Gfx-3D 簡單繪圖示例

圖 13. Gfx-3D 簡單繪圖示例

其他 DojoX Widget

DojoX 的 widget 包中還提供了更多的小部件,非常的方便易用,本文的最後再來兩個餐後甜點,拾色器和魚眼。拾色器是我們經常會用到的小部件,用 DojoX 生成拾色器非常簡單,僅需要 清單 20這一小段代碼即可。

清單 20. 拾色器

 <div id="pickerToo" dojoType="dojox.widget.ColorPicker"
 animatePoint="true"
 showHsv="true"
 showRgb="true"
 webSafe="true"
 onChange="alert(this.value)"> 
 </div>

除了這段 javascript 代碼,爲了能正確的顯示拾色器的樣式,我們還需要導入它的 css 文件:dojox/widget/ColorPicker/ColorPicker.css。代碼中,屬性 animatePoint 來確定指針是否有滑動動畫,默認爲 true;屬性 showHsv 表示是否顯示 HSV 顏色模式數值;屬性 showRgb 表示是否顯示 RGB 顏色模式的數值;webSafe 表示是否顯示 Web 安全色。清單 20顯示效果如下:

圖 14 .DojoX 拾色器

圖 14 .DojoX 拾色器

魚眼的實現也很簡單,我們需要製作一組圖標,命名爲 fe1.gif、fe2.gif、fe3.gif 一直到 fe7.gif。我們將它們和頁面放在同一文件夾下。然後在網頁中書寫如 清單 21所示代碼。

清單 21. 魚眼

 <div dojoType="dojox.widget.FisheyeList"
 itemWidth="40" itemHeight="40"
 itemMaxWidth="150" itemMaxHeight="150"
 orientation="horizontal"
 effectUnits="2"
 itemPadding="10"
 attachEdge="center"
 labelEdge="bottom"
 id="fisheye1"> 
 <div dojoType="dojox.widget.FisheyeListItem" id="item1"
 onclick="alert('click on ' + this.label !');" label="Item 1" iconSrc="fe1.gif"> 
 </div> 
 <div dojoType="dojox.widget.FisheyeListItem" label="Item 2" iconSrc="fe2.gif"></div> 
 <div dojoType="dojox.widget.FisheyeListItem" label="Item 3" iconSrc="fe3.gif"></div> 
 <div dojoType="dojox.widget.FisheyeListItem" label="Item 4" iconSrc="fe4.gif"></div> 
 <div dojoType="dojox.widget.FisheyeListItem" label="Item 5" iconSrc="fe5.gif"></div> 
 <div dojoType="dojox.widget.FisheyeListItem" label="Item 6" iconSrc="fe6.gif"></div> 
 </div>

運行,鼠標移上去來看看效果。

圖 15.DojoX 魚眼

圖 15 DojoX 魚眼

回頁首

結束語

JavaScript 並不是萬能的,同樣,使用 JavaScript 開發的 Dojo 也不是萬能的,但是爲了滿足衆多開發者的需要,DojoX 提供了非常豐富的選擇,並且提供了對 Flash、SilverLight、Google Gear 等組件的支持,使得 Dojo 愛好者們可以開發出更好更強大的 Web 應用。隨着 DojoX 項目的不斷完善和成熟,Dojo 將給開發者們帶來更多的驚喜。

參考資料

學習

獲得產品和技術

  • 下載最新版 Dojo 工具包。

討論