多年前接触过一个easyui写的一个流程设计器,功能蛮强大的,但时过境迁,以前的那种太过笨重,而且始终感觉运行起来太卡顿,最近公司不是特别忙,突然想采用d3来写个流程设计器。
流程设计器大概需求
第一步:自然就是搭建框架结构, 左边为操作区域,右边为绘图区域:
第二步:就是实现开始按钮,结束按钮的拖拽
通过mousedown事件来实现dom的拖拽
moseItemMove (event,nodeType) {
let odiv = $(event.target).closest(".flowNode").get(0); //获取当前元素
if(odiv==null){
return void(0);
}
let pos=GoingUtils.getElementPosition($("#flowLayout").get(0));
let {x,y}=GoingUtils.getElementPosition(odiv);
//需要实际
let newDiv=$(odiv).clone(true).appendTo("#flowLayout");
$(newDiv).css("position","absolute").css("opacity","0.8").css("left",x-pos.x).css("top",y-pos.y).css("border","1px solid #dfdfdf").css("width","150");
newDiv=newDiv.get(0);
//算出鼠标相对元素的位置
let disX = event.clientX - newDiv.offsetLeft;
var disy = event.clientY - newDiv.offsetTop;
let left = '',top='';
document.onmousemove = (e)=>{
//用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
left = e.clientX - disX;
top = e.clientY - disy;
if(this.moverTimer!=null){
clearTimeout(this.moverTimer);
}
this.moverTimer=setTimeout(()=>{
//这里是限制拖动范围
if(top<0||left<180){
$(newDiv).css("cursor","not-allowed");
newDiv.style.left = left + 'px';
newDiv.style.top = top + 'px';
this.appendNode=false;
}else{
this.appendNode=true;
$(newDiv).css("cursor","inherit");
//绑定元素位置到positionX和positionY上面
//移动当前元素
newDiv.style.left = left + 'px';
newDiv.style.top = top + 'px';
}
},5);
};
document.onmouseup = (e) => {
document.onmousemove = null;
document.onmouseup = null;
this.appendSvgNode(newDiv,nodeType);
};
}
这样就实现了dom元素的拖拽操作。如果拖拽到设计区域,则进行svg节点的添加
第三步:进行svg元素的添加
/**
* 绘制节点
* @param nodeColor
* @param nodeData
* @param circleWidth
* @param nodeText
* @private
*/
FlowDesCtl.prototype._addNode=function({nodeColor,nodeData,circleWidth,nodeText}){
this.svgObj.selectAll("rect")
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0);
function dragstarted(d) {
if (d != null) {
d.fx = d.x;
d.fy = d.y;
}
}
function dragged(d) {
console.log(d,"====");
if (d != null) {
d.fx = window.d3.event.x;
d.fy = window.d3.event.y;
window.d3.select(this).attr("transform",`translate(${d.fx},${d.fy })`);
}
}
function dragended(d) {
if(d!=null){
d.fx = null;
d.fy = null;
}
}
let {fx,fy}=nodeData;
const node = this.svgObj.append("g")
// .selectAll("g")
.data([nodeData])
.attr("fill", "currentColor")
.attr("stroke-linecap", "round")
.attr("stroke-linejoin", "round")
.attr("class", "moveNode")
.attr("transform",`translate(${fx},${fy})`)
.style("cursor",`move)`)
.call(window.d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("rect")
.attr("rx", 4)
.attr("ry", 4)
.attr("id", (d)=>{
return "rect_"+d.id+"";
})
.attr("fill", "#fff")
.attr("width", (d)=> {
return 10;
})
.attr("height", (d)=> {
console.log(d,"===height");
return 10;
})
.attr("stroke", "red")
.attr("stroke-width", 1)
.attr("transform",`translate(${15},${20})`)
.attr("fill-opacity", 1)
.attr("stroke-opacity", 1)
;
node.append("circle")
.attr("r", circleWidth)
.attr("fill", nodeColor)
.on("click", d => {
this.svgObj.selectAll("rect")
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0);
window.d3.select("#"+"rect_"+d.id)
.attr("fill-opacity", 1)
.attr("stroke-opacity", 1);
console.log(d,"====");
});
// .attr("cx", 20)
// .attr("cy", 20);
node.append("text")
.attr("x", -circleWidth/2)
.attr("y", "0.31em")
.text(nodeText)
.attr("fill", "#fff")
.attr("stroke", "white")
.attr("stroke-width", "0.5px")
.style('stroke',"#FFF")
.attr("stroke", "#fff");
};