上    篇

前言:之前项目里面用到了Web里面的拖拽流程图的技术JsPlumb,其实真不算难,不过项目里面用HTML做的一些类似flash的效果,感觉还不错,在此分享下。

Jsplumb官网:https://jsplumbtoolkit.com

GitHub:https://github.com/sporritt/jsplumb/

一、效果图展示

1、从左边拖动元素到中间区域,然后连线

2、连线类型可以自定义:这里定义为直线、折线、曲线。实际项目中根据业务我们定义为分装线、分装支线、总装线等

3、鼠标拖动区域选中元素,并且选中元素统一拖动位置。

4、对选中的元素左对齐。

5、对选中元素居中对齐

6、右对齐

7、上对齐

8、垂直居中对齐

9、下对齐

10、根据第一个选中的元素上下靠拢

11、根据第一个选中的元素左右靠拢

12、根据第一个选中的元素同高

13、根据第一个选中的元素同宽

14、选中元素顺时针旋转,点击一次旋转45度

15、选中元素逆时针旋转。

16、选中统一删除元素以及元素上面的连线

这里很多效果其实在项目中作用并不太大,很多单纯就是为了展示用的。没办法,领导要求,我们就只有做喽。

二、代码详解

这里涉及的效果比较多,可能要分多篇来介绍。这篇还是来看看构造流程图的核心技术:JsPlumb。

1、概述

关于JsPlumb的内容,园子里也有很多朋友做过分享,也有写得不错的博文。在此就简单说明下吧。jsPlumb是一个强大的JavaScript连线库,它可以将html中的元素用箭头、曲线、直线等连接起来,适用于开发Web上的图表、建模工具等。它同时支持jQuery+jQuery UI、MooTools和YUI3这三个JavaScript框架,十分强大。本项目中还是结合大家最熟悉的JQuery来讲解。并且还要注意的一点就是JsPlumb的浏览器兼容性,JsPlumb支持IE 6以上、火狐、谷歌等各种浏览器

2、使用

(1)引入JS文件

可以直接去官网上面下载最新的js库,由于JsPlumb需要JQuery的支持,按照网上的说法,它只兼容jQuery1.3.x及以上版本,并在jQuery UI 1.7.x、1.8.x及1.9.x上测试通过。所以我们需要下载较高一点版本的JQuery和JQuery UI。关于JsPlumb的内容只需要引用一个Js即可。类似

<script src="~/Content/jquery-1.9.1.min.js"></script>
<script src="~/Content/jquery-ui-1.11.4.custom/jquery-ui.js"></script>
<link href="~/Content/jquery-ui-1.11.4.custom/jquery-ui.min.css" rel="stylesheet" />
<script src="~/Content/jsPlumb-master/dist/js/jquery.jsPlumb-1.7.5.js"></script>

(2)初始化

使用JsPlumb需要注意一点,JsPlumb的连线的样式是由点确定的,也就是说点的样式里面包含了相关的属性来说明当使用此点来连线的时候,连线的样式应该是什么样的。

在我们项目里面,左边的模型区域,中间才是设计区域。那么要将一个元素从模型区域创建出来,就要用到我们JQuery UI里面的draggable和droppable事件。首先我们注册左边模型的draggable和中间区域的droppable事件。

cshtml页面代码,<div id="divContentLeftMenu">这个是左边模型的容器,<div id="divCenter"></div>表示中间区域容器。

      <div id="divContentLeftMenu">
<div class="sidebar-menu" id="divSidebar">
<a href="#plantmodel" onclick="Resize()" class="nav-header menu-first collapsed" data-toggle="collapse">工厂模型</a>
<ul id="plantmodel" class="nav nav-list collapse menu-second">
</ul>
<a href="#artlinemodel" onclick="Resize()" class="nav-header menu-first collapsed" data-toggle="collapse">工艺段模型</a>
<ul id="artlinemodel" class="nav nav-list collapse menu-second">
<li>
<a href="#">
<div class="node radius" id="node4" dbtype="DTO_TM_ART_LINE">
<label>工段</label>
</div>
</a>
</li>
<li>
<a href="#">
<div class="node" id="node5" dbtype="DTO_TM_ULOC">
<label>工位</label>
</div>
</a>
</li>
</ul>
</div>
</div>
<div id="divCenter"></div>

Js代码:

首先我们定义几个点的样式的全局变量

//基本连接线样式
var connectorPaintStyle = {
strokeStyle: "#1e8151",
fillStyle: "transparent",
radius: 5,
lineWidth: 2
};
// 鼠标悬浮在连接线上的样式
var connectorHoverStyle = {
lineWidth: 3,
strokeStyle: "#216477",
outlineWidth: 2,
outlineColor: "white"
};
var endpointHoverStyle = {
fillStyle: "#216477",
strokeStyle: "#216477"
};
//空心圆端点样式设置
var hollowCircle = {
DragOptions: { cursor: 'pointer', zIndex: 2000 },
endpoint: ["Dot", { radius: 7 }], //端点的形状
connectorStyle: connectorPaintStyle,//连接线的颜色,大小样式
connectorHoverStyle: connectorHoverStyle,
paintStyle: {
strokeStyle: "#1e8151",
fillStyle: "transparent",
radius: 5,
lineWidth: 2
}, //端点的颜色样式
//anchor: "AutoDefault",
isSource: true, //是否可以拖动(作为连线起点)
connector: ["Straight", { stub: [0, 0], gap: 10, cornerRadius: 5, alwaysRespectStubs: true }], //连接线的样式种类有[Bezier],[Flowchart],[StateMachine ],[Straight ]
isTarget: true, //是否可以放置(连线终点)
maxConnections: -1, // 设置连接点最多可以连接几条线
connectorOverlays: [["Arrow", { width: 10, length: 10, location: 1 }]]
};

然后再页面初始化完成之后注册事件

        $(function(){
//左边区域的draggable事件
$("#divContentLeftMenu .node").draggable({
helper: "clone",
scope: "plant"
}); //中间拖拽区的drop事件
$("#divCenter").droppable({
scope: "plant",
drop: function (event, ui) {
// 创建工厂模型到拖拽区
CreateModel(ui, $(this));
}
});
        });
//1.创建模型(参数依次为:drop事件的ui、当前容器、id、当前样式)
function CreateModel(ui, selector) {
//1.1 添加html模型
var modelid = $(ui.draggable).attr("id");
i++;
var id = modelid + i;
var cur_css = modelid;
var type = $(ui.helper).attr("dbtype");
$(selector).append('<div class="node ' + cur_css + '" id="' + id + '" dbtype="' + type + '" parentid="' + $(selector).attr("id") + '" onclick="oInitElement.GetPropertiesByType(\'' + type + '\',this)" ondblclick="InitStation().DbClick(\'' + type + '\',this)" >' + $(ui.helper).html() + '</div>');
var left = parseInt(ui.offset.left - $(selector).offset().left);
var top = parseInt(ui.offset.top - $(selector).offset().top);
$("#" + id).css("left", left).css("top", top);
//jsPlumb.setContainer($("#divCenter"));
//1.2 添加连接点
jsPlumb.addEndpoint(id, { anchors: "RightMiddle" }, hollowCircle);
jsPlumb.addEndpoint(id, { anchors: "LeftMiddle" }, hollowCircle);
jsPlumb.addEndpoint(id, { anchors: "TopCenter" }, hollowCircle);
jsPlumb.addEndpoint(id, { anchors: "BottomCenter" }, hollowCircle);
jsPlumb.draggable(id); //1.3 注册实体可draggable和resizable
$("#" + id).draggable({
containment: "parent",
start: function () {
startMove();
},
drag: function (event, ui) {
MoveSelectDiv(event, ui, id);
jsPlumb.repaintEverything();
},
stop: function () {
jsPlumb.repaintEverything();
}
}); $("#" + id).resizable({
resize: function () {
jsPlumb.repaintEverything();
},
stop: function () {
jsPlumb.repaintEverything();
//oInitElement.SendPropRequest("DTO_TM_PLANT", $(this));
}
});
return id;
};

重点来看看这一句:

jsPlumb.addEndpoint(id, { anchors: "RightMiddle" }, hollowCircle);

调用了JsPlumb里面的addEndpoint方法,第一个参数表示页面标签的id,第一个表示连线点的位置(RightMiddle、LeftMiddle、TopCenter、BottomCenter四个选项);第三参数表示点的样式以及连线的样式。没调用依次addEndpoint方法,元素上面就会多一个连线的节点。关于hollowCircle里面各个参数的意义,可以查看api。

还有一句多个地方都看到了:

jsPlumb.repaintEverything();

看字面意思大概能知道这句是干什么的,修复所有。当在中间区域拖动元素的时候,如果不带这一句,节点不会跟着元素一起移动。加上之后节点才会跟随标签移动。至此,最基础的JsPlumb连线就完成了。源码在下篇。


下    篇

前言:在上篇介绍了下JsPlumb在浏览器里面画流程图的效果展示,以及简单的JsPlumb代码示例。这篇还是接着来看看各个效果的代码说明。

一、设置连线的样式和颜色效果代码示例

大概的效果如图:

这些效果看着很简单,那么,我们如何用代码去实现它呢。上章我们说过,JsPlumb的连线样式是由点的某些属性决定的,既然如此,我们就通过设置点的样式来动态改变连线的样式即可。来看代码:

首先来看看连线类型的那个select

 <div id="btn_linetype" class="divMenuBtn btn-default btn">
连线类型:
  <select id="sel_linetype" style="width:80px;height:20px">
    <option value="2">直线</option>
    <option value="1">折线</option>
    <option value="3">曲线</option>
  </select>
</div>

在页面初始化的时候注册select的change事件

//全局的空心圆端点样式设置
var hollowCircle = {
DragOptions: { cursor: 'pointer', zIndex: 2000 },
endpoint: ["Dot", { radius: 7 }], //端点的形状
connectorStyle: connectorPaintStyle,//连接线的颜色,大小样式
connectorHoverStyle: connectorHoverStyle,
paintStyle: {
strokeStyle: "#1e8151",
fillStyle: "transparent",
radius: 5,
lineWidth: 2
}, //端点的颜色样式
//anchor: "AutoDefault",
isSource: true, //是否可以拖动(作为连线起点)
connector: ["Straight", { stub: [0, 0], gap: 10, cornerRadius: 5, alwaysRespectStubs: true }], //连接线的样式种类有[Bezier],[Flowchart],[StateMachine ],[Straight ]
isTarget: true, //是否可以放置(连线终点)
maxConnections: -1, // 设置连接点最多可以连接几条线
connectorOverlays: [["Arrow", { width: 10, length: 10, location: 1 }]]
};
//页面初始化完成之后
$(function () {
//连线样式下拉框的change事件
$("#sel_linetype").change(function () {
var strlinetype = "";
var strlinecolor = "";
//设置新添加元素的节点的连线样式
//直线的样式和样色
if ($(this).val() == "1") {
strlinetype = "Flowchart";
strlinecolor = "red";
hollowCircle.connector = ["Flowchart", { stub: [0, 0], gap: 10, cornerRadius: 5, alwaysRespectStubs: true }];
}
//折线的样式和颜色
else if ($(this).val() == "2") {
strlinetype = "Straight";
strlinecolor = "green";
hollowCircle.connector = ["Straight", { stub: [0, 0], gap: 10, cornerRadius: 5, alwaysRespectStubs: true }];
}
//曲线的样式和颜色
else if ($(this).val() == "3") {
strlinetype = "Bezier";
strlinecolor = "orange";
hollowCircle.connector = ["Bezier", { stub: [0, 0], gap: 10, cornerRadius: 5, alwaysRespectStubs: true }];
}
//设置已经存在的所有的连接点的连线样式
var arrnode = $("#divCenter").find(".node");
for (var i = 0; i < arrnode.length; i++) {
var arrendpoints = jsPlumb.getEndpoints($(arrnode[i]).attr("id"));
if (arrendpoints == undefined || arrendpoints == null) {
return;
}
var oconnector = arrendpoints[0].connector;
if (oconnector == null || oconnector == undefined) {
return;
}
oconnector[0] = strlinetype;
var oconnectstyle = arrendpoints[0].connectorStyle;
if (oconnectstyle == null || oconnectstyle == undefined) {
return;
}
oconnectstyle.strokeStyle = strlinecolor;
}
});
});

其实也就几行代码,设置已经存在和将要拖动到界面上面的端点的连线样式。

二、全选、全选拖动效果代码示例

可以选中元素,批量拖动元素和连线,大概效果:

看看实现代码:

1、初始化的时候注册可选中

$(function () {
var oRegionSelect = new RegionSelect({
region: '#divCenter div.node',
selectedClass: 'seled',
parentId: "divCenter"
});
oRegionSelect.select();
});

2、选中相关方法

var _selectedRegions = [];
//var selProp; function RegionSelect(selRegionProp) {
//selProp = selRegionProp;
this.regions = [];
this.selProp = selRegionProp;
this.InitRegions(selRegionProp);
this.selectedClass = selRegionProp["selectedClass"];
this.selectedRegion = [];
this.selectDiv = null;
this.startX = null;
this.startY = null;
this.parentId = selRegionProp["parentId"];
} RegionSelect.prototype.InitRegions = function () {
var _self = this;
_self.regions = [];
var _regions = document.getElementsBySelector(_self.selProp["region"]);//$("#divCenter > .node");// var bSelect = true;
if (_regions && _regions.length > 0) {
for (var i = 0; i < _regions.length; i++) {
_regions[i].onmousedown = function () {
bSelect = false;
var evt = window.event || arguments[0];
if (!evt.shiftKey && !evt.ctrlKey) {
if ($.inArray(this, _selectedRegions) === -1) {
// 清空所有select样式
_self.clearSelections(_regions);
this.className += " " + _self.selectedClass;
// 清空selected数组,并加入当前select中的元素
_selectedRegions = [];
_selectedRegions.push(this);
}
} else {
if (this.className.indexOf(_self.selectedClass) == -1) {
this.className += " " + _self.selectedClass;
_selectedRegions.push(this);
} else {
this.className = this.className.replaceAll(_self.selectedClass, "");
_selectedRegions.remove(this);
}
}
clearEventBubble(evt);
}
this.regions.push(_regions[i]);
}
} if (bSelect) {
// 清空所有select样式
_self.clearSelections(_regions);
// 清空selected数组,并加入当前select中的元素
_selectedRegions = [];
}
} RegionSelect.prototype.select = function () {
var _self = this;
var sDivId = _self.parentId;
var intMousePosition = [0, 0];
var intOriginalPosition = [0, 0];
var parentWidth = parseInt(document.getElementById(sDivId).parentElement.offsetWidth);
var parentHeight = parseInt(document.getElementById(sDivId).parentElement.offsetHeight);
addEvent("mousedown", function () {
var evt = window.event || arguments[0];
var buttonType = evt.buttons || evt.button;
if (evt.target != undefined) {
if (evt.target.id !== sDivId) return;
}
if (evt.srcElement != undefined) {
if (evt.srcElement.id !== sDivId) return;
}
if (evt.buttons == undefined && buttonType == 0){
_self.onBeforeSelect(evt, sDivId);
}
if (buttonType === 1) {
_self.onBeforeSelect(evt, sDivId);
}
if (buttonType === 2) {
intMousePosition = [evt.clientX, evt.clientY];
var movX = parseInt(GetStyle(document.getElementById(sDivId), "left"));
var movY = parseInt(GetStyle(document.getElementById(sDivId), "top"));
intOriginalPosition = [movX, movY];
document.getElementById(sDivId).style.cursor = "move";
}
clearEventBubble(evt);
}, document); addEvent("mousemove", function () {
var evt = window.event || arguments[0];
//if (evt.target.id !== sDivId) return;
var buttonType = evt.buttons || evt.button;
if (evt.buttons == undefined && buttonType == 0) {
_self.onSelect(evt, sDivId);
}
if (buttonType === 1) {
_self.onSelect(evt, sDivId);
}
if (buttonType === 2) {
var newX = intOriginalPosition[0] + evt.clientX - intMousePosition[0];
var newY = intOriginalPosition[1] + evt.clientY - intMousePosition[1];
if (newX >= 0) {
newX = 0;
}
if (newY >= 0) {
newY = 0;
}
$("#" + sDivId).css("left", newX + "px");
$("#" + sDivId).css("top", newY + "px");
$("#" + sDivId).css("width", (parentWidth-newX) + "px");
$("#" + sDivId).css("height", (parentHeight-newY) + "px"); }
clearEventBubble(evt);
}, document); addEvent("mouseup", function () {
var evt = window.event || arguments[0];
var buttonType = evt.buttons || evt.button;
if (evt.buttons == undefined && buttonType == 0) {
}
if (buttonType === 1) {
}
document.getElementById(sDivId).style.cursor = "default";
_self.onEnd();
}, document);
} RegionSelect.prototype.onBeforeSelect = function (evt, sDivId) {
// 创建模拟 选择框
var _self = this;
_self.InitRegions(_self.selProp);
if (!document.getElementById("selContainer")) {
this.selectDiv = document.createElement("div");
this.selectDiv.style.cssText = "position:absolute;width:0px;height:0px;font-size:0px;margin:0px;padding:0px;border:1px dashed #0099FF;background-color:#C3D5ED;z-index:1000;filter:alpha(opacity:60);opacity:0.6;display:none;";
this.selectDiv.id = "selContainer";
document.getElementById(sDivId).appendChild(this.selectDiv);
} else {
this.selectDiv = document.getElementById("selContainer");
} this.startX = posXY(evt, sDivId).x;
this.startY = posXY(evt, sDivId).y;
this.isSelect = true; } RegionSelect.prototype.onSelect = function (evt, sDivId) {
var self = this;
if (self.isSelect) {
if (self.selectDiv.style.display == "none") self.selectDiv.style.display = ""; var posX = posXY(evt, sDivId).x;
var poxY = posXY(evt, sDivId).y;
self.selectDiv.style.left = Math.min(posX, this.startX) + "px";
self.selectDiv.style.top = Math.min(poxY, this.startY) + "px";
self.selectDiv.style.width = Math.abs(posX - this.startX) + "px";
self.selectDiv.style.height = Math.abs(poxY - this.startY) + "px"; var regionList = self.regions;
for (var i = 0; i < regionList.length; i++) {
if (self.selectDiv.parentNode.id !== regionList[i].parentNode.id) continue;
var r = regionList[i], sr = self.innerRegion(self.selectDiv, r);
if (sr && r.className.indexOf(self.selectedClass) == -1) {
r.className = r.className + " " + self.selectedClass;
_selectedRegions.push(r);
} else if (!sr && r.className.indexOf(self.selectedClass) != -1) {
r.className = r.className.replaceAll(self.selectedClass, "");
_selectedRegions.remove(r);
} }
}
} RegionSelect.prototype.onEnd = function () {
var self = this;
if (self.selectDiv) {
self.selectDiv.style.display = "none";
}
this.isSelect = false;
//_selectedRegions = this.selectedRegion;
} // 判断一个区域是否在选择区内
RegionSelect.prototype.innerRegion = function (selDiv, region) {
var s_top = parseInt(selDiv.style.top);
var s_left = parseInt(selDiv.style.left);
var s_right = s_left + parseInt(selDiv.offsetWidth);
var s_bottom = s_top + parseInt(selDiv.offsetHeight); var r_top = parseInt(region.offsetTop);
var r_left = parseInt(region.offsetLeft);
var r_right = r_left + parseInt(region.offsetWidth);
var r_bottom = r_top + parseInt(region.offsetHeight); var t = Math.max(s_top, r_top);
var r = Math.min(s_right, r_right);
var b = Math.min(s_bottom, r_bottom);
var l = Math.max(s_left, r_left); if (b > t + 5 && r > l + 5) {
return region;
} else {
return null;
} } RegionSelect.prototype.clearSelections = function (regions) {
for (var i = 0; i < regions.length; i++) {
regions[i].className = regions[i].className.replaceAll(this.selectedClass, "");
}
} function getSelectedRegions() {
return _selectedRegions;
} /*-------------------------------------- 区域选择方法结束 --------------------------------------------*/ function showSelDiv() {
var selInfo = "";
var arr = getSelectedRegions();
for (var i = 0; i < arr.length; i++) {
selInfo += arr[i].innerHTML + "\n";
} alert("共选择 " + arr.length + " 个文件,分别是:\n" + selInfo); } function MoveSelectDiv(event, ui,id) {
var arr = getSelectedRegions();
var iMoveLeft = ui.position.left - ui.originalPosition.left;
var iMoveTop = ui.position.top - ui.originalPosition.top; for (var i = 0; i < arr.length; i++) {
//if (arr[i].id === id) continue; if (arr[i].parentNode.id !== document.getElementById(id).parentNode.id) continue;
var iLeft = parseInt($(arr[i]).attr("bLeft"));
var iTop = parseInt($(arr[i]).attr("bTop"));
$(arr[i]).css("left", (iLeft + iMoveLeft) + "px");
$(arr[i]).css("top", (iTop + iMoveTop) + "px");
}
} function startMove() {
var arr = getSelectedRegions();
for (var i = 0; i < arr.length; i++) {
$(arr[i]).attr("bLeft", $(arr[i]).position().left);
$(arr[i]).attr("bTop", $(arr[i]).position().top);
}
}

选中相关方法

三、对齐、旋转代码示例

//左对齐
function SelectAlignLeft() {
var arr = getSelectedRegions();
var iLeft = 0;
var id = ""; for (var i = 0; i < arr.length; i++) {
if (id === "") id = arr[i].parentNode.id;
if (id !== arr[i].parentNode.id) continue;
if ($(arr[i]).position().left<iLeft||iLeft===0) {
iLeft = $(arr[i]).position().left;
}
} for (var j = 0; j < arr.length; j++) {
if (id !== arr[j].parentNode.id) continue;
$(arr[j]).css("left", iLeft + "px");
} jsPlumb.repaintEverything();
} //居中对齐
function SelectAlignCenter() {
var arr = getSelectedRegions();
var iLeft = 0;
var id = ""; for (var i = 0; i < arr.length; i++) {
if (id === "") id = arr[i].parentNode.id;
if (id !== arr[i].parentNode.id) continue;
if ($(arr[i]).position().left < iLeft || iLeft === 0) {
iLeft = $(arr[i]).position().left + parseInt(GetStyle(arr[i],"width")) / 2;
}
} for (var j = 0; j < arr.length; j++) {
if (id !== arr[j].parentNode.id) continue;
$(arr[j]).css("left", (iLeft - parseInt(GetStyle(arr[j],"width")) / 2) + "px");
} jsPlumb.repaintEverything();
}
//右对齐
function SelectAlignRight() {
var arr = getSelectedRegions();
var iLeft = 0;
var id = ""; for (var i = 0; i < arr.length; i++) {
if (id === "") id = arr[i].parentNode.id;
if (id !== arr[i].parentNode.id) continue;
if ($(arr[i]).position().left + parseInt(GetStyle(arr[i], "width")) > iLeft || iLeft === 0) {
iLeft = $(arr[i]).position().left + parseInt(GetStyle(arr[i], "width"));
}
} for (var j = 0; j < arr.length; j++) {
if (id !== arr[j].parentNode.id) continue;
$(arr[j]).css("left", (iLeft - parseInt(GetStyle(arr[j], "width"))) + "px");
} jsPlumb.repaintEverything();
} //上对齐
function SelectAlignTop() {
var arr = getSelectedRegions();
var iTop = 0;
var id = ""; for (var i = 0; i < arr.length; i++) {
if (id === "") id = arr[i].parentNode.id;
if (id !== arr[i].parentNode.id) continue;
if ($(arr[i]).position().top < iTop || iTop === 0) {
iTop = $(arr[i]).position().top;
}
} for (var j = 0; j < arr.length; j++) {
if (id !== arr[j].parentNode.id) continue;
$(arr[j]).css("top", iTop + "px");
} jsPlumb.repaintEverything();
} //垂直居中
function SelectAlignMiddle() {
var arr = getSelectedRegions();
var iTop = 0;
var id = ""; for (var i = 0; i < arr.length; i++) {
if (id === "") id = arr[i].parentNode.id;
if (id !== arr[i].parentNode.id) continue;
if ($(arr[i]).position().top + parseInt(GetStyle(arr[i], "height")) / 2 < iTop || iTop === 0) {
iTop = $(arr[i]).position().top + parseInt(GetStyle(arr[i], "height")) / 2;
}
} for (var j = 0; j < arr.length; j++) {
if (id !== arr[j].parentNode.id) continue;
$(arr[j]).css("top", (iTop - parseInt(GetStyle(arr[j], "height")) / 2) + "px");
} jsPlumb.repaintEverything();
} //下对齐
function SelectAlignBottom() {
var arr = getSelectedRegions();
var iTop = 0;
var id = ""; for (var i = 0; i < arr.length; i++) {
if (id === "") id = arr[i].parentNode.id;
if (id !== arr[i].parentNode.id) continue;
if ($(arr[i]).position().top + parseInt(GetStyle(arr[i], "height")) > iTop || iTop === 0) {
iTop = $(arr[i]).position().top + parseInt(GetStyle(arr[i], "height"));
}
} for (var j = 0; j < arr.length; j++) {
if (id !== arr[j].parentNode.id) continue;
$(arr[j]).css("top", (iTop - parseInt(GetStyle(arr[j], "height"))) + "px");
} jsPlumb.repaintEverything();
} //上下靠拢
function SelectUpColse() {
var arr = getSelectedRegions();
var iTop = 0;
var id = "";
for (var i = 0; i < arr.length; i++) {
if (id === "") id = arr[i].parentNode.id;
if (id !== arr[i].parentNode.id) continue;
if (iTop === 0) iTop = $(arr[i]).position().top;
$(arr[i]).css("top", iTop + "px");
iTop += parseInt(GetStyle(arr[i], "height"));
} jsPlumb.repaintEverything();
} //左右靠拢
function SelectLeftColse() {
var arr = getSelectedRegions();
var iLeft = 0;
var id = "";
for (var i = 0; i < arr.length; i++) {
if (id === "") id = arr[i].parentNode.id;
if (id !== arr[i].parentNode.id) continue;
if (iLeft === 0) iLeft = $(arr[i]).position().left;
$(arr[i]).css("left", iLeft + "px");
iLeft += parseInt(GetStyle(arr[i], "width"));
} jsPlumb.repaintEverything(); } //同高
function SelectSameHeight() {
var arr = getSelectedRegions();
var iHeigth = 0;
var id = "";
for (var i = 0; i < arr.length; i++) {
if (id === "") id = arr[i].parentNode.id;
if (id !== arr[i].parentNode.id) continue;
if (iHeigth === 0) iHeigth = parseInt(GetStyle(arr[i], "height"));
$(arr[i]).css("height", iHeigth+"px");
} jsPlumb.repaintEverything();
} //同宽
function SelectSameWidth() {
var arr = getSelectedRegions();
var iWidth = 0;
var id = "";
for (var i = 0; i < arr.length; i++) {
if (id === "") id = arr[i].parentNode.id;
if (id !== arr[i].parentNode.id) continue;
if (iWidth === 0) iWidth = parseInt(GetStyle(arr[i], "width"));
$(arr[i]).css("width", iWidth + "px");
} jsPlumb.repaintEverything(); } //旋转
function SelectClockwise(index) {
var arr = getSelectedRegions();
//var iWidth = 0;
//var id = "";
for (var i = 0; i < arr.length; i++) {
//if (id === "") id = arr[i].parentNode.id;
//if (id !== arr[i].parentNode.id) continue;
var sIndex= arr[i].style.transform.replace("rotate(", "").replace("deg)", "");
var iNum = 0;
if (sIndex) iNum = parseInt(sIndex);
$(arr[i]).css("transform", "rotate(" + (iNum + index)%360 + "deg)"); var points = jsPlumb.getEndpoints(arr[i]);
} jsPlumb.repaintEverything(); }
//删除选中
function DeleteSelect() {
var arr = getSelectedRegions();
for (var i = 0; i < arr.length; i++) {
jsPlumb.remove(arr[i],true);
//var points = jsPlumb.getEndpoints(arr[i]);
//for (var j = 0; j < points.length; j++) {
// jsPlumb.deleteEndpoint(points[j]);
//}
//arr[i].parentNode.removeChild(arr[i]);
} jsPlumb.repaintEverything(); } function GetStyle(obj, attr) {
if (obj.currentStyle) {
return obj.currentStyle[attr]; //只适用于IE
}
else {
return getComputedStyle(obj, false)[attr]; //只适用于FF,Chrome,Safa
}
return obj.style[attr]; //本人测试在IE和FF下没有用,chrome有用
}

对齐、旋转代码示例

代码可能有点乱,待整理。上章有博友就找我要过源码,这次出来一个初级的版本,还是把源码贴出来。有兴趣可以看看。源码下载

出处:

http://www.cnblogs.com/landeanfen/p/4959584.html

http://www.cnblogs.com/landeanfen/p/4971211.html

JS组件系列——JsPlumb制作流程图及相关效果详解的更多相关文章

  1. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(一)

    前言:出于某种原因,需要学习下Knockout.js,这个组件很早前听说过,但一直没尝试使用,这两天学习了下,觉得它真心不错,双向绑定的机制简直太爽了.今天打算结合bootstrapTable和Kno ...

  2. JS组件系列——BootstrapTable 行内编辑解决方案:x-editable

    前言:之前介绍bootstrapTable组件的时候有提到它的行内编辑功能,只不过为了展示功能,将此一笔带过了,罪过罪过!最近项目里面还是打算将行内编辑用起来,于是再次研究了下x-editable组件 ...

  3. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(四):自定义T4模板快速生成页面

    前言:上篇介绍了下ko增删改查的封装,确实节省了大量的js代码.博主是一个喜欢偷懒的人,总觉得这些基础的增删改查效果能不能通过一个什么工具直接生成页面效果,啥代码都不用写了,那该多爽.于是研究了下T4 ...

  4. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查

    前言:之前博主分享过knockoutJS和BootstrapTable的一些基础用法,都是写基础应用,根本谈不上封装,仅仅是避免了html控件的取值和赋值,远远没有将MVVM的精妙展现出来.最近项目打 ...

  5. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(二)

    前言:上篇 JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(一) 介绍了下knockout.js的一些基础用法,由于篇幅的关系,所以只能分成两篇,望见谅!昨天就 ...

  6. JS组件系列——表格组件神器:bootstrap table

    前言:之前一直在忙着各种什么效果,殊不知最基础的Bootstrap Table用法都没有涉及,罪过,罪过.今天补起来吧.上午博主由零开始自己从头到尾使用了一遍Bootstrap Table ,遇到不少 ...

  7. JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)

    前言:前天刚写了篇JS组件系列——Bootstrap Table 表格行拖拽,今天接到新的需要,需要在之前表格行拖拽的基础上能够同时拖拽选中的多行.博主用了半天时间研究了下,效果是出来了,但是感觉不尽 ...

  8. JS组件系列——KnockoutJS用法

    前言:出于某种原因,需要学习下Knockout.js,这个组件很早前听说过,但一直没尝试使用,这两天学习了下,觉得它真心不错,双向绑定的机制简直太爽了.今天打算结合bootstrapTable和Kno ...

  9. JS组件系列——又一款MVVM组件:Vue(二:构建自己的Vue组件)

    前言:转眼距离上篇 JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查) 已有好几个月了,今天打算将它捡起来,发现好久不用,Vue相关技术点都生疏不少.经过这几个月的时间,Vue ...

随机推荐

  1. QuickBI助你成为分析师-数据建模(二)

    摘要: 数据集编辑功能界面介绍以及常见问题总结. 在数据集编辑界面可以进行数据建模来更好的展示数据,创建数据集默认将数值类型字段作为度量,日期.字符串等类型作为维度,度量可以根据维度分组展示.下面来介 ...

  2. 多数据源报错 expected single matching bean but found 2: xxx,xxx

    问题: expected single matching bean but found 2: xxx,xxx 原因:在 Spring 容器中配置了两个类型Bean,Spring 容器将无法确定到底要用 ...

  3. 第二次项目冲刺(Beta版本) 合集

    小组成员 [组长]金盛昌(201421122043).刘文钊(20142112255).陈笑林(201421122042) 张俊逸(201421122044).陈志建(201421122040).陈金 ...

  4. 汉诺塔问题php解决

    面向过程解决 <?php function hanio($n,$x,$y,$z){//把n个盘子,按照要求从x移到z,y是中介 //递归跳出条件 if($n==1){ move($n, $x, ...

  5. 查询包含指定字段的所有表名的SQL脚本

    select [name] from sysobjects where [id] in (select [id] from syscolumns where [name]='ReceiptNbr') ...

  6. 第一章 进入java的世界

    一.你要做的事情: 1.  编写源代码:xxx.java 2. 编译器编译:检测代码错误 3. 输出:编译器输出xxx.class 4. 运行:java虚拟机运行xxx.class

  7. c++ 浅拷贝和深拷贝 指针和引用的区别 malloc(free)和new(delete)的区别 重载重写重定义

    4.malloc(free)和new(delete)的区别 malloc()函数: 1.1 malloc的全称是memory allocation,中文叫动态内存分配. 原型:extern void ...

  8. Python之Pulsar框架使用

    本文内容主要包含Pulsar的介绍和安装.初步使用.应用.常见示例等. 一. 介绍和安装 Pulsar是Python事件驱动并发框架:Pulsar具有高扩展性.高可用性的框架,它能够基于事件驱动的开源 ...

  9. 记一次js之button问题

    问题描述:记得某天,发现一件让我非常气愤的事情,居然因为一个按钮导致页面跳转失败或者是根本跳转不了界面,哪怕404也不给我报. 问题回现步骤: (1)正常输入url localhost:8080/te ...

  10. Spring4自动装配(default-autowire)

    §1 什么是自动装配? Spring IoC容器可以自动装配(autowire)相互协作bean之间的关联关系.因此,如果可能的话,可以自动让Spring通过检查BeanFactory中的内容,来替我 ...