[導讀] 最初想起做二元樹是因為需要做一個公司結構圖。 以前的做法都是直接用圖象軟體畫出來一張圖片。很好看,但每次有變動後都需要重新畫一個新的。 另一方面,網頁上線條的顯示、版面相當侷限。根據動態產生的數字
最初想起做二元樹是因為需要做一個公司結構圖。 以前的做法都是直接用圖象軟體畫出來一張圖片。很好看,但每次有變動後都需要重新畫一個新的。 另一方面,網頁上線條的顯示、版面相當侷限。根據動態產生的資料進行排版、定位都相當困難, 而且在美觀上也差強人意。 做了各種嘗試以後,決定用xml+XSL作資料運算; 用VML來美化線條,用javaSCRipT來定位物件。
材質:
XML磁碟區之結構樹圖
有2個檔案:flow2.xml 和flow2.xsl
效果:
瀏覽這裡
講解:
二元樹思路(1)
<html xmlns:v="urn:schemas-microsoft-com:vml"> <STYLE> v\:* { BEHAVIOR: url(#default#VML) } </STYLE> <v:group id="group1" name="group1" coordsize = "100,100"> … </v:group>
以上這些都是VML的基本格式,我就不詳細講解了。
XML是樹形結構,我們讀取每個資料就需要對這個
XML資料樹進行遍歷。而遞歸運算是XSL優勢之一。
我也是在用其它多種方法進行遍歷運算失敗後才
決定使用XSL的。
<FlowRoot> <vcTitle>二叉树--结构图</vcTitle> <Author>Sailflying</Author> <Email>sailflying@163.net</Email> <FlowNode> <iPRocess>1</iProcess> <vcCourse>第一个节点</vcCourse> <iNextYes> <FlowNode> <iProcess>2</iProcess> <vcCourse>第二个节点</vcCourse> <iNextYes>…</iNextYes> <iNextNo>…</iNextNo> </FlowNode> </iNextYes> <iNextNo> <FlowNode> <iProcess>3</iProcess> <vcCourse>第三个节点</vcCourse> <iNextYes>…</iNextYes> <iNextNo>…</iNextNo> </FlowNode> </iNextNo> </FlowNode> </FlowRoot>
邏輯上很簡單,目前節點(1)下面有兩個子節點(2,3)。
只需要將節點2和節點3定位在節點1的左下方和右下方就可以了。
這裡我將左右節點的連接線分別用了綠色和紅色,方便顯示。
前面我們說到了XSL的遞歸功能,為了更清楚的看到每一個詳細的
顯示步驟,只需要仿照下面的程式碼,加上一個alert語句就可以了。
<xsl:template match="FlowNode"> … <SCRIPT language="Javascript1.2"> … alert('逐步显示'); … </SCRIPT> … </xsl:template>
看了上面的慢動作,是否能讓大家了解到我的想法。
二元樹思路(2)
我的想法很簡單:
(1)讀取目前節點的資料,用VML產生一個新的物件。
為物件賦初始數值(如name,id,style樣式等)
(2)用腳本控制來給目前物件定位
(3)目前節點和它的父親節點之間加箭頭,線條。
(4)繼續找目前節點的子節點,一直循環定位到結束。
也就是所有節點都遍歷完畢,已經生成好樹了。
a38aff74794773018dc1508133f44acc
… <xsl:apply-templates /> … </xsl:template> <xsl:template match="iNextYes"> <xsl:apply-templates select="./FlowNode" /> </xsl:template> <xsl:template match="iNextNo"> <xsl:apply-templates select="./FlowNode" /> </xsl:template>
整个递归过程就是靠上面这三个模块(template)来完成的。
第一个template在匹配当前节点中每一个子节点的模板的时候
调用了后面两个template; 而后面两个template又在具体执行
的时候调用了第一个template ,这就相当于一个递归函数。
语法:
要依次匹配当前节点中的每个子节点的模板,应使用该元
素的基本形式 c2ce39d392085d28c75651ff348a246e。
否则,匹配的节点由 select 参数中 XPath 表达式的值决
定,如 c0ee5204b92e8d51b2cab58f57fdffcb
(1)和(2)的作用都是返回由 select 参数给出的表达式的字符串值。
他们的搜索条件相同,所以返回的值也一样。
只不过是使用的场合不同,他们的书写形式也就不一样。
(1) c1269d3f6238f9c1d4a8ab62dc14721d
(2) {./iProcess/text()}
这里定义了一些变量,节点的定位就是根据这些变量来调用运算公式的。
root_left //根的左边距=所有叶子的分配宽度(y*10) + 所有叶子的宽度(y*50) + 左边距基本值(10)
root_top //根的上边距=上边距基本值(10)
objOval //当前对象,是一个object
objOval_iProcess //当前对象的步骤值
objParentOval //当前对象的父节点,是一个object
objParentOval_iProcess //当前对象父节点的步骤值
objParent_name //当前对象父节点的名称
Leaf_left //当前对象的所有子节点中的左边叶子数
Leaf_right //当前对象的所有子节点中的右边叶子数
Leaf_sum //当前对象的所有子节点中叶子数
叶子:是指当前节点没有子节点
节点的定位公式:
(1) 当前节点是根节点
//根的位置
SobjOval.style.left=parseInt(root_left);
SobjOval.style.top=parseInt(root_top);
//parseInt() 函数的作用是取整数值,如果不是则为NAN
//isNaN()函数的作用是判断parseInt取得的是否为整数
(2)当前节点是父节点的左边子节点
1)判断的条件是: 当前对象父节点的名称='iNextYes'
…
2)如果存在右边子叶子,则公式为:
当前节点的left=父节点的left - 当前节点的右边子叶子的总宽度- 当前节点的宽度
3)如果不存在右边子叶子,但存在左边子叶子,则公式为:
当前节点的left=父节点的left - 当前节点的左边子叶子的总宽度
4)如果当前节点本身就是叶子,则公式为:
当前节点的left=父节点的left - 当前节点的宽度
…
(3)当前节点是父节点的右边子节点
1)判断的条件是: 当前对象父节点的名称='iNextNo'
…
2)如果存在左边子叶子,则公式为:
当前节点的left=父节点的left + 当前节点的左边子叶子的总宽度 + 当前节点的宽度
3)如果不存在左边子叶子,但存在右边子叶子,则公式为:
当前节点的left=父节点的left + 当前节点的右边子叶子的总宽度
4)如果当前节点本身就是叶子,则公式为:
当前节点的left=父节点的left + 当前节点的宽度
…
(2)和(3)的公式都是得到当前节点的left,我们还需要得到当前节点的top
很简单的公式:当前节点的top=父节点的top + 偏移量(80)
二叉树思路(3)
连接线条的定位思路:
(1)找到当前节点和父节点的位置
(2)判断当前节点是父节点的左边子节点,还是右边子节点
(3)画线条
这里定义了一些变量。
objOval //当前节点,是一个object
objParentOval //当前对象的父节点,是一个object
objLine //当前线条,是一个object
线条的定位公式:
from="x1,y1" to="x2,y2" 是 VML 里定位线条的方式
当前节点是父节点的左边子节点,则公式为:
from = 父节点的left + 偏移量(15) , 父节点的top + 偏移量(32)
to = 父节点的left + 偏移量(30) , 父节点的top - 偏移量(2)
当前节点是父节点的右边子节点,则公式为:
from = 父节点的left + 偏移量(35) ,父节点的top + 偏移量(32)
to = 父节点的left + 偏移量(20) ,父节点的top - 偏移量(2)
我所能想到的也就这么多了。
如果只是单纯的做一个公司结构图的话,会更简单很多。
下面是赛扬的思路,我也是在他的基础上深入一点而已。
首先计算最下层节点个数,得出宽度,
然后应该根据节点的从属关系计算其上层节点位置,递归。
每一层级的节点要按从属关系先排序
首先设“基本值”=节点应向右偏移量
每个包含子节点的节点的left值等于它所拥有的节点所占宽度的一半加上基本值
后话:
最近不知为何,网络一直都不好。断线的时间比在线的时间多。
所以没对代码简化,其实,要完善的功能还有很多,比如:
需要加右键菜单
右键菜单内含新建节点、修改节点名称、改变关联关系等
在每一个节点上都可右键打开这个节点的右键菜单
讲解:
1)flow2.xml 是数据文件,相信大家都不会有问题。
2)flow2.xsl 是格式文件,有几个地方要注意。
(1)脚本中:
(1) c1269d3f6238f9c1d4a8ab62dc14721d ;
(2) {./iProcess/text()}
(1)和(2)的作用都是返回由 select 参数给出的表达式的字符串值。
他们的搜索条件相同,所以返回的值也一样。
只不过是使用的场合不同,他们的书写形式也就不一样。
613faffbac7ea3edce372d532402451f
比如我们想生成以下代码
bba950c6d155ac5073309a6041296da9内容94b3e26ee717c64999d7867364b1b4a3
我们假设名称为“name”,参数值为XML数据中当前节点下面的子节点book的值
第一种写法是先加属性名称,再加参数值
<p> <xsl:attribute name="name"> <xsl:value-of select="./book/text()"/> </xsl:attribute> 内容 </p>
第二种写法是直接加属性名称和参数值
<p name="{./book/text()}">内容</p>
具体的使用你可以看我写的代码中的例子。
XSL在正式的 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 的标准里
133fad1149361bc8e01bc3010ffc4318
作用是:只是把他的文本值写出来,而
bc2c50059d4a5ad633326b775d856e8c
是把他的文本值和他的所有子节点的内容显示出来。
大家可以试验一下,输出一个有子节点的,一个无子节点的
看看显示的结果是否相同。
(2)需要注意:
IE5 不支持 c55c83ef8ee979923031582b93a38c44
要用
8bf259f5a6144433b921fb8b7de949701e67e513ee09a39a9f0027cf5f43c1b071182dad282cff473f82d9ca3774f48574840d2e8628076055d931bf84be95ea
命名空间要用
xmlns:xsl="http://www.w3.org/TR/WD-xsl"
4d00dc9680f278f9620dee005285d2e1
另外说一点:
在大多的XML教科书中所显示的代码中很少会加上encoding="gb2312" ,
因此我们在XML中用到中文的时候会报错,原因就是没有写这个申明。
以上是XML實戰秘籍第五卷:結構樹圖的詳細內容。更多資訊請關注PHP中文網其他相關文章!