拖拉到中間的div層可以拖動,拖動不能超過中間div的邊框:
jsPlumb.draggable(id);
$("#" + id).draggable({ containment: "parent" });
使用jquery裏的draggable爲其標記。並指定可以拖拽的範圍(侷限於我們的content容器)。如果想限制元素拖拽的範圍,只需要設置它的containment屬性。
$(".jq-draggable-incontainer").draggable({ containment: $( "#content" ).length ? "#content" : "document" });
jsPlumb是一個強大的JavaScript連線庫,它可以將html中的元素用箭頭、曲線、直線等連接起來,適用於開發Web上的圖表、建模工具等。它同時支持jQuery+jQuery UI、MooTools和YUI3這三個JavaScript框架,十分強大。大家可以在官網的Demo中看看它的功能。目前可用的jsPlumb中文資料很少,希望這篇教程可以幫助大家更快的瞭解jsPlumb。出於篇幅考慮,本教程將以jQuery爲例介紹jsPlumb。
瀏覽器兼容性
在使用jsPlumb之前,大家需要先了解一下各瀏覽器對jsPlumb的兼容性。jsPlumb支持IE6以上以及各大瀏覽器,但是仍然有一些bug:
- 在IE9上,由於jQuery1.6.x和1.7.x的SVG相關實現有一個bug,會導致鼠標停留事件無法響應
- Safari5.1上有一個SVG的bug,會導致鼠標事件無法通過SVG元素的透明區域傳遞
- 在Firefox11上基於MooTools使用SVG時會出現一些問題
下載和引入
- <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
- <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.min.js"></script>
- <script type="text/javascript" src="PATH_TO/jquery.jsPlumb-1.4.0-all-min.js "></script>
初始化
- jsPlumb.ready(function() {
- ...
- // some code
- ...
- });
- jsPlumb.importDefaults({
- DragOptions : { cursor: 'pointer'}, //拖動時鼠標停留在該元素上顯示指針,通過css控制
- PaintStyle : { strokeStyle:'#666' },//元素的默認顏色
- EndpointStyle : { width:20, height:16, strokeStyle:'#666' },//連接點的默認顏色
- Endpoint : "Rectangle",//連接點的默認形狀
- Anchors : ["TopCenter"]//連接點的默認位置
- });
- var exampleDropOptions = {
- hoverClass:"dropHover",//釋放時指定鼠標停留在該元素上使用的css class
- activeClass:"dragActive"//可拖動到的元素使用的css class
- };
添加jsPlumb連接點
- var color1 = "#316b31";
- var exampleEndpoint1 = {
- endpoint:["Dot", { radius:11 }],//設置連接點的形狀爲圓形
- paintStyle:{ fillStyle:color1 },//設置連接點的顏色
- isSource:true, //是否可以拖動(作爲連線起點)
- scope:"green dot",//連接點的標識符,只有標識符相同的連接點才能連接
- connectorStyle:{ strokeStyle:color1, lineWidth:6 },//連線顏色、粗細
- connector: ["Bezier", { curviness:63 } ],//設置連線爲貝塞爾曲線
- maxConnections:1,//設置連接點最多可以連接幾條線
- isTarget:true, //是否可以放置(作爲連線終點)
- dropOptions : exampleDropOptions//設置放置相關的css
- };
- var color2 = "rgba(229,219,61,0.5)";
- var exampleEndpoint2 = {
- endpoint:"Rectangle", //設置連接點的形狀爲矩形
- anchor:"BottomLeft", //設置連接點的位置,左下角
- paintStyle:{ fillStyle:color2, opacity:0.5 }, //設置連接點的顏色、透明度
- isSource:true, //同上
- scope:'yellow dot', //同上
- connectorStyle:{ strokeStyle:color2, lineWidth:4},//同上
- connector : "Straight", //設置連線爲直線
- isTarget:true, //同上
- maxConnections:3,//同上
- dropOptions : exampleDropOptions,//同上
- beforeDetach:function(conn) { //綁定一個函數,在連線前彈出確認框
- return confirm("Detach connection?");
- },
- onMaxConnections:function(info) {//綁定一個函數,當到達最大連接個數時彈出提示框
- alert("Cannot drop connection " + info.connection.id + " : maxConnections has been reached on Endpoint " + info.endpoint.id);
- }
- };
- var anchors = [[1, 0.2, 1, 0], [0.8, 1, 0, 1], [0, 0.8, -1, 0], [0.2, 0, 0, -1] ],
- maxConnectionsCallback = function(info) {
- alert("Cannot drop connection " + info.connection.id + " : maxConnections has been reached on Endpoint " + info.endpoint.id);
- };
- var e1 = jsPlumb.addEndpoint("state2", { anchor:"LeftMiddle" }, exampleEndpoint1);//將exampleEndpoint1類型的點綁定到id爲state2的元素上
- e1.bind("maxConnections", maxConnectionsCallback);//也可以在加到元素上之後綁定函數
- jsPlumb.addEndpoint("state1", exampleEndpoint1);//將exampleEndpoint1類型的點綁定到id爲state1的元素上
- jsPlumb.addEndpoint("state3", exampleEndpoint2);//將exampleEndpoint2類型的點綁定到id爲state3的元素上
- jsPlumb.addEndpoint("state1", {anchor:anchors}, exampleEndpoint2);//將exampleEndpoint2類型的點綁定到id爲state1的元素上,指定活動連接點
Html和CSS代碼
- <body>
- <div id="state1" class="item"></div>
- <div id="state2" class="item"></div>
- <div id="state3" class="item"></div>
- </body>
- <style type="text/css">
- .dragActive { border:2px dotted orange; } //當拖動一個連接點時,可連接的連接點會自動使用該css
- .dropHover { border:1px dotted red; } //當拖動一個連接點到可連接的點時,該點會自動使用該css
- .item {
- border: 1px solid black;
- background-color: #ddddff;
- width: 100px;
- height: 100px;
- position: absolute;
- }
- #state1 {
- left: 100px;
- top: 100px;
- }
- #state2 {
- left: 250px;
- top: 250px;
- }
- #state3 {
- left: 100px;
- top: 250px;
- }
- </style>
介紹
使用svg完成畫圖,四個概念:
anchor: endpoint在的位置,可通過name訪問
endpoint:connection的一端節點,通過addPoint makeSource, connect創建,
connector:連接線
overlay:connector的裝飾組件,如labelarrow
一個connection由一個connector,兩個endpoint,0或者多個overlay構成,每個endpoint有一個關聯的anchor.
anchor有四種類型:
static:固定不可移動的點
dynamic:從一組staticanchor中,每次動態選取一個合適的
perimeter:沿着特定圖形的邊
continous:沒有固定的位置,根據線另一端點的朝向決定
connectors四種類型
Bezier(default):彎曲的貝塞爾曲線,參數是curviness用來定義控制點,默認150
Straight:stub和gap(線和endpoint的間隔)
flowchart: 折線,有stubalwaysRespectStubs gap midpoint(轉折點)和cornerRadius
statemachine:輕微彎曲的線,quadraticBezier(二次貝塞爾曲線),有margin,curviness, proximityLimit(連接到自己時最小距離)
endpoint有四種類型
dot:radius cssClass hoverClass
rectangle:width height cssClas hoverClass
blank:不可見,對於可拖拽鏈接的,使用dot和rectangle參數,並在css中設爲透明。
image:從url得到image,src cssClass hoverClass
overlay有五種類型
arrow:控制長寬等
label:
plainArrow:三角形箭頭
diamond:菱形
custom:任意的dom元素,需要實現create方法
location:
[0..1]代表connector上的比例
大於1或小於0代表絕對值的pixel
connections:
adding:在connectmakeSource addEndpoint中
show和hideoverlay:connection.hideOverlay,connection.showOverlay ,endPoint有同樣的方法
removeoverlay:connection.removeOverlay()
重用commonsetting
<span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">var common = {</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"> </span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">anchors:[ "BottomCenter", "TopCenter" ],</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"> </span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">endpoints:["Dot", "Blank" ]</span></span></span></code></span> <span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">};</span></span></span></code></span> <span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">jsPlumb.connect({ source:"someElement", target:"someOtherElement" }, common);</span></span></span></code></span> <span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">jsPlumb.connect({ source:"aThirdElement", target:"yetAnotherElement" }, common);</span></span></span></code></span>
畫連線
<span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 6pt;">設置</span></span><span style="font-family: 'Courier New', monospace;"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 6pt;">endPoint</span></span></span><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 6pt;">爲 </span></span><span style="font-family: 'Courier New', monospace;"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 6pt;">source</span></span></span></span> <span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">var exampleGreyEndpointOptions = {</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"> </span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">endpoint:"Rectangle",</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"> </span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">paintStyle:{ width:25, height:21, fillStyle:'#666' },</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"> </span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">isSource:true,</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"> </span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">connectorStyle : { strokeStyle:"#666" },</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"> </span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">isTarget:true</span></span></span></code></span> <span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">};</span></span></span></code></span> <span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">var endpoint = jsPlumb.addEndpoint('elementId', exampleGreyEndpointOptions </span></span></span></code></span> <span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">);</span></span></span></code></span>
makeTarget和makeSource
將整個element變成target或source
<span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">jsPlumb.makeSource("el1", {</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"> </span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">anchor:"Continuous",</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"> </span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">endpoint:["Rectangle", { width:40, height:20 }],</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"> </span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">maxConnections:3</span></span></span></code></span> <span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">});</span></span></span></code></span>
uniqueEndpoint:
設置只有一個 endPoint
移除connections和endpoint
<span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">jsPlumb.detach(conn);</span></span></span></code></span> <code class="cjk"><span style="color: rgb(51, 51, 51);"><span style="background: transparent;"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">刪除</span></span></span></span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">el</span></span></span></code></span><code class="cjk"><span style="color: rgb(51, 51, 51);"><span style="background: transparent;"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">上所有鏈接:</span></span></span></span></code><span style="font-family: 'Courier New', monospace;"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 6pt;">jsPlumb.detachAllConnections(el, [params])</span></span></span></span> <span style="color: rgb(51, 51, 51);"><span style="background: transparent;"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 6pt;">所有鏈接:</span></span></span><span style="font-family: 'Courier New', monospace;"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 6pt;"><span style="background: transparent;">jsPlumb.detachEveryConnection();</span></span></span></span></span>
通過connect中的parameters參數向jsPlumb中傳入參數。
動畫:
<span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">jsPlumb.animate : function(el, properties, options) </span></span></span></code></span><code class="cjk"><span style="color: rgb(51, 51, 51);"><span style="background: transparent;"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">調用方式類似於 </span></span></span></span></code><span style="font-family: 'Courier New', monospace;"><code class="western"><span style="color: rgb(51, 51, 51);"><span style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace;"><span style="font-size: 7pt;">jQuery</span></span></span></code></span>
查詢jsPlumb
可以用來查找connection或者endpoint等
css樣式
overlays: cssClass
paintStyle可設置:fillStyle strokeStyle lineWidth outlineWidth outlineColordashStyle
hoverpaint style
hoverPaintStyle endpointHoverStyles
使用 JsPlumb 繪製拓撲圖的通用方法
示例工程見: http://download.csdn.net/detail/shuqin1984/6488513
一、 實現目標
繪製拓撲圖, 實際上是個數據結構和算法的問題。 需要設計一個合適的數據結構來表達拓撲結構,設計一個算法來計算拓撲節點的位置及連接。
二、 實現思想
1. 數據結構
首先, 從節點開始。 顯然, 需要一個字段 type 表示節點類型, 一個字段 data 表示節點數據(詳情), 對於連接, 則採用一個 rel 字段, 表示有哪些節點與之關聯, 相當於C 裏面的指針。 爲了唯一標識該節點, 還需要一個字段 key 。 通過 type-key 組合來唯一標識該節點。結合Javascript 標準的數據格式 JSON, 定下數據結構如下:
a. 節點數據結構: node = { type: 'nodeType', key: 'nodeKey', rel: [], data: {'More Info'}}
b. rel, data 可選 , type-key 唯一標識該節點, rel 不存在表示該節點爲葉子節點
c. 關聯關係: rel: [node1, node2, ..., nodeN]
d. 節點詳情: 關於節點的更多信息可放置於 data 字段中
2. 算法
在算法上, 要預先規劃好各個節點類型如何佈局以及如何連接。 連接方向很容易定: 根據起始節點及終止節點的類型組合, 可以規定不同的連接方向, 比如 <VIP, VM>的連線方向爲<TopCenter, LeftCenter>, <VM,VIP> 的連線方向爲 <TopCenter, BottomCenter>。 節點位置的確定是一個關鍵問題, 該算法的實現難易取決於拓撲數據結構的設計。 這裏採用的方法是: 採用深度優先遍歷, 下一個的節點位置通過<上一個節點位置, 上一個節點類型, 下一個節點類型> 確定, 如果上一個節點有多個相同類型的後繼節點, 那麼這多個後繼節點的位置是重合的, 需要在後面進行調整。實際上, 這個節點位置的算法是比較笨拙的, 如果有更好的算法, 請告知。
3. JsPlumb
jsPlumb 有幾個基本概念。 首先, 拓撲節點實際上是 DIV 區域,每個DIV 都必須有一個ID,用於唯一標識該節點。 連接拓撲節點的一個重要概念是EndPoint . EndPoint 是附着於節點上的連接線的端點, 簡稱“附着點”。 將附着點 attach 到指定拓撲節點上的方法如下:
jsPlumb.addEndpoint(toId, this.sourceEndpoint, { anchor: sourceAnchor, uuid:sourceUUID });
toId 是 拓撲節點的 DIV 區域的 ID 值, sourceEndpoint 是附着點的樣式設置, 可以複用 , sourceAnchor 是附着點位置, 共有八種,也就是四方形的八個邊緣:
- Top
(also aliased as TopCenter
)
- TopRight
- Right
(also
aliased as RightMiddle
) -
BottomRight
- Bottom
(also
aliased as BottomCenter
) -BottomLeft
- Left
(also
aliased as LeftMiddle
) - TopLeft
sourceUUID 是拓撲節點與附着位置的結合, 也就是說, 要將一個 附着點附着到拓撲節點爲 toId 的 sourceAnchor 指定的位置上。 每個拓撲節點都可以定義多個源附着點和目標附着點。 源附着點是連接線的起始端, 目標附着點是連接線的終止端。
兩個 uuid 即可定義一條連接線:
jsPlumb.connect({uuids:[startPoint, endPoint], editable: false});
startPoint 和 endPoint 分別是連接線的起始端 Endpoint uuid 和 終止段 Endpoint uuid. 它定義了從起始拓撲節點的指定附着點連接到終止拓撲節點的指定附着點。
三、 實現代碼
drawTopo.js 提供繪製拓撲圖的基本方法, 只要按照指定格式將數據結構扔進去, 就可以自動繪製出拓撲圖來。
<p style="margin-top: 0px; margin-bottom: 0px; padding-top: 10px; padding-bottom: 0px; list-style: none; border: 0px; line-height: 26px;"></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 10px; padding-bottom: 0px; list-style: none; border: 0px; line-height: 26px;"><br style="margin: 0px; padding: 0px; list-style: none;" /></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 10px; padding-bottom: 0px; list-style: none; border: 0px; line-height: 26px;"> <span style="margin: 0px; padding: 0px; list-style: none;"> 五、 小結</span></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 10px; padding-bottom: 0px; list-style: none; border: 0px; line-height: 26px;"> 這裏看到, 數據結構/算法與實際應用的一個很好的結合。 所以說, 絕不要忽視數據結構和算法的作用, 實際上, 只要仔細去發現, 去挖掘, 就會發現其大有用武之地。</p>