- 你可以直接查看🌟🌟 官方文档 🌟🌟,文档写的很棒👍👍;
- 你也可以简单听我BBLL,顺便告诉你我的亲身经历,和常用参数;
遇到问题:
当你遇到下面这几种图形编辑时,你可能会想:
🤔这不是要专门搞一套js代码吗,canvas我也不熟练啊😱,这工程量也太大了,放弃吧!!跟老板说这个需求做不了
当然
x1
,起初我也是这么想的,而且我也尝试了自己用canvas写一个这个东西~当然
x2
,写起来又是一码事了,什么小问题各种层出不穷,然后果断弃坑~当然
x3
,我可以百度找现成的代码啊,网上那么多插件啥的,后来才发现鱼目混杂,根本无从下手,万一遇到问题找谁?🙆♂️🙆♂️所以,在最后朋友(
朋友多还是有那么点用的,致敬 🐯木兄
)的推荐下,还是遇到了它 ------🌟🌟 Antv/X6🌟🌟!!
步入正题:
上图是我结合自身项目需求+ Vue,制作的类似于一个工厂智能小车工作线路图,没听懂的,也可以忽略这句话😓~
🌟🌟 官方文档入口 🌟🌟
常用参数:
//创建地图
this.graph = new Graph({
snapline: true, //对齐线
history: {
enabled: true, //历史记录
ignoreChange: true //ignoreChange 是否忽略属性变化
},
panning: true, //支持平移拖拽
container: document.getElementById('container'),
width: window.innerWidth,
height: window.innerHeight,
background: {
color: '#fffbe6' // 设置画布背景颜色
},
grid: {
size: 10, // 网格大小 10px
visible: true // 渲染网格背景
},
connecting: {
allowPort: true, //是否允许边链接到链接桩
allowEdge: false, //是否允许边链接到另一个边
allowNode: false, //是否允许边链接到节点(非节点上的链接桩)
allowLoop: false, //是否允许创建循环连线,即边的起始节点和终止节点为同一节点
allowMulti: false, //是否允许在相同的起始节点和终止之间创建多条边
allowBlank: false, //是否允许连接到画布空白位置的点
// 自动吸附
snap: {
radius: 20
}
}
})
//加载地图数据
this.graph.fromJSON(this.data)
右上角工具栏:
自己写的,主要包含 撤销
,重做
,创建节点
,图形放大
,图形缩小
,重置视图
,数据导出
;
//撤销操作
this.graph.undo()
//重做
this.graph.redo()
//创建节点
const rect = this.graph.addNode({
shape: 'rect', // 指定使用何种图形,默认值为 'rect'
...
})
//地图放大缩小
this.graph.zoom(0.1)
this.graph.zoom(-0.1)
//重置视图
this.graph.centerContent() //画布居中
this.graph.zoom(0)
//序列化/反序列化 数据格式
// https://antv-x6.gitee.io/zh/docs/tutorial/intermediate/serialization
// graph.toJSON() 方法来导出图中的节点和边
this.graph.toJSON()
选中和工具Tools:
使用工具 Tools文档
节点和边选中代码示例
节点和边选中——事件属性参考文档
//在创建节点的时候,直接加入删除按钮tools
const rect = this.graph.addNode({
shape: 'rect', // 指定使用何种图形,默认值为 'rect'
...
attrs: {
...
},
//使用工具
tools: [
{
name: 'button-remove', // 工具名称
args: { x: 5, y: 5 } // 工具对应的参数
}
]})
案例代码:
<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="创建节点" @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: {
// 节点
// nodes: [
// {
// id: 'node1', // String,可选,节点的唯一标识
// x: 40, // Number,必选,节点位置的 x 值
// y: 40, // Number,必选,节点位置的 y 值
// width: 60, // Number,可选,节点大小的 width 值
// height: 60, // Number,可选,节点大小的 height 值
// label: 'node1', // String,节点标签
// tools: ['button-remove']
// },
// {
// id: 'node2', // String,节点的唯一标识
// x: 160, // Number,必选,节点位置的 x 值
// y: 180, // Number,必选,节点位置的 y 值
// width: 60, // Number,可选,节点大小的 width 值
// height: 60, // Number,可选,节点大小的 height 值
// label: 'node2' // String,节点标签
// },
// {
// id: 'node3', // String,节点的唯一标识
// x: 40, // Number,必选,节点位置的 x 值
// y: 180, // Number,必选,节点位置的 y 值
// width: 60, // Number,可选,节点大小的 width 值
// height: 60, // Number,可选,节点大小的 height 值
// label: 'node3' // String,节点标签
// }
// ],
// 边
// edges: [
// {
// source: 'node1', // String,必须,起始节点 id
// target: 'node2' // String,必须,目标节点 id
// }
// ],
// }
data: []
}
},
mounted() {
this.init()
},
methods: {
//初始化地图
init() {
//创建地图
this.graph = new Graph({
snapline: true, //对齐线
history: {
enabled: true, //历史记录
ignoreChange: true //ignoreChange 是否忽略属性变化
},
panning: true, //支持平移拖拽
container: document.getElementById('container'),
width: window.innerWidth,
height: window.innerHeight,
background: {
color: '#fffbe6' // 设置画布背景颜色
},
grid: {
size: 10, // 网格大小 10px
visible: true // 渲染网格背景
},
connecting: {
allowPort: true, //是否允许边链接到链接桩
allowEdge: false, //是否允许边链接到另一个边
allowNode: false, //是否允许边链接到节点(非节点上的链接桩)
allowLoop: false, //是否允许创建循环连线,即边的起始节点和终止节点为同一节点
allowMulti: false, //是否允许在相同的起始节点和终止之间创建多条边
allowBlank: false, //是否允许连接到画布空白位置的点
// 自动吸附
snap: {
radius: 20
},
// createEdge() {
// //创建动画虚线边
// return new Shape.Edge({
// attrs: {
// line: {
// stroke: '#1890ff',
// strokeDasharray: 5,
// targetMarker: 'classic',
// style: {
// animation: 'ant-line 30s infinite linear'
// }
// }
// }
// })
// }
}
})
//加载地图数据
this.graph.fromJSON(this.data)
this.graph.centerContent() //画布居中
//节点点击事件
this.graph.on('node:click', ({ e, x, y, node, view }) => {
// console.log(node)
this.selectReset()
node.attr('body/stroke', 'orange')
})
//边点击事件
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'
}
}
})
}),
//节点双击事件
this.graph.on('node:dblclick', ({ e, x, y, node, view }) => {
alert('节点ID:' + node.id)
console.log(node)
})
//边双击事件
this.graph.on('edge:dblclick', ({ e, x, y, edge, view }) => {
console.log(edge)
alert(
`边ID:${edge.id}, 起始节点: ${edge.source.cell},目标节点: ${edge.target.cell}`
)
})
},
//保存,获取节点等数据
save() {
//序列化/反序列化 数据格式
// https://antv-x6.gitee.io/zh/docs/tutorial/intermediate/serialization
// graph.toJSON() 方法来导出图中的节点和边
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
}
},
//创建节点
createNodes(type) {
if (type == 'rect') {
const rect = this.graph.addNode({
shape: 'rect', // 指定使用何种图形,默认值为 '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 } // 工具对应的参数
}
],
//连接桩
// ports: [
// {
// id: 'port1',
// attrs: {
// circle: {
// r: 6,
// // magnet: true 这个特殊属性,使链接桩在连线交互时可以被连接上
// 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)
},
//创建边
createEdges(type) {
const rect = this.graph.addEdge({
shape: 'edge', // 指定使用何种图形,默认值为 'edge'
source: 'node1',
target: 'node3'
})
},
//选择节点,边时重置颜色
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>