- 你可以直接查看???? 官方文檔 ????,文檔寫的很棒????;
- 你也可以簡(jiǎn)單聽我BBLL,順便告訴你我的親身經(jīng)歷姨拥,和常用參數(shù)帜乞;
遇到問(wèn)題:
當(dāng)你遇到下面這幾種圖形編輯時(shí)诊霹,你可能會(huì)想:
??這不是要專門搞一套js代碼嗎掌挚,canvas我也不熟練啊??玻淑,這工程量也太大了族壳,放棄吧?淄ァ梳侨!跟老板說(shuō)這個(gè)需求做不了
當(dāng)然
x1
蛉威,起初我也是這么想的,而且我也嘗試了自己用canvas寫一個(gè)這個(gè)東西~當(dāng)然
x2
走哺,寫起來(lái)又是一碼事了蚯嫌,什么小問(wèn)題各種層出不窮,然后果斷棄坑~當(dāng)然
x3
,我可以百度找現(xiàn)成的代碼啊丙躏,網(wǎng)上那么多插件啥的择示,后來(lái)才發(fā)現(xiàn)魚目混雜,根本無(wú)從下手晒旅,萬(wàn)一遇到問(wèn)題找誰(shuí)栅盲???♂???♂?所以,在最后朋友(
朋友多還是有那么點(diǎn)用的,致敬 ??木兄
)的推薦下废恋,還是遇到了它 ------???? Antv/X6????!!
步入正題:
上圖是我結(jié)合自身項(xiàng)目需求+ Vue谈秫,制作的類似于一個(gè)工廠智能小車工作線路圖扒寄,沒聽懂的,也可以忽略這句話??~
???? 官方文檔入口 ????
常用參數(shù):
//創(chuàng)建地圖
this.graph = new Graph({
snapline: true, //對(duì)齊線
history: {
enabled: true, //歷史記錄
ignoreChange: true //ignoreChange 是否忽略屬性變化
},
panning: true, //支持平移拖拽
container: document.getElementById('container'),
width: window.innerWidth,
height: window.innerHeight,
background: {
color: '#fffbe6' // 設(shè)置畫布背景顏色
},
grid: {
size: 10, // 網(wǎng)格大小 10px
visible: true // 渲染網(wǎng)格背景
},
connecting: {
allowPort: true, //是否允許邊鏈接到鏈接樁
allowEdge: false, //是否允許邊鏈接到另一個(gè)邊
allowNode: false, //是否允許邊鏈接到節(jié)點(diǎn)(非節(jié)點(diǎn)上的鏈接樁)
allowLoop: false, //是否允許創(chuàng)建循環(huán)連線拟烫,即邊的起始節(jié)點(diǎn)和終止節(jié)點(diǎn)為同一節(jié)點(diǎn)
allowMulti: false, //是否允許在相同的起始節(jié)點(diǎn)和終止之間創(chuàng)建多條邊
allowBlank: false, //是否允許連接到畫布空白位置的點(diǎn)
// 自動(dòng)吸附
snap: {
radius: 20
}
}
})
//加載地圖數(shù)據(jù)
this.graph.fromJSON(this.data)
右上角工具欄:
自己寫的该编,主要包含 撤銷
,重做
,創(chuàng)建節(jié)點(diǎn)
,圖形放大
,圖形縮小
,重置視圖
,數(shù)據(jù)導(dǎo)出
构灸;
//撤銷操作
this.graph.undo()
//重做
this.graph.redo()
//創(chuàng)建節(jié)點(diǎn)
const rect = this.graph.addNode({
shape: 'rect', // 指定使用何種圖形上渴,默認(rèn)值為 'rect'
...
})
//地圖放大縮小
this.graph.zoom(0.1)
this.graph.zoom(-0.1)
//重置視圖
this.graph.centerContent() //畫布居中
this.graph.zoom(0)
//序列化/反序列化 數(shù)據(jù)格式
// https://antv-x6.gitee.io/zh/docs/tutorial/intermediate/serialization
// graph.toJSON() 方法來(lái)導(dǎo)出圖中的節(jié)點(diǎn)和邊
this.graph.toJSON()
選中和工具Tools:
使用工具 Tools文檔
節(jié)點(diǎn)和邊選中代碼示例
節(jié)點(diǎn)和邊選中——事件屬性參考文檔
//在創(chuàng)建節(jié)點(diǎn)的時(shí)候,直接加入刪除按鈕tools
const rect = this.graph.addNode({
shape: 'rect', // 指定使用何種圖形喜颁,默認(rèn)值為 'rect'
...
attrs: {
...
},
//使用工具
tools: [
{
name: 'button-remove', // 工具名稱
args: { x: 5, y: 5 } // 工具對(duì)應(yīng)的參數(shù)
}
]})
案例代碼:
<template>
<div>
<span class="toolbar">
<div class="tool" title="撤銷" @click="toUndo()">
<i class="el-icon-refresh-left"></i>
</div>
<div class="tool" title="重做" @click="toRedo()">
<i class="el-icon-refresh-right"></i>
</div>
<div class="tool" title="創(chuàng)建節(jié)點(diǎn)" @click="createNodes('rect')">
<div class="rect"></div>
</div>
<div class="tool" @click="mapZoom('+')" title="放大視圖">
<i class="el-icon-zoom-in"></i>
</div>
<div class="tool" @click="mapZoom('-')" title="縮小視圖">
<i class="el-icon-zoom-out"></i>
</div>
<div class="tool" @click="mapZoom()" title="重置視圖">
<i class="el-icon-full-screen"></i>
</div>
<div class="tool" @click="save()" title="保存">
<i class="el-icon-cpu"></i>
</div>
</span>
<div id="container"></div>
</div>
</template>
<script>
import { Graph } from '@antv/x6'
export default {
data() {
return {
graph_zoom: 0, //地圖縮放比例
graph: null,
// data: {
// 節(jié)點(diǎn)
// nodes: [
// {
// id: 'node1', // String稠氮,可選,節(jié)點(diǎn)的唯一標(biāo)識(shí)
// x: 40, // Number半开,必選隔披,節(jié)點(diǎn)位置的 x 值
// y: 40, // Number,必選寂拆,節(jié)點(diǎn)位置的 y 值
// width: 60, // Number奢米,可選,節(jié)點(diǎn)大小的 width 值
// height: 60, // Number纠永,可選鬓长,節(jié)點(diǎn)大小的 height 值
// label: 'node1', // String,節(jié)點(diǎn)標(biāo)簽
// tools: ['button-remove']
// },
// {
// id: 'node2', // String尝江,節(jié)點(diǎn)的唯一標(biāo)識(shí)
// x: 160, // Number涉波,必選,節(jié)點(diǎn)位置的 x 值
// y: 180, // Number炭序,必選啤覆,節(jié)點(diǎn)位置的 y 值
// width: 60, // Number,可選惭聂,節(jié)點(diǎn)大小的 width 值
// height: 60, // Number窗声,可選,節(jié)點(diǎn)大小的 height 值
// label: 'node2' // String辜纲,節(jié)點(diǎn)標(biāo)簽
// },
// {
// id: 'node3', // String笨觅,節(jié)點(diǎn)的唯一標(biāo)識(shí)
// x: 40, // Number,必選侨歉,節(jié)點(diǎn)位置的 x 值
// y: 180, // Number屋摇,必選,節(jié)點(diǎn)位置的 y 值
// width: 60, // Number幽邓,可選炮温,節(jié)點(diǎn)大小的 width 值
// height: 60, // Number,可選牵舵,節(jié)點(diǎn)大小的 height 值
// label: 'node3' // String柒啤,節(jié)點(diǎn)標(biāo)簽
// }
// ],
// 邊
// edges: [
// {
// source: 'node1', // String倦挂,必須,起始節(jié)點(diǎn) id
// target: 'node2' // String担巩,必須方援,目標(biāo)節(jié)點(diǎn) id
// }
// ],
// }
data: []
}
},
mounted() {
this.init()
},
methods: {
//初始化地圖
init() {
//創(chuàng)建地圖
this.graph = new Graph({
snapline: true, //對(duì)齊線
history: {
enabled: true, //歷史記錄
ignoreChange: true //ignoreChange 是否忽略屬性變化
},
panning: true, //支持平移拖拽
container: document.getElementById('container'),
width: window.innerWidth,
height: window.innerHeight,
background: {
color: '#fffbe6' // 設(shè)置畫布背景顏色
},
grid: {
size: 10, // 網(wǎng)格大小 10px
visible: true // 渲染網(wǎng)格背景
},
connecting: {
allowPort: true, //是否允許邊鏈接到鏈接樁
allowEdge: false, //是否允許邊鏈接到另一個(gè)邊
allowNode: false, //是否允許邊鏈接到節(jié)點(diǎn)(非節(jié)點(diǎn)上的鏈接樁)
allowLoop: false, //是否允許創(chuàng)建循環(huán)連線,即邊的起始節(jié)點(diǎn)和終止節(jié)點(diǎn)為同一節(jié)點(diǎn)
allowMulti: false, //是否允許在相同的起始節(jié)點(diǎn)和終止之間創(chuàng)建多條邊
allowBlank: false, //是否允許連接到畫布空白位置的點(diǎn)
// 自動(dòng)吸附
snap: {
radius: 20
},
// createEdge() {
// //創(chuàng)建動(dòng)畫虛線邊
// return new Shape.Edge({
// attrs: {
// line: {
// stroke: '#1890ff',
// strokeDasharray: 5,
// targetMarker: 'classic',
// style: {
// animation: 'ant-line 30s infinite linear'
// }
// }
// }
// })
// }
}
})
//加載地圖數(shù)據(jù)
this.graph.fromJSON(this.data)
this.graph.centerContent() //畫布居中
//節(jié)點(diǎn)點(diǎn)擊事件
this.graph.on('node:click', ({ e, x, y, node, view }) => {
// console.log(node)
this.selectReset()
node.attr('body/stroke', 'orange')
})
//邊點(diǎn)擊事件
this.graph.on('edge:click', ({ e, x, y, edge, view }) => {
// console.log(edge)
this.selectReset()
edge.attr('line/stroke', 'orange')
edge.prop('labels/0', {
attrs: {
body: {
stroke: 'orange'
}
}
})
}),
//節(jié)點(diǎn)雙擊事件
this.graph.on('node:dblclick', ({ e, x, y, node, view }) => {
alert('節(jié)點(diǎn)ID:' + node.id)
console.log(node)
})
//邊雙擊事件
this.graph.on('edge:dblclick', ({ e, x, y, edge, view }) => {
console.log(edge)
alert(
`邊ID:${edge.id}, 起始節(jié)點(diǎn): ${edge.source.cell},目標(biāo)節(jié)點(diǎn): ${edge.target.cell}`
)
})
},
//保存涛癌,獲取節(jié)點(diǎn)等數(shù)據(jù)
save() {
//序列化/反序列化 數(shù)據(jù)格式
// https://antv-x6.gitee.io/zh/docs/tutorial/intermediate/serialization
// graph.toJSON() 方法來(lái)導(dǎo)出圖中的節(jié)點(diǎn)和邊
console.log(this.graph.toJSON())
},
//撤銷操作
toUndo() {
this.graph.undo()
},
//重做
toRedo() {
this.graph.redo()
// if (this.graph.isHistoryEnabled()) {
// this.graph.disableHistory()
// } else {
// this.graph.enableHistory()
// }
},
//地圖放大縮小
mapZoom(type) {
if (type == '+') {
this.graph.zoom(0.1)
this.graph_zoom += 0.1
} else if (type == '-') {
this.graph.zoom(-0.1)
this.graph_zoom -= 0.1
} else {
this.graph.zoom(
this.graph_zoom <= 0 ? Math.abs(this.graph_zoom) : -this.graph_zoom
)
this.graph.centerContent() //畫布居中
this.graph_zoom = 0
}
},
//創(chuàng)建節(jié)點(diǎn)
createNodes(type) {
if (type == 'rect') {
const rect = this.graph.addNode({
shape: 'rect', // 指定使用何種圖形犯戏,默認(rèn)值為 'rect'
x: 0,
y: 0,
width: 60,
height: 60,
angle: 0,
attrs: {
body: {
fill: '#fff', // 背景顏色
stroke: '#000' // 邊框顏色
},
label: {
text: 'Node', // 文本
fill: '#333', // 文字顏色
fontSize: 13 // 文字大小
}
},
tools: [
{
name: 'button-remove', // 工具名稱
args: { x: 5, y: 5 } // 工具對(duì)應(yīng)的參數(shù)
}
],
//連接樁
// ports: [
// {
// id: 'port1',
// attrs: {
// circle: {
// r: 6,
// // magnet: true 這個(gè)特殊屬性,使鏈接樁在連線交互時(shí)可以被連接上
// magnet: true,
// stroke: '#31d0c6',
// strokeWidth: 2,
// fill: '#fff'
// }
// }
// },
// {
// id: 'port2',
// attrs: {
// circle: {
// r: 6,
// magnet: true,
// stroke: '#31d0c6',
// strokeWidth: 2,
// fill: '#fff'
// }
// }
// }
// ]
ports: {
groups: {
// 輸入鏈接樁群組定義
left: {
position: 'left',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff'
}
}
},
right: {
position: 'right',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff'
}
}
},
top: {
position: 'top',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff'
}
}
},
bottom: {
position: 'bottom',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff'
}
}
}
},
items: [
{
id: 'port1-1',
group: 'left'
},
{
id: 'port1-2',
group: 'left',
attrs: {
circle: {
stroke: '#e9352f'
}
}
},
{
id: 'port2-1',
group: 'right',
attrs: {
circle: {
stroke: '#e9352f'
}
}
},
{
id: 'port2-2',
group: 'right'
},
{
id: 'port3-1',
group: 'top',
attrs: {
circle: {
stroke: '#e9352f'
}
}
},
{
id: 'port3-2',
group: 'top'
},
{
id: 'port4-1',
group: 'bottom'
},
{
id: 'port4-2',
group: 'bottom',
attrs: {
circle: {
stroke: '#e9352f'
}
}
}
]
}
})
}
// console.log(this.graph)
},
//創(chuàng)建邊
createEdges(type) {
const rect = this.graph.addEdge({
shape: 'edge', // 指定使用何種圖形拳话,默認(rèn)值為 'edge'
source: 'node1',
target: 'node3'
})
},
//選擇節(jié)點(diǎn)先匪,邊時(shí)重置顏色
selectReset() {
// this.graph.drawBackground({ color: '#fff' })
const nodes = this.graph.getNodes()
const edges = this.graph.getEdges()
nodes.forEach(node => {
node.attr('body/stroke', '#000')
})
edges.forEach(edge => {
edge.attr('line/stroke', 'black')
edge.prop('labels/0', {
attrs: {
body: {
stroke: 'black'
}
}
})
})
}
}
}
</script>
<style lang="scss" scoped>
.toolbar {
// padding: 0 20px;
box-sizing: border-box;
height: 40px;
background: white;
box-shadow: 0 2px 6px #e1e1e1;
position: fixed;
right: 0;
z-index: 999;
display: flex;
align-items: center;
.tool {
cursor: pointer;
width: 40px;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
&:hover {
background: #f1f1f1;
}
}
}
.rect {
width: 10px;
height: 10px;
border: 1px solid #5a5a5a;
// color: #e9352f;
}
</style>