From cb9f274a36ab220d1bec2990570d35ab6c6a7d04 Mon Sep 17 00:00:00 2001 From: Joel Alan <31396322+lxfu1@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:20:09 +0800 Subject: [PATCH] chore: opt the layout of force-directed graph (#5484) * chore: opt the layout of force-directed graph * fix: resolve conversation --- .../net/forceDirected/demo/basicFA2.js | 36 +--- .../net/forceDirected/demo/basicForce.js | 91 +++------- .../net/forceDirected/demo/forceBubbles.js | 155 ------------------ .../demo/forceConstrainedInRect.js | 80 --------- .../forceDirectedConfigurationTranslate.js | 66 ++------ .../demo/forceDirectedFunctionalParams.js | 46 +++--- 6 files changed, 67 insertions(+), 407 deletions(-) delete mode 100644 packages/site/examples/net/forceDirected/demo/forceBubbles.js delete mode 100644 packages/site/examples/net/forceDirected/demo/forceConstrainedInRect.js diff --git a/packages/site/examples/net/forceDirected/demo/basicFA2.js b/packages/site/examples/net/forceDirected/demo/basicFA2.js index 46a2e8e4fa1..2e8e60e007d 100644 --- a/packages/site/examples/net/forceDirected/demo/basicFA2.js +++ b/packages/site/examples/net/forceDirected/demo/basicFA2.js @@ -1,39 +1,21 @@ -import { Graph, Extensions, extend } from '@antv/g6'; - -const ExtGraph = extend(Graph, { - layouts: { - forceAtlas2: Extensions.ForceAtlas2Layout, - }, -}); - -const container = document.getElementById('container'); -const width = container.scrollWidth; -const height = container.scrollHeight || 500; +import { Graph } from '@antv/g6'; fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json') .then((res) => res.json()) .then((data) => { - const graph = new ExtGraph({ + const graph = new Graph({ container: 'container', - width, - height, - transforms: [ - { - type: 'transform-v4-data', - activeLifecycle: ['read'], - }, - ], - modes: { - default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'], - }, + data, layout: { - type: 'forceAtlas2', + type: 'force-atlas2', preventOverlap: true, kr: 20, center: [250, 250], }, - autoFit: 'view', - data, + behaviors: ['zoom-canvas', 'drag-canvas'], + autoResize: true, + zoomRange: [0.1, 5], }); -window.graph = graph; + + graph.render(); }); diff --git a/packages/site/examples/net/forceDirected/demo/basicForce.js b/packages/site/examples/net/forceDirected/demo/basicForce.js index fc3c5958b60..6d9fadda8b9 100644 --- a/packages/site/examples/net/forceDirected/demo/basicForce.js +++ b/packages/site/examples/net/forceDirected/demo/basicForce.js @@ -512,88 +512,37 @@ const data = { }; data.nodes.forEach((node) => (node.data.size = Math.random() * 30 + 5)); -const container = document.getElementById('container'); -const width = container.scrollWidth; -const height = container.scrollHeight || 500; + +const COLORS = { + a: '#4087FF', + b: '#CD74FE', + c: '#05B8A7', + d: '#FE8834', +}; + const graph = new Graph({ container: 'container', - width, - height, + data, node: { - lodLevels: [], - keyShape: { - r: { - fields: ['size'], - formatter: (model) => model.data.size / 2, - }, - }, - labelShape: { - text: { - fields: ['id'], - formatter: (node) => node.id, - }, - }, - animates: { - update: [ - { - fields: ['opacity'], - shapeId: 'haloShape', - }, - { - fields: ['lineWidth'], - shapeId: 'keyShape', - }, - ], + style: { + type: 'circle', + size: (d) => d.data.size, + labelText: (d) => d.id, + ports: [], + fill: (d) => COLORS[d.data.cluster], }, }, layout: { type: 'force', linkDistance: 50, - maxSpeed: 100, - animated: true, + animation: true, clustering: true, nodeClusterBy: 'cluster', clusterNodeStrength: 70, }, - theme: { - type: 'spec', - specification: { - node: { - dataTypeField: 'cluster', - }, - edge: { - dataTypeField: 'cluster', - }, - }, - }, - modes: { - default: ['zoom-canvas', 'drag-canvas', 'click-select'], - }, - data, -}); - -/******** 拖拽固定节点的逻辑 *********/ -graph.on('node:dragstart', function (e) { - graph.stopLayout(); -}); -graph.on('node:drag', function (e) { - refreshDragedNodePosition(e); -}); -graph.on('node:dragend', (e) => { - graph.layout(); + behaviors: ['zoom-canvas', 'drag-canvas'], + zoomRange: [0.1, 5], + autoResize: true, }); -function refreshDragedNodePosition(e) { - const { x, y } = e.canvas; - graph.updateData('node', { - id: e.itemId, - data: { - fx: x, - fy: y, - x, - y, - }, - }); -} -/*********************************/ -window.graph = graph; +graph.render(); diff --git a/packages/site/examples/net/forceDirected/demo/forceBubbles.js b/packages/site/examples/net/forceDirected/demo/forceBubbles.js deleted file mode 100644 index a9a9c8f4c7c..00000000000 --- a/packages/site/examples/net/forceDirected/demo/forceBubbles.js +++ /dev/null @@ -1,155 +0,0 @@ -import { Graph } from '@antv/g6'; - -// TODO: skip this demo - -const data = { - nodes: [ - { - id: '0', - label: '0', - value: 10, - cluster: 'a', - description: 'this is node 0, \nand the value of it is 10', - }, - { - id: '1', - label: '1', - value: 20, - cluster: 'b', - description: 'this is node 1, \nand the value of it is 20', - }, - { - id: '2', - label: '2', - value: 5, - cluster: 'a', - description: 'this is node 2, \nand the value of it is 5', - }, - { - id: '3', - label: '3', - value: 10, - cluster: 'a', - description: 'this is node 3, \nand the value of it is 10', - }, - { - id: '4', - label: '4', - value: 12, - cluster: 'c', - subCluster: 'sb', - description: 'this is node 4, \nand the value of it is 12', - }, - { - id: '5', - label: '5', - value: 18, - cluster: 'c', - subCluster: 'sa', - description: 'this is node 5, \nand the value of it is 18', - }, - { - id: '6', - label: '6', - value: 3, - cluster: 'c', - subCluster: 'sa', - description: 'this is node 6, \nand the value of it is 3', - }, - { - id: '7', - label: '7', - value: 7, - cluster: 'b', - subCluster: 'sa', - description: 'this is node 7, \nand the value of it is 7', - }, - { - id: '8', - label: '8', - value: 21, - cluster: 'd', - subCluster: 'sb', - description: 'this is node 8, \nand the value of it is 21', - }, - { - id: '9', - label: '9', - value: 9, - cluster: 'd', - subCluster: 'sb', - description: 'this is node 9, \nand the value of it is 9', - }, - ], - edges: [], -}; - -const tipDiv = document.createElement('div'); -tipDiv.innerHTML = 'Try to click or drag a bubble!'; -const graphDiv = document.getElementById('container'); -graphDiv.appendChild(tipDiv); - -const container = document.getElementById('container'); -const width = container.scrollWidth; -const height = (container.scrollHeight || 500) - 20; - -const graph = new Graph({ - container: 'container', - width, - height, - transforms: [ - 'transform-v4-data', - { - type: 'map-node-size', - field: 'value', - }, - ], - theme: { - type: 'spec', - specification: { - node: { - dataTypeField: 'cluster', - }, - }, - }, - layout: { - type: 'force', - nodeStrength: 10, - preventOverlap: true, - animated: true, - nodeSize: (node) => (node.data.keyShape.r || 16) * 2, - }, - modes: { - default: ['drag-node'], - }, - node: { - labelShape: { - position: 'center', - }, - }, - data, -}); - -graph.on('node:dragstart', function (e) { - graph.stopLayout(); -}); -graph.on('node:dragend', function (e) { - graph.layout(); -}); -graph.on('node:click', function (e) { - const currentR = graph.getNodeData(e.itemId).data.keyShape?.r; - const originR = graph.getNodeData(e.itemId).data?.originR || currentR; - graph.updateData('node', { - id: e.itemId, - data: { - keyShape: { - r: currentR === 100 ? originR : 100, - }, - originR, - }, - }); - // TODO: did not envoke - graph.layout({ type: 'force' }); -}); - -window.graph = graph; diff --git a/packages/site/examples/net/forceDirected/demo/forceConstrainedInRect.js b/packages/site/examples/net/forceDirected/demo/forceConstrainedInRect.js deleted file mode 100644 index 6ae58a38b1e..00000000000 --- a/packages/site/examples/net/forceDirected/demo/forceConstrainedInRect.js +++ /dev/null @@ -1,80 +0,0 @@ -import { Graph } from '@antv/g6'; - -// TODO: do not add this demo, onTick cannot assign the original layout data - -const graphDiv = document.getElementById('container'); -const descriptionDiv = document.createElement('div'); -descriptionDiv.innerHTML = 'Constrians the nodes to be layed in the gray area with force-directed layout'; -graphDiv.appendChild(descriptionDiv); - -const container = document.getElementById('container'); -const width = container.scrollWidth; -const height = container.scrollHeight || 500; - -fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json') - .then((res) => res.json()) - .then((data) => { - // 灰色区域 - const constrainBox = { x: 60, y: 50, width: 500, height: 150 }; - - const backrect = document.createElement('div'); - backrect.style.backgroundColor = '#666'; - backrect.style.opacity = 0.1; - backrect.style.marginLeft = `${constrainBox.x}px`; - backrect.style.marginTop = `${constrainBox.y}px`; - backrect.style.width = `${constrainBox.width}px`; - backrect.style.height = `${constrainBox.height}px`; - backrect.style.position = 'absolute'; - graphDiv.appendChild(backrect); - - const onTick = (nodes) => { - let minx = 99999999; - let maxx = -99999999; - let miny = 99999999; - let maxy = -99999999; - let maxsize = -9999999; - nodes.forEach((node) => { - if (minx > node.data.x) { - minx = node.data.x; - } - if (maxx < node.data.x) { - maxx = node.data.x; - } - if (miny > node.data.y) { - miny = node.data.y; - } - if (maxy < node.data.y) { - maxy = node.data.y; - } - if (maxsize < node.size) { - maxsize = node.size; - } - }); - const scalex = (constrainBox.width - maxsize) / (maxx - minx); - const scaley = (constrainBox.height - maxsize) / (maxy - miny); - nodes.forEach((node) => { - node.data.x = (node.data.x - minx) * scalex + constrainBox.x; - node.data.y = (node.data.y - miny) * scaley + constrainBox.y; - }); - }; - - const graph = new Graph({ - container: 'container', - width, - height, - transforms: [ - { - type: 'transform-v4-data', - activeLifecycle: ['read'], - }, - ], - layout: { - type: 'force', - animated: true, - onTick, - }, - data, - }); - -window.graph = graph; - }); diff --git a/packages/site/examples/net/forceDirected/demo/forceDirectedConfigurationTranslate.js b/packages/site/examples/net/forceDirected/demo/forceDirectedConfigurationTranslate.js index 3f8d083b23c..acbd31f3bef 100644 --- a/packages/site/examples/net/forceDirected/demo/forceDirectedConfigurationTranslate.js +++ b/packages/site/examples/net/forceDirected/demo/forceDirectedConfigurationTranslate.js @@ -522,60 +522,30 @@ const graph = new Graph({ container: 'container', width, height, - modes: { - default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'], - }, - layout: { - type: 'force', - linkDistance: 50, - }, + data, node: (model) => { return { id: model.id, - data: { - ...model.data, - labelShape: { - text: model.data.label, - }, - labelBackgroundShape: {}, - animates: { - update: [ - { - fields: ['x', 'y'], - shapeId: 'group', - }, - ], - }, - }, + labelText: (d) => model.data.label, }; }, - data, + layout: { + type: 'force', + linkDistance: 50, + animation: true, + }, + behaviors: ['zoom-canvas', 'drag-canvas'], }); -layoutConfigTranslation(); - -setInterval(() => { - layoutConfigTranslation(); +setTimeout(() => { + descriptionDiv.innerHTML = 'Force layout, linkDistance = 100, preventOverlap: true'; + graph.layout({ + type: 'force', + linkDistance: 100, + preventOverlap: true, + nodeSize: 20, + animation: true, + }); }, 5000); -function layoutConfigTranslation() { - setTimeout(() => { - descriptionDiv.innerHTML = 'Force layout, linkDistance = 100, preventOverlap: true'; - graph.layout({ - type: 'force', - linkDistance: 100, - preventOverlap: true, - nodeSize: 20, - }); - }, 2500); - setTimeout(() => { - descriptionDiv.innerHTML = 'Force layout, linkDistance = 50, preventOverlap: false'; - graph.layout({ - type: 'force', - linkDistance: 50, - preventOverlap: false, - }); - }, 5000); -} - -window.graph = graph; +graph.render(); diff --git a/packages/site/examples/net/forceDirected/demo/forceDirectedFunctionalParams.js b/packages/site/examples/net/forceDirected/demo/forceDirectedFunctionalParams.js index ee38b4b7a53..580a25f8161 100644 --- a/packages/site/examples/net/forceDirected/demo/forceDirectedFunctionalParams.js +++ b/packages/site/examples/net/forceDirected/demo/forceDirectedFunctionalParams.js @@ -1,32 +1,7 @@ import { Graph } from '@antv/g6'; -const container = document.getElementById('container'); -const width = container.scrollWidth; -const height = container.scrollHeight || 500; const graph = new Graph({ container: 'container', - width, - height, - transforms: [ - { - type: 'transform-v4-data', - activeLifecycle: ['read'], - }, - ], - layout: { - type: 'force', - preventOverlap: true, - animated: true, - linkDistance: (d) => { - if (d.source === 'node0' || d.target === 'node0') { - return 200; - } - return 80; - }, - }, - modes: { - default: ['zoom-canvas', 'drag-canvas', 'click-select', 'drag-node'], - }, data: { nodes: [ { id: 'node0', size: 50 }, @@ -66,6 +41,25 @@ const graph = new Graph({ { source: 'node3', target: 'node16' }, ], }, + node: { + style: { + size: (d) => d.size, + }, + }, + layout: { + type: 'force', + preventOverlap: true, + animation: true, + linkDistance: (d) => { + if (d.source === 'node0' || d.target === 'node0') { + return 200; + } + return 80; + }, + }, + behaviors: ['zoom-canvas', 'drag-canvas'], + autoResize: true, + zoomRange: [0.1, 5], }); -window.graph = graph; +graph.render();