任志豪,賴源勁
(華南師范大學軟件學院,廣州 510631)
?
基于SVG的思維導(dǎo)圖的系統(tǒng)實現(xiàn)
任志豪,賴源勁
(華南師范大學軟件學院,廣州510631)
摘要:基于SVG實現(xiàn)的繪圖技術(shù),具有對象化和易于交互的特點,適合用于思維導(dǎo)圖在線繪制的實現(xiàn)?;赟VG實現(xiàn)的思維導(dǎo)圖繪制系統(tǒng),其基礎(chǔ)功能有思維導(dǎo)圖節(jié)點及邊的繪制、增刪、位置變換和拖動,其中的關(guān)鍵技術(shù)點包括圖形的位置計算和渲染等。
關(guān)鍵詞:思維導(dǎo)圖;SVG;實現(xiàn)
隨著信息時代的飛速發(fā)展,人們在日常工作或者學習中越來越需要記錄信息和知識,使用思維導(dǎo)圖可為人們更高效地記錄和歸納信息。思維導(dǎo)圖又稱心智圖,它從中心主題出發(fā),發(fā)散關(guān)鍵點,以簡潔的信息表示整個結(jié)構(gòu)。一個典型的思維導(dǎo)圖由多個節(jié)點所組成,一個節(jié)點也可引申出多個子節(jié)點,體現(xiàn)了更清晰的分類思想,通過合理的分類方法,可以清晰地整理任務(wù)和快速定位信息的關(guān)鍵點。本文的主要內(nèi)容則是如何實現(xiàn)一個思維導(dǎo)圖的Web應(yīng)用。
該系統(tǒng)使用SVG技術(shù)繪制思維導(dǎo)圖的圖形。SVG是可伸縮矢量圖形(Scalable Vector Graphics),用于描述二維矢量的一種圖形格式。SVG是一種基于XML的語言,可以在XML或者HTML中嵌入SVG。SVG的事件模型基于SVG圖形元素上,所以SVG具有很好的交互性。目前的已有的思維導(dǎo)圖Web應(yīng)用中,某些網(wǎng)站的思維導(dǎo)圖應(yīng)用使用了Canvas技術(shù),而Canvas是像素級別的圖形繪制技術(shù),不太適合于思維導(dǎo)圖這種對象級別的圖形繪制中;另外一些網(wǎng)站的思維導(dǎo)圖應(yīng)用使用了Flash技術(shù),但Flash技術(shù)比較繁瑣,性能不高,而且Flash需要下載插件,在一部分的操作系統(tǒng)不能使用。與上述兩種技術(shù)相比,SVG將圖像當成對象,可將思維導(dǎo)圖中節(jié)點和線等圖形表現(xiàn)為對象,事件模型基于圖形元素上的特性使得SVG更適合用于動態(tài)交互的應(yīng)用。目前兼容SVG的瀏覽器有IE9以及其他主流的瀏覽器,使得SVG的應(yīng)用具有較好的移植性。該系統(tǒng)使用一個SVG圖形庫——Raphael.js提供的API來繪制思維導(dǎo)圖應(yīng)用。
該系統(tǒng)的主要功能有:添加節(jié)點、刪除節(jié)點和拖動節(jié)點。
該系統(tǒng)采用面向?qū)ο蟮脑O(shè)計方式,如圖1所示,當中有4個主要的對象類型:Node、Edge、Graph和Renderer,前3個對象類型屬于數(shù)據(jù)層,第4個對象類型屬于渲染層。Node和Edge分別對應(yīng)于思維導(dǎo)圖的節(jié)點和邊,每一個節(jié)點都有一個父節(jié)點引用與父節(jié)點相連的父邊引用,用于表示節(jié)點與節(jié)點之間的關(guān)系。Graph 與Node之間是聚合關(guān)系,它擁有思維導(dǎo)圖根節(jié)點和所有節(jié)點的列表。Renderer用于渲染圖像,只有Graph擁有一個Render,Render負責具體的渲染操作,使得在數(shù)據(jù)有改變時才做出相應(yīng)的渲染,這樣的層次劃分可以使得數(shù)據(jù)操作和視圖渲染不會耦合在一起。只有Graph模塊暴露給客戶端使用,減少了繪圖系統(tǒng)和客戶端的耦合度。
圖1
Raphael.js提供繪制基本圖形的功能,例如長方形,圓形。這些基本圖形可以組在一起形成集合,對這個集合進行的操作即對集合內(nèi)所有元素進行操作,在Raphael.js中,這個集合稱為Set。
2.1節(jié)點的繪制
一個節(jié)點由一個文本和長方形組成,節(jié)點的位置由左上角坐標決定。為了讓文本在長方形中在長方形區(qū)域里水平垂直居中,先繪制文本,求得文本所占區(qū)域的寬度和長度,然后加上長方形的上下補白,即可求得長方形的寬度和長度,再將文本位移二分之一的長方形寬度和高度即可讓文本在長方形區(qū)域中水平垂直居中。最后將長方形設(shè)置為新的寬度和高度。實現(xiàn)代碼如下:
function drawNode(paper, x, y){ // paper為Raphael對象
var label = paper.text(x, y, '節(jié)點');
var textBox = label.getBBox();
var nodeXPadding = 40; //長方形的x補白
var nodeYPadding = 20; //長方形的y補白
//得到矩形的長度
var rectWidth = textBox.width + nodeXPadding;
var rectHeight = textBox.height + nodeYPadding;
var rect = paper.rect(x, y, 1, 1, 4);
label.toFront();
label.attr({
x: x + rectWidth * 0.5,
y: y + rectHeight * 0.5,
'font-size': 14
});
rect.attr({
width: rectWidth,
height: rectHeight,
'stroke': '#808080',
'stroke-width': 1
});
return paper.set().push(label).push(rect);
}
2.2連線的繪制
如果節(jié)點間的連線都用直線,思維導(dǎo)圖整體上會不太美觀。于是,為了讓連線更好看,在根結(jié)點與第一層節(jié)點間使用貝塞爾曲線,其他的連線都使用“三段連線”。
(1)貝塞爾曲線的繪制
①貝塞爾曲線
貝塞爾曲線是應(yīng)用于二維圖形應(yīng)用程序的數(shù)學曲線,通過計算貝塞爾曲線的計算公式,即可精確地繪制出復(fù)雜的曲線。貝塞爾曲線有三種重要的點:起始點、終止點和控制點。下面以二階貝塞爾曲線為例說明其原理,如圖2所示,設(shè)P0為起始點,P1為控制點,P2為終止點,在P0到P1的線段上作點Q0,在P1到P2的線段上作點Q1,在Q0和Q1的線段上作點Q2
圖2
引入?yún)?shù)t,使以下比例成立
求得Q2為:
Q2=(1-t)2P0+2t(1-t)P1+
當t的范圍在0到1時,表示由三個頂點P0、P1、P2即可描繪一條二階的貝塞爾曲線。
②根節(jié)點與第一層節(jié)點的連線
為了美觀,根結(jié)點與第一層節(jié)點使用貝塞爾曲線,起始點為根節(jié)點的中心點,終止點為第一層節(jié)點的中心點,引入?yún)?shù)k1和k2,再加上起始點和終止點的坐標來求出控制點。Raphael.js的Paper.path()提供了繪制貝塞爾曲線的方法。實現(xiàn)代碼如下:
// paper為Raphael對象,source為源節(jié)點的set集合
// target為目標節(jié)點的set集合
function drawCurve(paper, source, target){
var sourceBox = source.getBBox();
var targetBox = target.getBBox();
//求出起始點、控制點、終止點
var x1 =(sourceBox.x + sourceBox.x2)/2;
var y1 =(sourceBox.y + sourceBox.y2)/2;
var x2 =(targetBox.x + targetBox.x2)/2;
var y2 =(targetBox.y + targetBox.y2)/2;
var k1 = 0.8;
var k2 = 0.2;
var pathPoints = {
x1: x1,
y1: y1,
x2: x2,
y2: y2,
x3: x2 - k1 *(x2 - x1),
y3: y2 - k2 *(y2 - y1)
};
var edgePath = paper.path(Raphael.fullfill(
"M{x1},{y1}Q{x3},{y3},{x2},{y2}",pathPoints)); edgePath.attr({
'stroke': '#999',
'stroke-width': 2
});
edgePath.toBack();
return paper.set().push(edgePath);
}
(2)“三段連線”的繪制
除了根節(jié)點與第一層的連線,其他連線都采用“三段連線”。如圖3所示,“三段連線”由path1、path2、path3組成,這樣的連線可以使得兩層以外的節(jié)點看起來更緊湊,更美觀。
圖3
3.1添加和刪除節(jié)點時的渲染
添加和刪除節(jié)點功能的實現(xiàn)的關(guān)鍵點在于如何計算出新增的節(jié)點及剩余節(jié)點的新的坐標位置。在該系統(tǒng)的實現(xiàn)中,某一節(jié)點的位置僅受到父節(jié)點的當前位置、兄弟節(jié)點的數(shù)目和子節(jié)點的數(shù)目的影響。當添加一個新節(jié)點的時候,需要計算出新節(jié)點的位置和新節(jié)點的兄弟節(jié)點的新位置。當刪除一個節(jié)點的時候,需要計算剩余兄弟節(jié)點的新位置。
3.2渲染子節(jié)點的垂直位置
當添加或刪除一個節(jié)點時,為了使該節(jié)點所在層次的所有子節(jié)點相對于父節(jié)點垂直居中,會重新渲染子節(jié)點的位置,子節(jié)點的位置具體計算方式如下。
圖4
如圖4所示,設(shè)父節(jié)點的中心點F坐標為(hfx, hfy),父節(jié)點與子節(jié)點的橫向距離interval,父節(jié)點的寬為parentWidth。作水平線段FC,C點的橫坐標即為子節(jié)點的橫坐標childX。
每個子節(jié)點上下之間都有補白padding。設(shè)area-Height的值為所有子節(jié)點的高度和補白的高度,迭代所有子節(jié)點,即可求得areaHeight。然后在線段FC的C點上作一條高度為areaHeight的垂直平分線AB。
通過迭代子節(jié)點的高度可以求出每個子節(jié)點的垂直位置childY。設(shè)一個節(jié)點的高度加上上下補白稱為節(jié)點區(qū)域,startY為每一輪迭代中當前子節(jié)點的節(jié)點區(qū)域垂直坐標。在每一輪迭代中,當前子節(jié)點的childY等于當前子節(jié)點的startY加上一個padding。根據(jù)求得每個子節(jié)點的左上角坐標(childX, childY)即可渲染對應(yīng)子節(jié)點。實現(xiàn)代碼如下:
function renderChild(father, padding, interval){
var hfx, //父節(jié)點的中心x軸坐標
hfy, //父節(jié)點的中心y軸坐標
chilldren, //子節(jié)點數(shù)組
childX, //子節(jié)點的x軸坐標
startY, //子節(jié)點區(qū)域的起始坐標
areaHeight = 0; //所有子節(jié)點的高度加補白
hfx = father.x + interval;
hfy = father.y + interval;
children.forEach(function(child){
"use strict";
var childY = startY + padding;
//已經(jīng)求得當前子節(jié)點坐標(childX, childY),在這里作渲染操作
renderNode(child, childX, childY);
startY += getNodeHeight(child)+ padding * 2;
});
}
function getNodeHeight(){} //獲取節(jié)點高度
function renderNode(node, x, y){} //渲染節(jié)點,如果節(jié)點不存在則新建節(jié)點,否則移動節(jié)
3.3調(diào)整父節(jié)點的同級節(jié)點的垂直位置
添加或刪除節(jié)點會影響父節(jié)點的同級節(jié)點的垂直位置。如圖5所示,當添加一個節(jié)點時,該節(jié)點的父節(jié)點的同級節(jié)點需要被“撐開”:設(shè)該節(jié)點的1/2的區(qū)域高度為moveY,父節(jié)點的同級節(jié)點中,比父節(jié)點高的向上偏移一個moveY,比其低的向下偏移一個moveY,偏移操作可以調(diào)用svg的transform方法垂直平移節(jié)點。父節(jié)點的父節(jié)點的同級節(jié)點也做相同的處理,一直遞歸到根節(jié)點為止。當刪除一個節(jié)點時,節(jié)點的父節(jié)點的同級節(jié)點會被“壓低”,“壓低”操作和上述操作相似。注意,當增加第一個子節(jié)點和刪除最后一個節(jié)點時,不會進行“撐開”或者“壓低”操作。
圖5
4.1根節(jié)點的拖動
當拖動根節(jié)點時,整個思維導(dǎo)圖也會移動。實現(xiàn)這種效果的其中一個方案就是移動整個思維導(dǎo)圖內(nèi)的節(jié)點和連線。單個svg圖形的移動操作需要綁定mouseup、mousedown和mousemove三個事件,在移動時會不斷地觸發(fā)mousemove事件重新渲染svg圖形的位置,因此,移動svg圖形操作的性能并不高。移動單個svg對象不會有很大的性能瓶頸,但是如果一次性移動多個svg對象,在移動時會有明顯的“卡頓”感覺,嚴重影響用戶體驗。
實現(xiàn)整個思維導(dǎo)圖移動的另一個方案就是改變svg區(qū)域的視野。在svg中,視野是觀察世界的一個矩形區(qū)域,而世界是無窮大的。當改變視野的坐標時,視野中觀察到的物體的位置看上去好像是改變了,實際上是物體在世界上的位置沒有改變,而是觀察世界的矩形區(qū)域移動到了另外一個位置。如圖6和圖7所示,一開始物體在視野內(nèi)的中央,當將視野向右下移動時,物體的位置變成了在視野內(nèi)的左上角,效果上和將整個視圖左上角拖動是一樣的。
svg標簽提供了viewbox屬性來控制視野的范圍,而Raphale.js提供一個setViewBox的方法更便捷地控制視野。拖動根結(jié)點時改變svg視野的實現(xiàn)與上一個方案相比,既能達到拖動整個思維導(dǎo)圖的效果,性能上又能大幅度地提高。
4.2非根節(jié)點的拖動
本文實現(xiàn)的系統(tǒng)將拖動非根節(jié)點用于改變父子關(guān)系:節(jié)點拖動結(jié)束后,如果該節(jié)點與另一個節(jié)點重疊,則使重疊節(jié)點變?yōu)樵摴?jié)點的父節(jié)點,否則,節(jié)點回到原來的位置。
圖6
圖7
一個節(jié)點是Raphael.js的set對象,set對象可以監(jiān)聽鼠標事件。監(jiān)聽每個非根節(jié)點的mouseup、mousemove和movedown事件,分別對應(yīng)按下鼠標、鼠標移動、放下鼠標三個狀態(tài)。在按下鼠標狀態(tài)下,會以當前節(jié)點為原型克隆一個節(jié)點用于占位。在拖動鼠標狀態(tài)下,通過改變節(jié)點的坐標實現(xiàn)節(jié)點位置改變。在放下鼠標狀態(tài)下,會判斷當前拖動的節(jié)點是否與其他節(jié)點重疊,通過Raphael.js的isBBoxIntersect函數(shù)即可判斷兩個節(jié)點是否重疊,如果重疊,則改變節(jié)點的父子關(guān)系,實現(xiàn)流程如圖8:
圖8
本文主要闡述了如何使用SVG實現(xiàn)思維導(dǎo)圖,SVG具有對象化的特點,使得很容易地將思維導(dǎo)圖的節(jié)點與SVG對象對應(yīng)起來,且基于SVG對象所實現(xiàn)的事件機制,令繪圖時的交互實現(xiàn)變得簡單和高效。本文實現(xiàn)的系統(tǒng)將數(shù)據(jù)層和渲染層分離開來,讓數(shù)據(jù)和視圖解耦開來。本文介紹了如何繪制思維導(dǎo)圖的兩個基本元素——節(jié)點和邊;添加或刪除節(jié)點時各個節(jié)點位置都會有所改變,如何計算和渲染節(jié)點的位置是實現(xiàn)添刪節(jié)點功能的關(guān)鍵;本文還闡述了實現(xiàn)拖動節(jié)點改變父子關(guān)系的關(guān)鍵點。
參考文獻:
[1]高科.基于HTML5的數(shù)據(jù)可視化實現(xiàn)方法研究[J].科技傳播,2013,01:186-187.
[2]朱文.基于HTML5Canvas技術(shù)的在線圖像處理方法的研究[D].華南理工大學,2013.
[3]高峰,談俊忠. Java Script在基于SVG的網(wǎng)絡(luò)地圖中的應(yīng)用[J].江西師范大學學報(自然科學版),2004,03:262-265.
[4]徐曼.基于HTML5的統(tǒng)計圖表系統(tǒng)的研究與設(shè)計[D].武漢科技大學,2012.
[5]管英祥,任淵博,向為鋒.基于HTML Canvas的電磁態(tài)勢繪制方法[J].電腦知識與技術(shù),2015,14:68-70.
[6]王喬俊,何原榮,李佳楠.基于SVG的旅游地圖符號庫的設(shè)計與實現(xiàn)[J].湖北科技學院學報,2015,08:21-23.
[7]宋善德,熊展志,李衛(wèi)國,唐咸峰.基于SVG的矢量圖形編輯器的設(shè)計與實現(xiàn)[J].計算機工程與科學,2003,02:91-94.
任志豪(1993-),男,廣東花都人,本科生,現(xiàn)就讀于華南師范大學,研究方向為軟件工程
賴源勁(1994-),男,廣東番禺人,本科生,現(xiàn)就讀于華南師范大學,研究方向為數(shù)據(jù)庫
Implementation of Mind Map System Based on SVG
REN Zhi-hao,LAI Yuan-jin
(Software College, South China Normal University, Guangzhou 501631)
Abstract:The graphics technology which based on SVG, has characteristics of objectivity and convenient interaction, so it is suitable for the realization of the online mind map drawing. The basics functions of the mind map system based on the SVG include rendering node and edge, adding and removing nodes, changing nodes' position, dragging nodes. The key technical points of theses functions include calculating and rendering the nodes position.
Keywords:Mind Map; SVG; Realization
收稿日期:2015-12-31修稿日期:2016-03-02
作者簡介:book=75,ebook=76
文章編號:1007-1423(2016)09-0070-06
DOI:10.3969/j.issn.1007-1423.2016.09.018
基金項目:國家級大學生創(chuàng)新訓(xùn)練計劃(No.2014115)