效果展示
在线预览
源码
SVG介绍
阮一峰:SVG图像入门
SVGtutorial
因为antv/x6是基于SVG的图编辑器,所以SVG的知识有必要了解下的
简介
- 可缩放矢量图形【基于图形】
全称:Scalable Vector Graphics
- 定义基于矢量的图形
- 基于XML语法
- 放大缩小不会失真
- 属于万维网标准
- 可以插入DOM,通过JavaScript和CSS来操作
语法
<svgwidth=“200”height=“200”viewBox=“-100-100200200”><polygonpoints="0,0 80,120 -80,120"fill="#234236"/><polygonpoints="0,-40 60,60 -60,60"fill="#0C5C4C"/><polygonpoints="0,-80 40,0 -40,0"fill="#38755B"/><rectx="-20"y="120"width="40"height="30"fill="brown"/></svg><!–viewBox:视口开始位置-->
<svg>
的
width
属性和
height
属性,指定了
SVG
图像在
HTML
元素中所占据的宽度和高度。
<viewBox>
属性的值有四个数字,分别是左上角的横坐标和纵坐标、视口的宽度和高度。
形状标签
常用的基本形状,也是我们常用的标签
- 矩形
- 圆形
- 椭圆
- 线
- 折线
- 多边形
- 路径
属性
通过属性可以去修改SVG的一些样式
.red{fill: red;}.fancy{fill: none;stroke: black;stroke-width: 3px;}
SVG
的
CSS
属性与网页元素有所不同,主要的属性如下:
fill:填充色
stroke:描边色
stroke-width:边框宽度
Antv/X6介绍
Antv:蚂蚁集团数据可视化团队
简介
X6:基于
HTML
和
SVG
的图编辑引擎,
X6
是
AntV
旗下的图编辑引擎,提供了一系列开箱即用的交互组件和简单易用的节点定制能力,方便我们快速搭建
DAG
图、
ER
图、流程图等应用。
图编辑核心能力:节点、连线与画布
使用
Step 1 创建容器
在页面中创建一个用于容纳
X6
绘图的容器,可以是一个
div
标签。
<divid="container"></div>
Step 2 准备数据
X6
支持
JSON
格式数据,该对象中需要有节点
nodes
和边
edges
字段,分别用数组表示:
const data ={// 节点nodes:[{id:'node1',// String,可选,节点的唯一标识x:40,// Number,必选,节点位置的 x 值y:40,// Number,必选,节点位置的 y 值width:80,// Number,可选,节点大小的 width 值height:40,// Number,可选,节点大小的 height 值label:'hello',// String,节点标签},{id:'node2',// String,节点的唯一标识x:160,// Number,必选,节点位置的 x 值y:180,// Number,必选,节点位置的 y 值width:80,// Number,可选,节点大小的 width 值height:40,// Number,可选,节点大小的 height 值label:'world',// String,节点标签},],// 边edges:[{source:'node1',// String,必须,起始节点 idtarget:'node2',// String,必须,目标节点 id},],};
Step 3 渲染画布
首先,我们需要创建一个
Graph
对象,并为其指定一个页面上的绘图容器,通常也会指定画布的大小。
import{ Graph }from'@antv/x6';const graph =newGraph({container: document.getElementById('container'),width:800,height:600,});// 读取数据
graph.fromJSON(data)
画布 Graph
https://x6.antv.vision/zh/docs/tutorial/basic/graph
图的载体,包含了所有元素、渲染及交互。
新建画布
let graph =newGraph(graphOptions())
基类 Cell
https://x6.antv.vision/zh/docs/tutorial/basic/cell
图形共同的基类 ,定义了节点和的边共同属性和方法
┌──────────────────┐
┌──▶│ Shape.Rect │
│ └──────────────────┘
│ ┌──────────────────┐
├──▶│ Shape.Circle │
┌────────┐ │ └──────────────────┘
┌─▶│ Node │──┤ ┌──────────────────┐
│ └────────┘ ├──▶│ Shape.Ellipse │
│ │ └──────────────────┘
│ │ ┌──────────────────┐
│ └──▶│ Shape.Xxx... │
┌────────┐ │ └──────────────────┘
│ Cell │──┤
└────────┘ │ ┌──────────────────┐
│ ┌──▶│ Shape.Edge │
│ │ └──────────────────┘
│ ┌────────┐ │ ┌──────────────────┐
└─▶│ Edge │──┼──▶│ Shape.DoubleEdge │
└────────┘ │ └──────────────────┘
│ ┌──────────────────┐
└──▶│ Shape.ShadowEdge │
└──────────────────┘
节点 Node
https://x6.antv.vision/zh/docs/tutorial/basic/node
根据不同的SVG元素来渲染节点和边,x6提供了内置节点和自定义节点
节点属性
节点都有共同的基类 Cell,除了从Cell继承的选项外,还支持以下选项。
属性名类型默认值描述xNumber0节点位置 x 坐标,单位为 ‘px’。yNumber0节点位置 y 坐标,单位为 ‘px’。widthNumber1节点宽度,单位为 ‘px’。heightNumber1节点高度,单位为 ‘px’。angleNumber0节点旋转角度。
添加节点
const rect = graph.addNode({shape:'rect',// 指定使用何种图形,默认值为 'rect'x:100,y:200,width:80,height:40,angle:30,attrs:{body:{fill:'blue',},label:{text:'Hello',fill:'white',},},})
内置节点
https://x6.antv.vision/zh/examples/gallery/#category-%E5%86%85%E7%BD%AE%E8%8A%82%E7%82%B9
内置节点与svg标签
构造函数shape 名称svg 标签描述Shape.Rectrectrect矩形Shape.Circlecirclecircle圆形Shape.Ellipseellipseellipse椭圆Shape.Polygonpolygonpolygon多边形Shape.Pathpathpath路径Shape.Imageimageimage图片Shape.HTMLhtml–HTML 节点,使用 foreignObject 渲染 HTML 片段Shape…………
自定义节点
https://x6.antv.vision/zh/docs/tutorial/intermediate/custom-node/#gatsby-focus-wrapper
我们可以通过
markup
和 attrs 来定制节点的形状和样式,
markup
可以类比
HTML
,
attrs
类比
CSS
。
1、注册
// 自定义节点的名称exportconstGAS_SHAPE_NAME='gas-shape'// 对象节点
Graph.registerNode(GAS_SHAPE_NAME,{...customNodeOptions,},true// 重名时是否覆盖)
配置解析
// 自定义对象节点的配置,需要展示更多的节点内容在这里去添加,并更新数据// https://x6.antv.vision/zh/docs/tutorial/intermediate/custom-nodeexportconst customNodeOptions ={// 来指定继承的基类inherit:'rect',width:64,height:105,// 标签及选择器markup:[{tagName:'rect',selector:'body',},{tagName:'image',selector:'image',},{tagName:'text',// 标签名称selector:'diagramName',// 选择器},],// 属性设置attrs:{body:{stroke:'transparent',fill:'transparent',},image:{width:64,height:64,refX:0,y:10,// 向下偏移 10px},diagramName:{width:64,refX:32,refY:'100%',// 右下角textAnchor:'middle',textVerticalAnchor:'bottom',fontSize:14,fill:'#009CFF',},},// 链接桩配置ports:{...ports },}
标签结构
2、使用
const newNode = graph.createNode({shape:GAS_SHAPE_NAME,attrs:{},data:{},})
修改节点
- node.attr(path, value),详细使用见 attr。
// 修改节点属性
node.attr('selector/attr', value)// 修改携带数据
node.setData({...data })// 获取携带数据
node.getData()
边Edge
https://x6.antv.antgroup.com/tutorial/basic/edge
内置节点节点和边都有共同的基类 Cell, 并继承
Cell
的属性
边的属性
属性名类型默认值描述sourceTerminalData-源节点或起始点。targetTerminalData-目标节点或目标点。verticesPoint.PointLike[]-路径点。routerRouterData-路由。connectorConnectorData-连接器。labelsLabel[]-标签。defaultLabelLabel默认标签默认标签。
箭头marker样式
节点间连线
链接桩
https://x6.antv.vision/zh/docs/tutorial/basic/port/
负责连线的输入与输出
添加
graph.addNode({x:60,y:60,width:160,height:80,label:'Rect With Ports',ports:[{id:'port1'},{id:'port2'},{id:'port3'},],})// 分组添加
graph.addNode({x:60,y:60,width:160,height:80,label:'Rect With Ports',groups:{top:{// 定义连接柱的位置,如果不配置,将显示为默认样式position:'top',// 定义连接柱的样式attrs:{circle:{...portStyle,},},},},// 链接桩组定义items:[{group:'top',},{group:'right',},{group:'bottom',},{group:'left',},],// 链接桩})
动态添加链接桩
通过鼠标位置,以及当前节点位置,计算链接桩位置
1、计算链接桩位置
// 双击添加链接桩
graph.on('node:dblclick',e=>{const{e: event, node }= e
// 当前选中元素const $select = document.querySelector('.x6-node-selected > rect')if(!$select){return}// 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置const position = $select.getBoundingClientRect && $select.getBoundingClientRect()if(!position){return}// 鼠标位置const pageX = event.pageX
const pageY = event.pageY
// graph.zoom() 缩放层级const zoom = graph.zoom()// 相对节点左上角的位置/鼠标的位置-元素的位置就是相对位置const x =(pageX - position.x)/ zoom
const y =(pageY - position.y)/ zoom
node.addPort({group:'absolute',args:{// 传递给自定义连接算法的参数// 通过鼠标位置,以及当前节点位置,计算链接桩位置x: Math.round(x),y: Math.round(y),},silent:false,// 为 true 时不触发 'change:ports' 事件和画布重绘。})})
2、添加链接桩
// 鼠标位置 - 元素位置 = 鼠标相对于节点的位置
node.addPort({group:'absolute',args:{// 传递给自定义连接算法的参数// 通过鼠标位置,以及当前节点位置,计算链接桩位置x: Math.round(x),y: Math.round(y),},silent:false,// 为 true 时不触发 'change:ports' 事件和画布重绘。})
连线规则
https://x6.antv.vision/zh/docs/tutorial/basic/interacting/#%E8%BF%9E%E7%BA%BF%E8%A7%84%E5%88%99
定制节点和边的交互行为
interacting
// 定制节点和边的交互行为 ==> boolean 节点或边是否可交互interacting: check
?{nodeMovable:false,edgeMovable:false,magnetConnectable:false,// 是否触发连线交互vertexDeletable:false,// 边的路径点是否可以被删除}:true
对连线过程进行控制
connecting
// 连线规则{connecting:{// 路由类型router:{// 连线类型在此修改// 曼哈顿路由 'manhattan' 路由是正交路由 'orth' 的智能版本,该路由由水平或垂直的正交线段组成,并自动避开路径上的其他节点(障碍)。name:'manhattan',args:{padding:1,},},// 圆角连接器,将起点、路由点、终点通过直线按顺序连接,并在线段连接处通过圆弧连接(倒圆角)。connector:{name:'rounded',args:{radius:8,},},anchor:'center',connectionPoint:'anchor',// 是否允许连接到画布空白位置的点,默认为 true。allowBlank:false,// 距离节点或者连接桩 50px 时会触发自动吸附snap:{radius:20,},// 拽出新的边createEdge(){returnnewShape.Edge({markup:[{tagName:'path',selector:'stroke',},{tagName:'path',selector:'fill',},],connector:{name:'rounded'},attrs:{fill:{class:'pipe-ant-line',fill:'none',connection:true,strokeDasharray:25,strokeWidth:3,strokeLinecap:'round',style:{animation:'ant-line 30s infinite linear',},// 渐变使用教程:https://x6.antv.vision/zh/docs/api/registry/attr#fill// ??无法垂直渐变/**
* 当y1和y2相等,而x1和x2不同时,可创建水平渐变
* 当x1和x2相等,而y1和y2不同时,可创建垂直渐变
* 当x1和x2不同,且y1和y2不同时,可创建角形渐变
*/// attrs: { x1: 0, y1: 0, x2: 0, y2: 1, },stroke:'#fff',},stroke:{fill:'none',connection:true,strokeWidth:6,strokeLinecap:'round',stroke:'#8CF7C3',},},zIndex:0,})},validateConnection({ targetMagnet }){return!!targetMagnet
},},}
指定触发某种交互时的高亮样式
highlighting
- ‘default’ 默认高亮选项,当以下几种高亮配置缺省时被使用。
- ‘embedding’ 拖动节点进行嵌入操作过程中,节点可以被嵌入时被使用。
- ‘nodeAvailable’ 连线过程中,节点可以被链接时被使用。
- ‘magnetAvailable’ 连线过程中,链接桩可以被链接时被使用。
- ‘magnetAdsorbed’ 连线过程中,自动吸附到链接桩时被使用。
{// 高亮样式highlighting:{// 连线过程中,自动吸附到链接桩时被使用。magnetAdsorbed:{name:'stroke',args:{attrs:{width:12,r:6,magnet:true,stroke:'#008CFF',strokeWidth:2,fill:'#0F67FF',},},},},}
关于2.0
https://x6.antv.antgroup.com/tutorial/about
2.0 重新设计和实现了渲染架构
图编辑器实现
画布初始化
新建画布
let graph =newGraph(graphOptions())
配置解析
/**
* @desc 初始化面板配置
* @param check 查看模式
*/constgraphOptions=(check =false)=>{return{container: document.getElementById('gasDiagramPanel'),// 定制节点和边的交互行为 ==> boolean 节点或边是否可交互interacting: check
?{nodeMovable:false,edgeMovable:false,magnetConnectable:false,vertexDeletable:false,}:true,// 对齐线snapline:true,// 撤销/重做history:!check,// 点选/框选selecting:{enabled:true,multiple:!check,// 多选【开启】rubberband:false,// 启用框选【关闭】},// 显示网格 // 'dot' | 'fixedDot' | 'mesh'grid:{visible:!check,size:20,// 网格大小type:'mesh',args:{color:'#e9e9e9',thickness:2,// 网格线宽度/网格点大小},},// 滚动scroller:{enabled:true,pageVisible:false,// 是否分页pageBreak:false,pannable:true,// 是否平移},// 滚轮缩放 MouseWheelmousewheel:{enabled:true,zoomAtMousePosition:true,modifiers:['ctrl','meta'],maxScale:3,minScale:0.3,},resizing:false,// 不能修改大小rotating:false,// 不能旋转keyboard:!check,// 按键操作clipboard:!check,// 剪切板autoResize:true,onToolItemCreated({ tool }){const options = tool.options
if(options && options.index %2===1){
tool.setAttrs({fill:'red'})}},// 连线规则connecting:{...},// 连线高亮highlighting:{...},}}
添加节点
通过拖拽交互往画布中添加节点
添加拖拽
https://x6.antv.vision/zh/docs/tutorial/basic/dnd
1、初始化
import{ Addon }from'@antv/x6'const dnd =newAddon.Dnd(options)
2、开始拖拽
选项类型说明nodeNode开始拖拽的节点【添加的节点】eMouseEvent / JQuery.MouseDownEvent鼠标事件
dnd.start(newNode, e)
<template>
<div
class="flow-library-item"
v-for="group in item.groups"
:key="group.id"
>
<div
class="flow-library-item__img"
:class="'flow-library-item__img--' + group.shape"
:data-name="group.name"
:data-id="group.id"
:data-image="group.image"
:data-shape="group.shape"
:style="{
backgroundImage: `url(${group.image})`,
}"
@mousedown.stop="handleonAddNode"
>
<div class="flow-library-item__name">{{ group.name }}</div>
</div>
</div>
</template>
<script>
export default {
methods: {
/**
* 拖拽并添加节点
* @param e
*/
addNode(e) {
const target = e && e.target.closest('.thumbnail-img') // 匹配特定选择器且离当前元素最近的祖先元素
if (target) {
const id = target.getAttribute('data-id')
const label = target.getAttribute('data-label')
const image = target.getAttribute('data-image')
const newNode = graph.createNode({
shape: 'custom-image',
label,
attrs: {
image: {
'xlink:href': `${image}`,
},
},
data: {
label,
id
}
})
dnd.start(newNode, e)
}
}
}
}
</script>
添加节点
先创建后添加
创建并添加到画布
const rect = graph.addNode({shape:'rect',// 指定使用何种图形,默认值为 'rect'x:100,y:200,width:80,height:40,angle:30,attrs:{body:{fill:'blue',},label:{text:'Hello',fill:'white',},},})
推荐第二种,可以通过
shape
来指定图形类型,包括自定义类型
定制样式
气路图切换
https://x6.antv.vision/zh/docs/tutorial/intermediate/serialization
完成数据的保存和读取
保存
graph.toJSON()
读取
graph.fromJSON()
数据与交互
事件系统
https://x6.antv.vision/zh/docs/tutorial/intermediate/events
回调参数包含鼠标位置x、y,事件对象e…
// cell:元素类型,// click:事件类型,
graph.on('cell:click',({ e, x, y, cell })=>{})
事件cell 节点/边node 节点edge 边blank 画布空白区域****单击cell:clicknode:clickedge:clickblank:click双击cell:dblclicknode:dblclickedge:dblclickblank:dblclick右键cell:contextmenunode:contextmenuedge:contextmenublank:contextmenu鼠标按下cell:mousedownnode:mousedownedge:mousedownblank:mousedown移动鼠标cell:mousemovenode:mousemoveedge:mousemoveblank:mousemove鼠标抬起cell:mouseupnode:mouseupedge:mouseupblank:mouseup鼠标滚轮cell:mousewheelnode:mousewheeledge:mousewheelblank:mousewheel鼠标进入cell:mouseenternode:mouseenteredge:mouseentergraph:mouseenter鼠标离开cell:mouseleavenode:mouseleaveedge:mouseleavegraph:mouseleave
获取数据
node.getData()
设置数据
node.setData({...data
})
修改节点属性
// selector:选择器// attr:属性// value:修改值
node.attr('selector/attr', value)
线的拖拽
// https://x6.antv.vision/zh/docs/tutorial/intermediate/tools// 1、vertices 路径点工具,在路径点位置渲染一个小圆点,// 拖动小圆点修改路径点位置,双击小圆点删除路径点,在边上单击添加路径点。// 2、segments 线段工具。在边的每条线段的中心渲染一个工具条,可以拖动工具条调整线段两端的路径点的位置。// 基类 Cell
graph.on('cell:mouseenter',({ cell, node })=>{if(!cell.isNode()){
cell.addTools(['vertices','segments',// {// name: 'button-remove',// args: {// x: '30%',// y: '50%',// },// },])}})
画布销毁
graph.dispose()
查看模式
禁用以下操作,保留点击查看的交互
newGraph({// 定制节点和边的交互行为 ==> boolean 节点或边是否可交互interacting:!check,// 撤销/重做history:!check,// 点选/框选selecting:{enabled:true,multiple:!check,// 多选【开启】rubberband:false,// 启用框选【关闭】},keyboard:!check,// 按键操作clipboard:!check,// 剪切板})
画布缩放及居中
监听页面resize,动态修改画布大小,并居中画布
记得移除监听
mounted(){
window.removeEventListener('resize', this.autoResize, false)
},
methods: {
// 容器大小适配浏览器缩放/防抖一下
autoResize: debounce(() => {
const gasContainer = document.querySelector('.gas-diagram-container')
if (gasContainer && graph) {
// 画布适配 https://x6.antv.vision/zh/docs/api/graph/transform/#resize
graph.resize(gasContainer.clientWidth, gasContainer.clientHeight)
}
}, 300),
}
画布居中
// 画布居中
graph.centerContent()
节点缩放
缩放设置
resizing:{enabled:true,minWidth:64,// 最小宽maxWidth:64*2,// 最大宽minHeight:105/2,// 最小高maxHeight:105*2,// 最大高orthogonal:true,// 是否显示中间调整点,默认为 truerestricted:false,// 调整大小边界是否可以超出画布边缘preserveAspectRatio:true,// 调整大小过程中是否保持节点的宽高比例}
节点内容改变
https://x6.antv.vision/zh/docs/tutorial/intermediate/attrs
通过相当大小和位置来替换原有单位,达到节点缩放,内容跟着改变
常用参数:
- refWidth 和 refHeight 元素大小。
- refX 和 refY 元素位置。
- refCx 和 refCy 椭圆 和圆 中心位置。
- refRx 和 refRy 椭圆 半径。
- refR 圆 半径。
diagramName: {
// width: 64,
// refX: 32,
refWidth: transformToPercent(64, 64),
refX: transformToPercent(32, 64),
},
…
右键菜单
扩展
撤销/重做
滚动/缩放
对齐线
快捷键
…
SVG动画
Thanks
版权归原作者 forguo 所有, 如有侵权,请联系我们删除。