拖拉到中间的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.youkuaiyun.com/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>