長期更新版檔案請移步語雀(「AntV」X6開發實踐:踩過的坑與解決方案 (yuque.com))
??? | 如何自定義拖拽源?
相信你們在開發中更多的需求是需要自定義拖拽源,畢竟自定義的功能擴展性高一些,而且可以根據你的業務需求靈活設定,自定義拖拽的優點就是:萬物皆可成為拖拽源,不管你使用的是html標簽,還是第三方的ui框架,或者樹形串列,……這些都可以設定成拖拽源,只有你想不到的,沒有官方做不到的,來吧,開整,
官方提供的拖拽
自定義的拖拽
解決方案
這里使用的是Dnd插件,因為我看官方介紹說Stencil插件內部也是依靠Dnd實作的,索性就直接使用Dnd來搞吧
步驟1:初始化Dnd
// 先定義個全域的dnd變數
let dnd = null;
// 在mounted中對dnd進行初始化(在graph之后初始化)
dnd = new Dnd({
target: graph,
scaled: false,
dndContainer: proxy.$refs.dndContainer
});
步驟2:定義拖拽事件
/**
* 自定義拖拽源事件
* @param {*} e
* @param {*} treeNode 根據需要傳入要添加的引數
* @param {*} data 根據需要傳入要添加的引數
* 這里使用的是elementPlus的tree組件
*/
const startDrag = (e, treeNode, data) => {
console.log('eee', e);
console.log('treeNode', treeNode);
console.log('data', data);
const node = graph.createNode({
shape: 'cu-data-node',
width: 150,
height: 104,
label: data?.label,
// 傳遞給自定義節點的資料
data: {
label: data?.label,
img: data?.img,
desc: data?.desc
},
ports: {
...port,
items: [
{
group: 'top'
}
]
}
});
dnd.start(node, e);
};
步驟3:自定義html節點
// 注冊自定義節點 圖示+標題+描述
Shape.HTML.register({
shape: 'cu-data-node',
width: 'auto',
height: 104,
effect: ['data'],
html(cell) {
// 獲取節點傳遞過來的資料
const { label, img, desc } = cell.getData();
// 創建自定義的節點容器
const container = document.createElement('div');
container.setAttribute('class', 'cu-container');
// 圖片根據不同的型別進行切換,可以是后端回傳的圖示,也可以是自己本地的圖示,如果是后端回傳就通過節點的data傳進來
const container_img = document.createElement('img');
container_img.src = https://www.cnblogs.com/sxdpanda/archive/2023/06/07/currentTab.value === 0 ?'/src/assets/images/operator/datasouce.png' : img;
container_img.setAttribute('class', 'cu-container-img');
const container_title = document.createElement('div');
container_title.innerText = label;
container_title.setAttribute('class', 'cu-container-title');
const container_desc = document.createElement('div');
container_desc.setAttribute('class', 'cu-container-desc');
container_desc.innerText = desc || '描述資訊';
container.appendChild(container_img);
container.appendChild(container_title);
container.appendChild(container_desc);
return container;
}
});
步驟4:元素系結拖拽事件
<!-- $event必傳,后面的引數根據你的業務需求動態添加 -->
<div @mousedown="startDrag($event, node, data)">拖拽的節點</div>
??? | 本地圖片匯出后不顯示?
先看看官方的匯出檔案:
圖片匯出
由于業務需要,需要把畫布上的節點保存成圖片供其它模塊展示,如果你的后端回傳的資料格式是前端想要的,那么大不必搞圖片的形式,直接把官網的快速上手代碼拿過來回圈一下就好了……,這里就拿
toPng的方法來講解
問題梳理
- 呼叫
toPng拿到畫布的base64資料 - 把base64的資料傳給后端
- 后端把base64的資料轉存后生成可訪問的圖片地址再回傳給前端
- 前端開始展示
然而事情卻沒有這么簡單,第一步就遇到了一堆的坑,由于官網上匯出的都是它內置的節點,所以匯出都沒啥問題,但是我使用的是html節點,匯出的時候,我節點的圖片就死活導不出來,而且匯出的樣式也是亂的(樣式錯亂問題)
解決方案
一句話:圖片必須得是base64格式的匯出才會有圖片,不然無法匯出節點的圖片
- 自定義html節點中把圖片轉換成base格式的
需要呼叫 imageToDataUri把圖片地址轉成base64的資料,這個方法我也是摸索了好久才找到的,官方檔案完全沒有提及這個方法,如果需要查看其它方法,請列印
DataUri這個物件
DataUri.imageToDataUri('/images/operator/datasouce.png',
function (nu, url) {
// 第一個引數無效,用的只是第二個引數,但是第一個引數不寫不行
container_img.src = https://www.cnblogs.com/sxdpanda/archive/2023/06/07/url; // 給圖片標簽賦值
}
);
- 呼叫
toPng生成base64資料
這個步驟中要處理的問題:匯出后樣式不正確,匯出的時候頁面閃動
graph.toPNG(
dataUri => {
console.log('dataUri >>>>', dataUri); // 這個就是base的圖片地址
},
{
width: 526,
height: 268,
backgroundColor: 'rgba(25, 87, 121, 0.18)',
quality: 1, // 圖片質量 取值范圍:0-1,默認0.92
// copyStyles: false,
// 自定義樣式表,為了解決匯出后節點樣式丟失的問題,暫時官方還沒有修復這個bug
stylesheet: `
.cu-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.cu-container-title {
color: #d3e6f3;
}
.cu-container-img {
width: 53px;
height: 53px;
margin-bottom: 4px;
}
.cu-container-desc {
color: rgba(211, 230, 243, 0.7);
margin-top: 3px;
}
.cu-container-title,
.cu-container-desc {
font-size: 14px;
font-weight: 400;
line-height: 20px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}
`
}
);
:::warning
Tip:圖片匯出樣式和原節點的樣式不一致問題:如果你遇到了這個問題,目前最好的方法是不斷調整stylesheet中的樣式,直到匯出的樣式和原節點的樣式幾乎一致即可,當然,如果你的甲方不重視頁面互動,咱們完全可以使用copyStyles:true這個屬性就行了,這樣就不用設定 stylesheet了
:::
??? | 神奇的圖片邊框
:::tips
昨天才把html節點中的圖片轉成base格式的,今天就發現一個用戶體驗的問題;那么是啥呢?就是我從左側的樹形選單中拖拽節點的時候(滑鼠按下也是同樣問題),發現節點的圖片區域那里會出現一個邊框,持續時間不是很長,就幾毫秒的時間,但是當你連續拖拽幾個不同節點的時候就會發現這個邊框竟然又消失不見了,如果此時重新進入頁面,再開始拖動節點,圖片的邊框又出現了,
:::
問題梳理
- 是否是官方節點自帶的邊框?如果是,配置項是什么?
- 圖片的默認邊框是否被清除?還是外圍元素的邊框導致?
- 圖片的加載時機?
- 是否是轉base64的問題,畢竟在這之前沒有這個情況發生……
:::tips
帶著這些問題,第一時間去翻閱了官方檔案,發現沒有和這個問題相關的配置項,即使有設定了也不管用;然后就把問題拋到了交流群里面,發現壓根沒人回答……,那就只能開始第二個方案了:把圖片的默認邊框都去除掉,比如border,box-shadow,outline這些屬性都去除掉了,發現還是沒用;好吧,開始第三套方案:使用new Image處理圖片的加載時機問題,嗯!不出意外的話,這個方法還是不行
:::
定位問題
經過上面四個方案的嘗試后,我大概知道了問題的源頭在哪邊了,那就是我自定義html節點中圖片地址賦值的地方,由于
DataUri.imageToDataUri這個方法是個異步執行的,所以才會導致在渲染的時候會出現短暫的視覺差
解決方案
- 先給圖片賦值個普通的地址(非base64的地址)
- 在
DataUri.imageToDataUri('隨便寫個引數名',url)的回呼中再把圖片的src替換成base64的
const container_img = document.createElement('img');
container_img.setAttribute('class', 'cu-container-img');
container_img.setAttribute('alt', '節點ico');
container_img.style.cursor = 'pointer';
// 先用遠程圖片地址給圖片的src賦值,然后再重新賦值成base64的格式;這么做的目的就是解決節點拖拽到畫布上會出現短暫的邊框閃動問題,如果你要復現這個邊框,可以把下面這一行代碼注掉(不是必現)
container_img.src = https://www.cnblogs.com/sxdpanda/archive/2023/06/07/img;
// 把圖片轉成base64方便存盤到后端
DataUri.imageToDataUri(img, function (nu, url) {
// 第一個引數無效,用的只是第二個引數,但是第一個引數不寫由不行
container_img.src = url;
});
??? | 畫布內容從介面獲取資料后無法居中?
場景
前端需要把畫布上的節點保存到后端,然后前端在獲取詳情介面的時候要把節點進行居中處理
問題梳理
在不呼叫后端介面的情況下使用centerContent()是沒得問題的;但是在動態獲取節點資料后就會存在異步加載的問題,也就是先將內容居中了,之后再設定節點到畫布中去,此時centerContent的時機已經過去了,節點還是更具自身的位置進行排列
解決方案
由于作者用的vue技術堆疊,所以這里的解決方法主要以vue為主
方案1:使用nextTick等待dom全部渲染完成
nextTick(() => {
graph.centerContent();
});
方案2:直接在介面中使用
getDataView({ size: -1, name: item.tableMetaName }).then(res => {
if (res.code === 0) {
// 縮放
graph.zoom(-0.1);
// 畫布居中
graph.centerContent();
}
});
??? | 自定義右鍵選單坐標不準確?
問題梳理
原本的寫法是節點右鍵的時候通過node.position()的方法獲取節點的坐標,然后再把節點的坐標綁到右鍵選單的dom上,但是發現對畫布進行平移的時候,右鍵選單的位置還停留在第一次的位置,原因就是畫布平移和節點沒啥關系,節點的坐標并不會因為畫布平移了就自動更改自身的坐標
解決方案
對滑鼠的坐標進行轉換,這也是1.9版本中新增的一個方法,關鍵是在官方檔案中還找不到這個方法,只能死馬當活馬醫了 坐標轉換,果然問題解決了
graph.on('node:contextmenu', ({ e, node }) => {
const pos = graph.clientToGraph(e.clientX, e.clientY);// 核心代碼就是這一行
createMenuDom({ x: pos.x, y: pos.y, node, type: 0 });
});
附:自定義右鍵選單完整代碼
這里需要對javascript的dom有點基礎,不過這只是我創建dom的方法,如果你們想用其它的方法也是可以的哈
let divMenuContainer = null;
const createMenuDom = ({ x, y, node, edge, type }) => {
if (divMenuContainer) {
// 如果存在了選單,就先移除再創建,不然你的頁面上會多出來好多選單的
document.getElementById('container').removeChild(divMenuContainer);
}
divMenuContainer = document.createElement('div');
divMenuContainer.setAttribute('class', 'div-menu-container');
divMenuContainer.style.left = x + 30 + 'px';
divMenuContainer.style.top = y + 'px';
const divMenuItem = document.createElement('div');
divMenuItem.setAttribute('class', 'div-menu-item');
divMenuItem.innerText = type === 0 ? '洗掉節點' : '洗掉邊';
divMenuItem.addEventListener('click', () => {
type === 0 ? graph.removeNode(node) : graph.removeEdge(edge);
divMenuContainer.style.display = 'none';
});
divMenuContainer.appendChild(divMenuItem);
document.getElementById('container').appendChild(divMenuContainer);
document.body.addEventListener('click', () => {
if (divMenuContainer) {
divMenuContainer.style.display = 'none';
}
});
};
graph.on('node:contextmenu', ({ e, node }) => {
// 坐標轉換
const pos = graph.clientToGraph(e.clientX, e.clientY);
// 呼叫創建dom的方法,把坐標和節點資訊傳遞進去
createMenuDom({ x: pos.x, y: pos.y, node, type: 0 });
});
效果圖
??? | 在歷史記錄中忽略某個屬性的修改
常見問題
也是下面這個問題的解決方案
這是官方的demo
連線 undo - CodeSandbox
history插件配置
new Graph({
history: {
enabled: true,
beforeAddCommand(event, args: any) {
// 忽略歷史變更
if (args.options.ignoreHistory) {
return false
}
},
},
})
邊的寫法
graph.on('edge:connected', ({ edge }) => {
// 傳入自定義的 ignoreHistory 選項來忽略歷史變更
edge.attr('line/strokeDasharray', null, { ignoreHistory: true })
})
節點的寫法
new History({
enabled: true,
beforeAddCommand(event, args) {
if (args.options.ignoreHistory) {
return false;
}
}
})
node.setData({ tableMeta: res.data.records, desc: res?.data?.records?.length || 0 }, { ignoreHistory: true });
??? | x和y坐標為字串報錯問題
解決方案
直接把x,y坐標轉成純數字的即可,不然拖動節點的時候會報錯的
??? | 設定節點移動范圍在畫布內
Transform
new Graph({
translating: {
restrict: true
}
})
??? | 獲取當前節點的所有父級節點
此方法會回傳所有的輸入和輸出邊,如果只要輸入邊的節點或者輸出邊的節點資訊,請看這里鏈接
const getParentNodes = node => {
const nodeId = node.id;
const connectedNodes = [];
// 如果需要其他方法,請看下方的具體配置,根據自己的需要修改這里的代碼即可
const edges = graph.getConnectedEdges(node, { deep: true });
for (const edge of edges) {
const sourceNode = edge.getSourceNode();
const targetNode = edge.getTargetNode();
if (sourceNode.id !== nodeId) {
connectedNodes.push(sourceNode);
}
if (targetNode.id !== nodeId) {
connectedNodes.push(targetNode);
}
}
return connectedNodes;
};
// 具體的配置
const edges = graph.getConnectedEdges(node) // 回傳輸入和輸出邊
const edges = graph.getConnectedEdges(node, { incoming: true, outgoing: true }) // 回傳輸入和輸出邊
const edges = graph.getConnectedEdges(node, { incoming: true }) // 回傳輸入邊
const edges = graph.getConnectedEdges(node, { incoming: true, outgoing: false }) // 回傳輸入邊
const edges = graph.getConnectedEdges(node, { outgoing: true }) // 回傳輸出邊
const edges = graph.getConnectedEdges(node, { incoming:false, outgoing: true }) // 回傳輸出邊
const edges = graph.getConnectedEdges(node, { deep: true }) // 回傳輸入和輸出邊,包含鏈接到所有子孫節點/邊的輸入和輸出邊
const edges = graph.getConnectedEdges(node, { deep: true, incoming: true }) // 回傳輸入邊,包含鏈接到所有子孫節點/邊的輸入邊
const edges = graph.getConnectedEdges(node, { deep: true, enclosed: true }) // 回傳輸入和輸出邊,同時包含子孫節點/邊之間相連的邊
const edges = graph.getConnectedEdges(node, { indirect: true }) // 回傳輸入和輸出邊,包含間接連接的邊
??? | 注冊自定義節點報錯?
報錯資訊
(in promise) Error: Node with name 'cu-port' already registered.
下面這個報錯是群友發的,但是問題和我之前遇到的是一類問題,所以就直接告訴群友問題所在了??
錯誤代碼
Graph.registerNode(name,options)
正確代碼
Graph.registerNode(name,options,true)
??? | 常用方法集錦
- 清慷訓布:
graph.clearCells() - 畫布縮小:
graph.zoom(-0.5) - 獲取畫布縮放比例:
graph.zoom() - 獲取畫布上所有節點:
graph.getNodes() - 獲取畫布上所有邊:
graph.getEdges() - 設定節點移動范圍在畫布內鏈接
translating: {
restrict: true
},
- 動態設定節點位置鏈接:
node.position(x,y) - 節點只能被相同的源節點連接一次鏈接:
allowMulti: false - 兩個節點連接的時候獲取目標節點的資料鏈接
graph.on('edge:connected', ({ isNew, edge, currentCell }) => {
// 回呼的引數:https://antv-x6.gitee.io/zh/docs/tutorial/intermediate/events/#%E8%BE%B9%E8%BF%9E%E6%8E%A5%E5%8F%96%E6%B6%88%E8%BF%9E%E6%8E%A5
console.log('被連接的節點詳細引數', currentCell);
if (currentCell.data['type'] === 0) {
proxy.$modal.msgError('資料源無法作為輸出節點');
// 移除連接的邊
graph.removeEdge(edge?.id);
}
});
- 判斷當前節點是否被連接鏈接
const node = graph.getCellById('node1')
const connectedEdges = graph.getConnectedEdges(node)
附:參考檔案
Dnd插件
Stencil插件
圖片匯出
1.x常見問題
坐標轉換
Transform
Model
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/554609.html
標籤:其他
上一篇:JS基礎語法(一)
下一篇:返回列表
