图形化引擎antv/G6--自定义节点创建紧凑树
1.参考: G6 TreeGraph树图默认展开几层节点
graph.data(data) graph.render() graph.fitCenter() // 移到图中心 setTimeout(() => { // 1 默认展开两层节点,之后,重新渲染 G6.Util.traverseTree(data, function(item) { console.log(item) if (item.depth > 0) { //collapsed为true时默认收起 item.collapsed = true } }) graph.render() graph.fitCenter() // 移到图中心 }, 10)
2.树形节点增删改,需要重新调用 graph.render()或者 graph.layout()
3.树形图,右键菜单插件
以此为例,右键菜单的触发事件回调函数: handleMenuClick
const menu = new G6.Menu({ offsetX: 10, offsetY: 10, itemTypes: ['node'], getContent(e) { const {item} = e console.log(item.getModel()) let {bqglParentUuid} = item.getModel() if (bqglParentUuid == 'root') { return `` } const outDiv = document.createElement('div') outDiv.innerHTML = `<ul class="ant-cascader-menu"> <li class="add">增加节点</li> <li class="addClassification">增加分类</li> <li class="del">删除节点</li> <li class="proview">查看节点</li> <li class="edit">编辑节点</li> </ul>` return outDiv }, handleMenuClick: (target, item) => { var nodeId = item.get('id') const newParentData = graph.findDataById(nodeId) let newchildren = newParentData.children switch (target.className) { case 'add': var childData = { id: 'child-data-' + new Date().getTime(), type: 'flow-rect', name: 'x-' } graph.addChild(childData, nodeId) graph.render() graph.fitCenter() // 移到图中心 graph.fitView() break case 'addClassification': console.log(target.innerText) break case 'del': graph.removeChild(nodeId) graph.render() graph.fitCenter() // 移到图中心 graph.fitView() // graph.fitView() break case 'proview': console.log(target.innerText) break case 'edit': console.log(target.innerText) break default: break } } })
4.树形图 矩形上线连接点的位置
//自定义节点 G6.registerNode( 'flow-rect', { getAnchorPoints() { //矩形上线连接点的位置 return [ [0.5, 0], [0.5, 1] ] } }, 'rect' ) //连接线 G6.registerEdge('line-arrow', { options: { style: { stroke: '#ccc' } }, draw: function draw(cfg, group) { const startPoint = cfg.startPoint const endPoint = cfg.endPoint const stroke = (cfg.style && cfg.style.stroke) || this.options.style.stroke const startArrow = (cfg.style && cfg.style.startArrow) || undefined const endArrow = (cfg.style && cfg.style.endArrow) || undefined //连接线路径 const keyShape = group.addShape('path', { attrs: { path: [ ['M', startPoint.x, startPoint.y], ['L', startPoint.x, (startPoint.y + endPoint.y) / 2], ['L', endPoint.x, (startPoint.y + endPoint.y) / 2], ['L', endPoint.x, endPoint.y] ], stroke, lineWidth: 1, startArrow, endArrow }, className: 'edge-shape', name: 'edge-shape' }) return keyShape } })
5. 点击切换选中
setState(name, value, item) {
var group = item.getContainer()
// 获取外边框
var nodeRect = group.find(function(e) {
return e.get('name') === 'node-rect'
})
// 获取任务名称
var collapseTitleText = group.find(function(e) {
return e.get('name') === 'node-name'
})
// 获取折叠点
var collapseText = group.find(function(e) {
return e.get('name') === 'collapse-text'
})
// 设置状态
if (name === 'selected') {
if (value) {
nodeRect.attr({
stroke: '#1258ED',
lineWidth: 2
})
collapseTitleText.attr({
fill: '#1258ED'
})
} else {
nodeRect.attr({
stroke: '#999999',
lineWidth: 1
})
collapseTitleText.attr({
fill: '#333'
})
}
}
}
//点击节点文字 graph.on('node-name:click', e => { const {item} = e graph.setItemState(item, 'selected', !item.hasState('selected')) // 切换选中 })
6.解决自定义节点动态更新调用graph.render(),坐标问题
根据G6坐标系的 pointX/pointY 缩放平移不影响 pointX/pointY 坐标计算 ,
通过记录最初创建时的 画布中心point坐标,计算拖拽后和最初创建的坐标差,在每次render后进行平移
记录缩放比例,render之后进行还原
注意:缩放比例和缩放中心的坐标,需要和拖拽后画布中心的坐标一致,否则会有位置差
//this.initGraphCenterPoint 为创建时保存的画布中心坐标
updateGraph() {
this.graphCenterPoint = graph.getGraphCenterPoint()
this.graphCenterZoom = graph.getZoom()
let x = this.graphCenterPoint.x - this.initGraphCenterPoint.x,
y = this.graphCenterPoint.y - this.initGraphCenterPoint.y
graph.render()
graph.fitCenter()
graph.translate(x, y)
graph.zoomTo(this.graphCenterZoom, this.graphCenterPoint)
},
7.双击节点编辑
//监听双击点击 graph.on('node:dblclick', e => { const nodeItem = e.item // 获取被点击的节点元素对象 that.editNode(e) console.log('双击', nodeItem._cfg) }) //双击编辑 editNode(evt) { let that = this const item = evt.item const model = item.get('model') const {x, y, width, height} = item.calculateBBox() const graph = evt.currentTarget const realPosition = evt.currentTarget.getClientByPoint(x, y) const el = document.createElement('div') el.style.fontSize = 16 + 'px' el.style.position = 'fixed' el.style.top = realPosition.y + 'px' el.style.left = realPosition.x + ((width - G6.Util.getTextSize(model.bqglLabel, 20)[0]) * graph.getZoom()) / 2 + 'px' el.style.transformOrigin = 'top left' el.style.transform = `scale(${evt.currentTarget.getZoom()})` const input = document.createElement('input') input.value = model.bqglLabel input.style.width = G6.Util.getTextSize(model.bqglLabel, 20)[0] + 'px' input.style.fontWeight = 'bold' input.style.height = height + 'px' input.className = 'dice-input' el.className = 'dice-input' el.appendChild(input) document.body.appendChild(el) const destroyEl = () => { document.body.removeChild(el) } const clickEvt = event => { if ( !( event.target && event.target.classList.length && event.target.className && event.target.className.includes('dice-input') ) ) { window.removeEventListener('mousedown', clickEvt) window.removeEventListener('scroll', clickEvt) graph.updateItem(item, { bqglLabel: input.value }) //更新视图 that.updateGraph() graph.layout() graph.off('wheelZoom', clickEvt) destroyEl() } } graph.on('wheelZoom', clickEvt) window.addEventListener('mousedown', clickEvt) window.addEventListener('scroll', clickEvt) input.addEventListener('keyup', event => { if (event.key === 'Enter') { clickEvt({ target: {} }) } }) },
8.dom节点插入
SVG 与 DOM 图形在 V3.3.x 中不支持。 仅在 Graph 的 renderer
为 'svg'
时可以使用 DOM 自定义节点。
new G6.TreeGraph({ renderer: 'svg', })
G6 的节点/边事件不支持 DOM 类型的图形。如果需要为 DOM 节点绑定事件,请使用原生 DOM 事件
参考文档:使用-dom-自定义节点
rectConfig 为基础公共变量配置对象
rectConfig: { //配置项 width: 200, height: 40, lineWidth: 1, fontSize: 16, fill: '#fff', // 填充背景 radius: 4, stroke: '#F7F7F7', opacity: 1 }
此处代码是循环插入
cfg.list.forEach((item, i) => { let agrments = `${i},"${cfg.id}","${item.bqglLabel}"`, classList = item.selected ? `childNode selected` : `childNode` group.addShape('dom', { itemData: { bqglUuid: cfg.bqglUuid, bqglType: cfg.bqglType, bqglLabel: cfg.bqglLabel }, attrs: { x: nodeOrigin.x + 10, y: i * (rectConfig.height + 5) + nodeOrigin.y + 30, width: rectConfig.width - 20, height: rectConfig.height, html: ` <div class='${classList}' oncontextmenu='nodeChildern(event , ${agrments})' onclick='clickNodeChildern(event , ${agrments})' onmousemove='nodeChildernMοusemove(event, ${agrments})' onmouseout='nodeChildernMοuseout(event, ${agrments})' style='height:${rectConfig.height}px' > <span ondblclick='dbclickNodeChildern(event , ${agrments})' >${that.fittingString(item.bqglLabel, rectBBox.width - 10 * 2 - rectConfig.height, 16)} </span> </div>` }, name: 'node-childern-rect' }) })
原生DOM事件函数,在 mounted 中声明 绑定在window上
//标签右键触发的事件 window.nodeChildern = (e, index, parentId) => { } //标签点击触发的事件 window.clickNodeChildern = (e, index, parentId) => { } //标签双击触发的事件 window.dbclickNodeChildern = (e, index, parentId) => { } //鼠标移入 window.nodeChildernMοusemove = (e, index, parentId, bqglLabel) => { } //标签鼠标移出 window.nodeChildernMοuseout = (e, index, parentId, bqglLabel) => { t.style.display = 'none' }
9.节点偏移量修正
宽度未自适应,造成重叠的bug
在 getHeight 中改变下高度间距,触发了引擎 layout 重新渲染最终位置被修正
new G6.TreeGraph({ layout: { type: 'compactBox', //紧凑型树形菜单 direction: 'TB', //垂直 dropCap: false, getHeight: nodeItem => { //添加判断后就可以自适应了 if (nodeItem.collapsed) { return rectConfig.height + 30 } return rectConfig.height + 20 }, getWidth: () => { return rectConfig.width + 40 } }, })
。。。
本文作者:混名汪小星
本文链接:https://www.cnblogs.com/wxyblog/p/16017585.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步