推薦閱讀: 沒(méi)做過(guò)流程圖可視化,又突然接到神奇需求的選手~
最近公司需要做一個(gè)內(nèi)部使用的機(jī)器學(xué)習(xí)平臺(tái),
看了一下需求,有類似單向無(wú)環(huán)圖的內(nèi)容
參考了一下國(guó)內(nèi)外大廠的成品,
自己嘗試做了一下,
教程1講解節(jié)點(diǎn)實(shí)現(xiàn), 節(jié)點(diǎn)連線實(shí)現(xiàn), 節(jié)點(diǎn)拖動(dòng)模擬,連線拖動(dòng)模擬
教程2預(yù)計(jì)實(shí)現(xiàn)右鍵菜單,節(jié)點(diǎn)增刪,連線增刪.
教程3預(yù)計(jì)實(shí)現(xiàn)繪圖板放縮,重定位,橡皮筋選框,最后給出完成代碼.
教程一實(shí)現(xiàn)結(jié)果圖:
點(diǎn)擊藍(lán)字跳轉(zhuǎn)github地址''https://github.com/murongqimiao/DAGBoard
現(xiàn)在教程已更新到新版, 完整demo點(diǎn)我
一箩张、節(jié)點(diǎn)的實(shí)現(xiàn)
靈魂繪圖師上線 ~~~
因?yàn)閟vg的事件操作比canvas的事件實(shí)現(xiàn)起來(lái)更直觀福青、簡(jiǎn)潔,所以整體使用了svg,
{
name: "name1",
description: "description1",
id: 1,
parentNode: 0,
childNode: 2,
imgContent: "",
parentDetails: {
a: "",
b: ""
},
linkTo: [{ id: 2 }, { id: 3 }],
translate: {
left: 100,
top: 20
}
}
此為初版的節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu), 利用translate定位每個(gè)節(jié)點(diǎn)的位置.step5后替換為
{
name: "name2",
id: 2,
imgContent: "",
pos_x: 300,
pos_y: 400,
type: 'constant',
in_ports: [0, 1, 2, 3, 4],
out_ports: [0, 1, 2, 3, 4]
}
二酣衷、節(jié)點(diǎn)連線的實(shí)現(xiàn)
<path
class="connector"
v-for="(each, n) in item.linkTo" :key="n"
:d="computedLink(i, each, n)"></path>
基于vue實(shí)現(xiàn)所以直接用了:d 動(dòng)態(tài)計(jì)算貝塞爾曲線,
點(diǎn)擊->關(guān)于貝塞爾曲線可參考https://brucewar.gitbooks.io/svg-tutorial/15.SVG-path%E5%85%83%E7%B4%A0.html
三钠龙、節(jié)點(diǎn)拖拽的實(shí)現(xiàn)
具體建議在github clone一下代碼查看, 注釋非常詳細(xì).
dragPre(e, i) {
// 準(zhǔn)備拖動(dòng)節(jié)點(diǎn)
this.setInitRect(); // 初始化畫板坐標(biāo)
this.currentEvent = "dragPane"; // 修正行為
this.choice.index = i;
this.setDragFramePosition(e);
},
mousedown時(shí)獲取拖拽元素的下標(biāo),修正坐標(biāo)
<g
:transform="`translate(${dragFrame.posX}, ${dragFrame.posY})`"
class="dragFrame">
<foreignObject width="180" height="30" >
<body xmlns="http://www.w3.org/1999/xhtml">
<div
v-show="currentEvent === 'dragPane'"
class="dragFrameArea">
</div>
</body>
</foreignObject>
</g>
dragIng(e) {
if (this.currentEvent === "dragPane") {
this.setDragFramePosition(e);
// 模擬框隨動(dòng)
}
},
setDragFramePosition(e) {
const x = e.x - this.initPos.left; // 修正拖動(dòng)元素坐標(biāo)
const y = e.y - this.initPos.top;
this.dragFrame = { posX: x - 90, posY: y - 15 };
}
拖動(dòng)時(shí)給模擬拖動(dòng)的元素賦值位置
dragEnd(e) {
// 拖動(dòng)結(jié)束
if (this.currentEvent === "dragPane") {
this.dragFrame = { dragFrame: false, posX: 0, posY: 0 };
this.setPanePosition(e); // 設(shè)定拖動(dòng)后的位置
}
this.currentEvent = null; // 清空事件行為
},
setPanePosition(e) {
const x = e.x - this.initPos.left - 90;
const y = e.y - this.initPos.top - 15;
const i = this.choice.index;
this.DataAll[i].translate = { left: x, top: y };
},
拖動(dòng)結(jié)束把新的位置賦值給對(duì)應(yīng)元素
當(dāng)然在實(shí)際項(xiàng)目中, 每次變更需要跟后臺(tái)交互這些數(shù)據(jù), 不需要前端模擬數(shù)據(jù)變更的,直接請(qǐng)求整張圖的接口重新渲染就好了,更easy
四、節(jié)點(diǎn)連線拖拽的實(shí)現(xiàn)
和step3類似,在step4中我們也是通過(guò)監(jiān)聽(tīng)mousedown mousemove 與 mouseup這些事件.來(lái)實(shí)現(xiàn)節(jié)點(diǎn)間連線的拖拽效果.
<g>
<path
class="connector"
:d="dragLinkPath()"
></path>
</g>
首先來(lái)個(gè)path
setInitRect() {
let { left, top } = document
.getElementById("svgContent")
.getBoundingClientRect();
this.initPos = { left, top }; // 修正坐標(biāo)
},
linkPre(e, i) {
this.setInitRect();
this.currentEvent = "dragLink";
this.choice.index = i;
this.setDragLinkPostion(e, true);
e.preventDefault();
e.stopPropagation();
},
mousedown修正坐標(biāo)
dragIng(e) {
if (this.currentEvent === "dragLink") {
this.setDragLinkPostion(e);
}
},
mousemove的時(shí)候確定位置
linkEnd(e, i) {
if (this.currentEvent === "dragLink") {
this.DataAll[this.choice.index].linkTo.push({ id: i });
this.DataAll.find(item => item.id === i).parentNode = 1;
}
this.currentEvent = null;
},
setDragLinkPostion(e, init) {
// 定位連線
const x = e.x - this.initPos.left;
const y = e.y - this.initPos.top;
if (init) {
this.dragLink = Object.assign({}, this.dragLink, {
fromX: x,
fromY: y
});
}
this.dragLink = Object.assign({}, this.dragLink, { toX: x, toY: y });
},
mouseup的時(shí)候判斷連入了哪個(gè)元素
五逼侦、整合以上步驟, 組件抽離
隨著內(nèi)容的增多,我們需要把所有內(nèi)容整合, 基于耦合內(nèi)容對(duì)組件進(jìn)行分割,具體可看目錄結(jié)構(gòu)
所有的連線變成arrow組件,只繼承坐標(biāo)位置用以渲染
simulateFrame和simulateArrow只動(dòng)態(tài)繼承拖拽時(shí)的坐標(biāo),用以模擬拖拽效果