如何使用Antv X6使用拖拽布局?

拖拽效果圖

拖拽后

布局預覽

官方: X6 圖編輯引擎 | AntV

安裝依賴

# npm
npm install @antv/x6 --save
npm install @antv/x6-plugin-dnd --save
npm install @antv/x6-plugin-export --save

需要引入的代碼

import { Graph, Shape } from '@antv/x6';
import { Dnd } from "@antv/x6-plugin-dnd";

頁面布局實現 我們設計左右布局

<div class="pannels-drag-view"><div class="left-tree"><div style="width: 100%;padding: 0px 10px 10px;"><div style="border-bottom: 1px solid #FAFAFA;"></div><div class="tree-one"><a-space><AppstoreFilled height="20px"/><span>00000001</span></a-space></div><div style="padding-left: 10px;" class="move-view"@mousedown="startDrag($event, {key: value.key, title: value.title})">
1111111111111111</div><div></div></div><div class="right-view" :style="{height: graphHeight+'px', 'min-height': 500, maxHeight: 900}"><div ref="container" :style="{height: graphHeight+'px'}"></div></div>
</div>
<style lang="less">
.pannels-layout-view {background-color: #FFFFFF;width: 100%;height: 100%;display: flex;flex-direction: column;.left-tree {width: 200px;min-width: 200px;border: 1px solid #e5e5e5;margin-right: 20px;display: flex;display: -webkit-flex;flex-direction: column;.tree-one {padding: 10px 0px 0px;cursor: pointer;display: flex;flex-direction: row;justify-items: start;}.move-view {padding: 10px;cursor: move;}.move-view:hover {background-color: #F7F7F7;}.ant-tree-switcher {width: 10px;}.ant-tree {.ant-tree-node-content-wrapper  {.cursor_move {cursor: move;}}}}.right-view {display: flex;flex-direction: column;flex: 1;height: 100%;overflow: auto;border: 1px solid #e5e5e5;overflow: hidden;position: relative;.layout-setting {position: absolute;top: 1px;right: 1px;z-index: 10;padding: 6px 12px;background: hsla(0, 0%, 100%, .7);.ant-btn {border-width: 0;}.ant-btn-icon-only {padding: 1px 0;border-width: 0;}}}
}</style>

創建節點

// 創建節點
Graph.registerNode('custom-node',{inherit: 'rect',width: 50,height: 70,},true,
);

在onMounted 中設置畫布,和初始化內容

// 初始化畫布
graph = new Graph({container: container.value,autoResize: true,background: {color: '#F2F7FA',},interacting: ({cell}) => {if (cell.getData() == undefined || cell.getData().disableMove) {return { nodeMovable: false }}return true;},panning: true,mousewheel: true,embedding: {enabled: true,findParent({node}) {const bbox = node.getBBox();return this.getNodes().filter((nodeTemp) => {const data = nodeTemp.getData();if (data && data.parent) {const targetBox = nodeTemp.getBBox();const targetBBox = bbox.intersectsWithRect(targetBox);return targetBBox;}return false;})}}});

處理布局 ,在畫布上繪制 19 * 20個虛線方框,作為父容器,如下圖

代碼示例:

onMounted(() => {let targetPointTemp = [];let nodesListTemp = []// 處理布局for (let i = 0; i < 20; i++) {for (let j = 0; j < 9; j++) {let x = i * 60;let y = j * 80;// 設置吸附點targetPointTemp.push({x: x,y: y})const rectNode = new Shape.Rect({id: `${i}-${j}`,shape: 'custom-node',x: x,y: y,width: 50,height: 70,zIndex: 9,// label: `${i}-${j}`,data: {parent: true,disableMove: true,cpx: i,cpy: j},draggable: false,attrs: {body: {fill: '#F1F4F6',stroke: "#333333",strokeWidth: 1,strokeDasharray: '4, 4'},title: {text: `${i}-${j}`,fill: '#333333',verticalAnchor: 'bottom',fontSize: 12,refX: 10,refY: 60,}},markup: [{tagName: 'rect',selector: 'body',},{tagName: 'text',selector: 'title',}],})nodesListTemp.push(rectNode);}}graph.zoom(-0.2);shapeNodesList.value = nodesListTemp;// 添加節點到畫布graph.addNodes(nodesListTemp);
})

使用DND畫布外向畫布內拖拽,并吸附,效果如下:

實現:外部向畫布拖拽

import { Dnd } from "@antv/x6-plugin-dnd";// 移動左側樹,配合DND 與Graph 拖拽與監聽
const startDrag = (e, data) => {const {key, title } = data;const keys = key.split('-');const invSn = keys[0];const id = keys[1];const newNode = graph.createNode({id: title?title:'',shape: 'rect',width: 150,height: 30,draggable: true,data: {parent: false,disableMove: false,id: id,partSn: title,invSn: invSn,partSn: title,},attrs: {label: {text: title,fill: '#FFFFFF',verticalAnchor: 'middle',fontSize: 12,ellipsis: true,breakWord: true,textWrap: {width: -10,height: -10,ellipsis: true}},body: {stroke: "#333333",strokeWidth: 1,fill: '#999999'}},zIndex: 11});const dnd = new Dnd({target: graph,getDragNode: (node) => node.clone({keepId: true}),getDropNode: (node) => node.clone({keepId: true}),validateNode: () => {console.log("drag successed")},})dnd.start(newNode, e);currentParent.value = null;
}

畫布中監聽并處理拖拽事件并吸附

onMounted(() => {
// 添加節點監聽graph.on('node:added', (env) => {const { cell, node } = env;console.log("node:added");const data = cell.data;// 獲取父節點const parent = node.getParent();let position = cell.position();if (parent) {position = parent.getPosition();if (position.x > maxX.value || position.y > maxY.value || position.x < 0 || position.y < 0) {graph.removeNode(cell);return false;}} else {if (position.x > maxX.value || position.y > maxY.value || position.x < 0 || position.y < 0) {graph.removeNode(cell);return false;}}//  刪除dom   removeDomResData(data, cell);node.setProp('size', { width: 50, height: 70 });if (parent) {// 判斷子節點數量const childCount = parent.getChildCount();if (childCount > 1) {startDragOut(data, cell);return false}position = parent.getPosition();cell.position(position.x, position.y, cell);cell.setAttrs({body: {stroke: "#222222",strokeWidth: 1,fill: '#3E82FF'}})cell.setParent(parent);cell.insertTo(parent);} else {const cellParent = cell.getParent();cell.setAttrs({body: {stroke: "#222222",strokeWidth: 1,fill: '#3E82FF'}})if (cellParent) {cell.setParent(cellParent);cell.insertTo(cellParent);}}saveLayoutData(data, cell);});})

嵌入父節點的監聽

// 嵌入父節點監聽graph.on('node:embedded', ({ cell, node }) => {const parent = cell.getParent();const position = parent.getPosition();const data = cell.getData(); // 獲取節點數據if (data.parent != undefined && data.parent) {parent.removeChild(node);return false;}if (position.x > maxX.value || position.y > maxY.value || position.x < 0 || position.y < 0) {parent.removeChild(node);cell.position(startX.value, startY.value, cell);cell.setParent(currentParent.value);cell.insertTo(currentParent.value);return false;}const childCount = parent.getChildCount();if (childCount > 1) {parent.removeChild(node);if (currentParent.value) {cell.position(startX.value, startY.value, cell);cell.setParent(currentParent.value);cell.insertTo(currentParent.value);return false} else {const cellParent = cell.parent;if (cellParent) {const cellCount = cell.parent.getChildCount();if (cellCount > 1) {return false}const px = cellParent.getPosition().x;const py = cellParent.getPosition().y;cell.position(px, py, cell);cell.setParent(cellParent);cell.insertTo(cellParent);return false}// cell.setParent(null);graph.removeCell(cell);startDragOut(data, cell);return false;}}cell.position(position.x, position.y, cell);cell.setAttrs({body: {stroke: "#222222",strokeWidth: 1,fill: '#3E82FF'}})cell.setParent(parent);cell.insertTo(parent);saveLayoutData(data, cell);});

畫布中 子節點的移動處理

onMounted(() => {// 鼠標按下事件graph.on('node:mousedown', (node)=>{console.log("node:mousedown")const { cell } = node;const parent = cell.getParent();if (parent) {currentParent.value = parent;} else {currentParent.value = null;}// 記錄初始位置if (!cell.data.parent) {const position = cell.position();startX.value = position.x;startY.value = position.y;} else {startX.value = null;startY.value = null;}});
/// 鼠標按下后的離開事件graph.on("node:mouseup", (env) => {console.log("node:mouseup")const { cell, node } = env;const position = cell.position();if (position.x < 0 && position.y > 0 && position.y < maxY.value) {const data = cell.getData();startDragOut(data, cell);return false;}if (position.x > maxX.value || position.y > maxY.value || position.x < 0 || position.y < 0) {cell.position(startX.value, startY.value, cell);if (currentParent.value) {cell.setParent(currentParent.value);cell.insertTo(currentParent.value);}return false;}});// 監聽節點事件函數graph.on('node:removed', (args) => {//   更新有效節點數據對象const { cell } = args;const data = cell.getData();removeLayoutData(data);});})

畫布中布局變化記錄事件

// 保存布局變化
const saveLayoutData = (data, cell) => {const { id, partSn, invSn} = data;let tempList = notSavedDataList.value;const position = cell.getPosition();const x = Math.ceil(position.x / 60);const y = Math.ceil(position.y / 80);const _index = _.findIndex(tempList, {id: id});if (_index >= 0) {const newData = {id: id,laidOutX: x,laidOutY: y,partSn: partSn,invSn: invSn}tempList[_index] = newData;} else {const newData = {id: id,laidOutX: x,laidOutY: y,partSn: partSn,invSn: invSn}tempList.push(newData);}notSavedDataList.value = tempList;
}

需要結合antv/X6的事件監聽事件靈活應用

 

graph.on('cell:click', ({ e, x, y, cell, view }) => {})

graph.on('node:click', ({ e, x, y, node, view }) => {})

graph.on('edge:click', ({ e, x, y, edge, view }) => {})

graph.on('blank:click', ({ e, x, y }) => {})

graph.on('cell:mouseenter', ({ e, cell, view }) => {})

graph.on('node:mouseenter', ({ e, node, view }) => {})

graph.on('edge:mouseenter', ({ e, edge, view }) => {})

graph.on('graph:mouseenter', ({ e }) => {})

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83988.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83988.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83988.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據庫健康監測器(BHM)實戰:如何通過 HTML 報告識別潛在問題

在數據庫運維中,健康監測是保障系統穩定性與性能的關鍵環節。通過 HTML 報告,開發者可以直觀查看數據庫的運行狀態、資源使用情況與潛在風險。 本文將圍繞 數據庫健康監測器(Database Health Monitor, BHM) 的核心功能展開分析,結合 Prometheus + Grafana + MySQL Export…

PCB設計實踐(二十四)PCB設計時如何避免EMI

PCB設計中避免電磁干擾&#xff08;EMI&#xff09;是一項涉及電路架構、布局布線、材料選擇及制造工藝的系統工程。本文從設計原理到工程實踐&#xff0c;系統闡述EMI產生機制及綜合抑制策略&#xff0c;覆蓋高頻信號控制、接地優化、屏蔽技術等核心維度&#xff0c;為高密度、…

嵌入式硬件篇---陀螺儀|PID

文章目錄 前言1. 硬件準備主控芯片陀螺儀模塊電機驅動電源其他2. 硬件連接3. 軟件實現步驟(1) MPU6050初始化與數據讀取(2) 姿態解算(互補濾波或DMP)(3) PID控制器設計(4) 麥克納姆輪協同控制4. 主程序邏輯5. 關鍵優化與調試技巧(1) 傳感器校準(2) PID參數整定先調P再調D最后…

【Linux基礎I/O】文件調用接口、文件描述符、重定向和緩沖區

【Linux基礎I/O一】文件描述符和重定向 1.C語言的文件調用接口2.操作系統的文件調用接口2.1open接口2.2close接口2.3write接口2.4read接口 3.文件描述符fd的本質4.標準輸入、輸出、錯誤5.重定向5.1什么是重定向5.2輸入重定向和輸出重定向5.3系統調用的重定向dup2 6.緩沖區 1.C語…

鴻蒙HarmonyOS 【ArkTS組件】通用屬性-背景設置

&#x1f4d1;往期推文全新看點&#xff08;附帶最新鴻蒙全棧學習筆記&#xff09; 嵌入式開發適不適合做鴻蒙南向開發&#xff1f;看完這篇你就了解了~ 鴻蒙崗位需求突增&#xff01;移動端、PC端、IoT到底該怎么選&#xff1f; 分享一場鴻蒙開發面試經驗記錄&#xff08;三面…

【76. 最小覆蓋子串】

Leetcode算法練習 筆記記錄 76. 最小覆蓋子串 76. 最小覆蓋子串 滑動窗口的hard題目&#xff0c;思路先找到第一個覆蓋的窗口&#xff0c;不斷縮小左邊界&#xff0c;找到更小的窗口并記錄。 思路很簡單&#xff0c;寫起來就不是一會事了&#xff0c;看題解看了幾個h&#xff0…

Spring事務簡單操作

什么是事務&#xff1f; 事務是一組操作的集合&#xff0c;是一個不可分割的操作 事務會把所有的操作作為?個整體, ?起向數據庫提交或者是撤銷操作請求. 所以這組操作要么同時 成功, 要么同時失敗. 事務的操作 分為三步&#xff1a; 1. 開啟事start transaction/ begin …

Rust 學習筆記:關于錯誤處理的練習題

Rust 學習筆記&#xff1a;關于錯誤處理的練習題 Rust 學習筆記&#xff1a;關于錯誤處理的練習題想看到回溯&#xff0c;需要把哪個環境變量設置為 1&#xff1f;以下哪一項不是使用 panic 的好理由&#xff1f;以下哪一項最能描述為什么 File::open 返回的是 Result 而不是 O…

MCP 協議傳輸機制大變身:拋棄 SSE,投入 Streamable HTTP 的懷抱

在技術的江湖里&#xff0c;變革的浪潮總是一波接著一波。最近&#xff0c;模型上下文協議&#xff08;MCP&#xff09;的傳輸機制就搞出了大動靜&#xff0c;決定和傳統的服務器發送事件&#xff08;SSE&#xff09;說拜拜&#xff0c;轉身擁抱 Streamable HTTP&#xff0c;這…

138. Copy List with Random Pointer

目錄 題目描述 方法一、使用哈希表 方法二、不使用哈希表 題目描述 問題的關鍵是&#xff0c;random指針指向的是原鏈表的結點&#xff0c;這個原鏈表的結點對應哪一個新鏈表的結點呢&#xff1f;有兩種辦法。一是用哈希表。另一種是復制原鏈表的每一個結點&#xff0c;并將…

如何評估開源商城小程序源碼的基礎防護能力?

在電商行業快速發展的背景下&#xff0c;開源商城已經為更多企業或者開發者的首選方案&#xff0c;不過并不是所有的開源商城源碼都能讓人放心使用&#xff0c;今天就帶大家一起了解下如何評估開源商城小程序源碼的基礎防護能力&#xff0c;幫助大家更好地篩選安全性高的商城源…

[Vue]跨組件傳值

父子組件傳值 詳情可以看文章 跨組件傳值 Vue 的核?是單向數據流。所以在父子組件間傳值的時候&#xff0c;數據通常是通過屬性從?組件向?組件&#xff0c;??組件通過事件將數據傳遞回?組件。多層嵌套場景?般使?鏈式傳遞的?式實現provideinject的?式適?于需要跨層級…

悠易科技智能體矩陣撬動AI全域營銷新時代

大數據產業創新服務媒體 ——聚焦數據 改變商業 在數字化浪潮與AI技術的雙重驅動下&#xff0c;數據營銷正經歷前所未有的變革&#xff0c;從傳統的全域智能營銷&#xff0c;邁向更具顛覆性的AI全域營銷時代。 麥肯錫的報告顯示&#xff0c;采用AI驅動營銷的企業&#xff0c;客…

Xilinx XCAU10P-2FFVB676I 賽靈思 Artix UltraScale+ FPGA

XCAU10P-2FFVB676I 是 AMD Xilinx 推出的 Artix UltraScale? FPGA 器件&#xff0c;內部集成了約 96,250 邏輯單元&#xff0c;滿足中等規模高性能應用的需求。該芯片采用 16 nm FinFET 制程工藝&#xff0c;核心電壓典型值約 0.85 V&#xff0c;能夠在較低功耗下提供高達 775…

Java SpringBoot 項目中 Redis 存儲 Session 具體實現步驟

目錄 一、添加依賴二、配置 Redis三、配置 RedisTemplate四、創建控制器演示 Session 使用五、啟動應用并測試六、總結 Java 在 Spring Boot 項目中使用 Redis 來存儲 Session&#xff0c;能夠實現 Session 的共享和高可用&#xff0c;特別適用于分布式系統環境。以下是具體的實…

分布式電源的配電網無功優化

分布式電源(Distributed Generation, DG)的大規模接入配電網,改變了傳統單向潮流模式,導致電壓波動、功率因數降低、網損增加等問題,無功優化成為保障配電網安全、經濟、高效運行的關鍵技術。 1. 核心目標 電壓穩定性:抑制DG并網點(PCC)及敏感節點的電壓越限(如超過5%…

JS手寫代碼篇---手寫Promise

4、手寫promise Promise 是一個內置對象&#xff0c;用于處理異步操作。Promise 對象表示一個尚未完成但預期將來會完成的操作。 Promise 的基本結構 一個 Promise 對象通常有以下狀態&#xff1a; pending&#xff08;進行中&#xff09;&#xff1a;初始狀態&#xff0c;…

我喜歡的vscode幾個插件和主題

主題 Monokaione Monokai Python 語義高光支持 自定義顏色為 self 將 class , def 顏色更改為紅色 為裝飾器修復奇怪的顏色 適用于魔法功能的椂光 Python One Dark 這個主題只在python中效果最好。 我為我個人使用做了這個主題,但任何人都可以使用它。 插件 1.Pylance Pylanc…

【深度學習新浪潮】大模型時代,我們還需要學習傳統機器學習么?

在大模型時代,AI 工程師仍需掌握傳統機器學習知識,這不僅是技術互補的需求,更是應對復雜場景和職業發展的關鍵。以下從必要性和學習路徑兩方面展開分析: 一、傳統機器學習在大模型時代的必要性 技術互補性 大模型(如GPT、BERT)擅長處理復雜語義和生成任務,但在數據量少…

年度工作計劃總結述職報告PPT模版一組分享

工作計劃總結述職報告PPT模版&#xff1a;工作計劃述職報告PPT模版https://pan.quark.cn/s/fba40a5e87da 第一套PPT模版是醫院年度工作計劃的封面頁&#xff0c;有藍橙配色、醫院標題、年度工作計劃的大字、英文副標題、匯報人信息和右上角的醫院logo區域&#xff0c;右側還有醫…