import {Cell, Graph, Shape} from '@antv/x6'; import {CodeBlockContent, CodeBlockShape} from './CodeBlockShape'; import {ClassShape} from './ClassShape'; import {CLASS_SHAPE_ID_TAG, CODE_BLOCK_ID_TAG, CodeData, Utils} from './Utils'; import {Edge} from './Edge' const myUtils = new Utils() const codeBlockShape = new CodeBlockShape() const classShape = new ClassShape() const edge = new Edge() export let initGraph = function (codeBlocks: CodeBlockContent[]) { // 創建畫布 const graph = new Graph({ container: document.getElementById('container'), width: 3000, height: 5000, background: { color: '#ffffff', // 设置画布背景颜色 }, grid: { size: 10, // 網格大小 10px visible: true, // 渲染網格背景 }, }); // 將 代碼塊字串 集合 解析成 CodeData 對象集合,並將相同 className 的對象進行合併 let codeDatas: CodeData[] = [] codeBlocks.forEach((codeBlock: CodeBlockContent) => { let codeData: CodeData = myUtils.parseCodeData(codeBlock) codeDatas.push(codeData) }) codeDatas = myUtils.mergeCodeDatas(codeDatas) // 將 CodeData 對象列表 繪製成 類圖形、代碼塊圖形 for (const codeData of codeDatas) { // 將 類圖形、代碼塊圖形 加入到 畫布 中 let funcNames = codeData.funcs.map(item => item.name) graph.addNode(classShape.createClassShape(codeData.className, funcNames)) graph.addNodes(codeBlockShape.createCodeBlockShape(codeData)) // 創建 函數名稱 與 代碼塊圖形 的 連線 graph.addEdges(edge.createFuncToCodeEdges(codeData)) } // 繪製 CodeData 中的 函數調用 連線 for (const codeData of codeDatas) { graph.addEdges(edge.createFuncCallEdges(codeData)) } // 綁定 開啟/關閉 代碼塊圖形 事件 // @ts-ignore graph.on('toggle:codeBlock', ({e}) => { // 獲取 代碼塊圖形、開關文字 對象 let codeBlock = graph.getCellById(e.currentTarget.getAttribute('target-code-block')) let toggleText = e.currentTarget.parentNode.childNodes[5].children[0] // 如果當前 代碼塊圖形 正在顯示,則關閉。反之則開啟 if (codeBlock.getProp().visible) { codeBlock.hide() toggleText.textContent = '+' toggleText.setAttribute('dy', '0.3em') } else { // 計算 代碼塊 顯示位置 const intervalX = 100 const posX = e.offsetX + intervalX const posY = e.offsetY - (codeBlock.getProp().size.height / 2) codeBlock.prop("position", {x: posX, y: posY}) // 顯示 代碼塊,並更新按鈕內容 codeBlock.show() toggleText.textContent = '-' toggleText.setAttribute('dy', '0.3em') } }) // 最大的 類圖形 的寬度 let maxClassShapeWidth = 0; // 所有的 類圖形 集合 let classShapes: Cell[] = []; // 遍歷 所有的圖形 for (const node of graph.getCells()) { // 默認隱藏所有的 代碼塊 if (node.id.startsWith(CODE_BLOCK_ID_TAG)) { node.hide() } // 計算 類圖形 最大寬度、加入到集合中 if (node.id.startsWith(CLASS_SHAPE_ID_TAG)) { maxClassShapeWidth = Math.max(maxClassShapeWidth, node.getProp().size.width) classShapes.push(node) } } /** * 計算 類圖形 的 座標 */ // 左側保留寬度 const marginLeftWidth = 100 // 類圖形的 Y 座標 let curY = 200 // 多個 類圖形 的 上下間格 const intervalY = 150 // 所有 類圖形 的 中線 X 座標 const midX = marginLeftWidth + (maxClassShapeWidth / 2) for (const classShape of classShapes) { // 計算 類圖形 的 左上X 座標,並設置 const posX = midX - (classShape.getProp().size.width / 2) classShape.prop("position", {x: posX, y: curY}) // 下個 類圖形 的 Y 座標 = 當前 類圖形 高度 + 間隔 curY = curY + classShape.getProp().size.height + intervalY } }