動機:
初めてバイナリーツリーを作ろうと思ったのは、会社の構造図を作る必要があったからです。 以前のアプローチは、グラフィック ソフトウェアを使用して直接絵を描くことでした。見た目は素晴らしいですが、変更があるたびに新しいものをペイントする必要があります。 一方、Web ページ上の行の表示とレイアウトはかなり制限されています。動的に生成されたデータに基づいて植字や配置を行うのは非常に難しく、美観も満足のいくものではありません。 いろいろ試した結果、データ操作には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 の利点の 1 つです。
また、トラバーサル操作を実行するために他のさまざまな方法を使用して失敗した後、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 つの子ノード (2、3) があります。
ノード 2 とノード 3 をノード 1 の左下と右下に配置するだけです。
ここでは、表示しやすいように、左側と右側のノードの接続線にそれぞれ緑と赤を使用しています。
XSL の再帰関数については前に説明しましたが、各詳細な
表示ステップをより明確に確認するには、次のコードを模倣してアラート ステートメントを追加するだけです。
<xsl:template match="FlowNode">…<SCRIPT language="JavaScript1.2">…alert('逐步显示');…</SCRIPT>…</xsl:template>
二分木のアイデア (2)
私のアイデアは非常に単純です:
(1) 現在のノードのデータを読み取り、VML を使用して新しいオブジェクトを生成します。
オブジェクトに初期値を割り当てます(名前、ID、スタイルなど)
(2) スクリプトコントロールを使用して現在のオブジェクトを配置します
(3) 現在のノードとその親ノードの間に矢印と線を追加します。
(4) 現在のノードの子ノードを探し続け、最後までループします。
つまり、すべてのノードが走査され、ツリーが生成されました。
<xsl:template match="FlowNode">…<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>
最初のテンプレート
は、現在のノードの各サブノードのテンプレートと一致するときに次の 2 つのテンプレートを呼び出します。後の 2 つのテンプレートは、特定の実行中に最初のテンプレート
を呼び出します。これは再帰関数と同等です。
それ以外の場合、一致するノードは、
検索条件が同じなので、戻り値も同じになります。
ただ、使われる場面によって書き方が違うだけです。
(1)
いくつかの変数とノードがここで定義されていますこれらの変数を基に計算式を呼び出して位置決めを行います。
root_left //ルートの左マージン = すべてのリーフの割り当てられた幅 (y*10) + すべてのリーフの幅 (y*50) + 左マージンの基本値 (10)
root_top //ルートの上マージン = 基本の上マージン Value (10)
objOval_iProcess //現在のオブジェクトのステップ値
objParentOval //現在のオブジェクトの親ノードはオブジェクトです
objParentOval_iProcess //現在のオブジェクトの親ノードのステップ値
objParent_name //現在のオブジェクトの親ノードの名前
Leaf_left //現在のオブジェクトのすべての子ノードの左の葉の数
Leaf_right //右の葉の数現在のオブジェクトのすべての子ノード内
Leaf_sum //現在のオブジェクトのすべての子ノード内のリーフの数
Leaf: 現在のノードには子ノードがないことを意味します
ノードの位置決め式:
(1) 現在のノードはルートノードです
//根的位置 SobjOval.style.left=parseInt(root_left); SobjOval.style.top=parseInt(root_top); //parseInt() 函数的作用是取整数值,如果不是则为NAN //isNaN()函数的作用是判断parseInt取得的是否为整数
(2) 現在のノードは親ノードの左の子ノードです
…
2) 右側の子リーフがある場合、式は次のようになります: 現在のノードの左側 = 親ノードの左側 - 現在のノードの右側の子リーフ の合計幅 -現在のノードの幅
3) 右の子リーフがなく、左の子リーフがある場合、式は次のようになります:
現在のノードの左 = 親ノードの左 - ノードの合計幅現在のノードの左の子リーフ
4) 現在のノード自体がリーフの場合、式は次のようになります:
現在のノードの左 = 親ノードの左 - 現在のノードの幅
1) 判定の条件は、現在のオブジェクトの親ノードの名前 = 'iNextNo'
…
2) がある場合左側の子リーフ、式は次のとおりです: 現在のノードの左側 = 親ノードの左側 + 現在のノードの左側の子リーフ 合計の幅 + 現在のノードの幅
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) <xsl:value-of select="./iProcess/text()" /> ;(2) {./iProcess/text()}
(1)和(2)的作用都是返回由 select 参数给出的表达式的字符串值。
他们的搜索条件相同,所以返回的值也一样。
只不过是使用的场合不同,他们的书写形式也就不一样。
<xsl:apply-templates select="team" order-by="blue_ID"/>
比如我们想生成以下代码
内容
我们假设名称为“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" 的标准里
<xsl:value-of select="./book/text()"/>
作用是:只是把他的文本值写出来,而
<xsl:value-of select="./book"/>
是把他的文本值和他的所有子节点的内容显示出来。
大家可以试验一下,输出一个有子节点的,一个无子节点的
看看显示的结果是否相同。
(2)需要注意:
IE5 不支持
要用
<tag><xsl:attribute name="att"><xsl:value-of select="xpath"></xsl:attribute>
命名空间要用
xmlns:xsl="http://www.w3.org/TR/WD-xsl" <?xml version="1.0" encoding="gb2312" ?>
另外说一点:
在大多的XML教科书中所显示的代码中很少会加上encoding="gb2312" ,
因此我们在XML中用到中文的时候会报错,原因就是没有写这个申明。
后记:
这里说的是一种思路。如果触类旁通,自然能够派上用场。
以上就是XML卷之实战锦囊(5):结构树图的内容,更多相关内容请关注PHP中文网(www.php.cn)!