g6/demos/case-simplify.html
2020-02-14 11:30:12 +08:00

372 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>
<style>
body {
background: rgb(255, 255, 255);
}
.g6-tooltip {
border: 1px solid #e2e2e2;
border-radius: 4px;
font-size: 12px;
color: #545454;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px 8px;
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
canvas {
border: 1px solid red;
}
</style>
<body>
<div id="tip">布局中,请稍候......</div>
<div id="mountNode"></div>
<script src="../build/g6.js"></script>
<script src="./assets/d3-4.13.0.min.js"></script>
<script>
const colors = ['#f5222d', '#faad14', '#a0d911', '#13c2c2', '#1890ff', '#b37feb', '#eb2f96'];
const beginColor = '#5b8c00'; // green
const endColor = '#ff4d4f'; // red
d3.json('./assets/data/filtered-trade.json', function(data) {
const nodes = data.nodes;
const edges = data.edges;
const nodeMap = new Map();
const clusterMap = new Map();
let cidx = 0;
nodes.forEach(n => {
nodeMap.set(n.id, n);
let region = n.region.split(' ');
if (n.region === 'East Asia') region = n.region;
else region = region[region.length - 1];
if (clusterMap.get(region) === undefined) {
clusterMap.set(region, cidx);
cidx++;
}
const clusterId = clusterMap.get(region);
const color = colors[clusterId % colors.length];
n.style = {
color,
fill: color,
stroke: '#666',
lineWidth: 0.6,
};
n.cluster = clusterId;
n.importValue = 0;
n.exportValue = 0;
});
// map the value of
edges.forEach(e => {
e.id = e.eid;
if (e.value === '') e.value = 0;
const v = parseFloat(e.value);
nodeMap.get(e.source).exportValue += v;
nodeMap.get(e.target).importValue += v;
});
mapValueToProp(nodes, 'exportValue', 'size', [2, 12]);
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 600,
layout: {
type: 'fruchterman',
maxIteration: 8000,
gravity: 10,
clustering: true,
clusterGravity: 30,
workerEnabled: true,
},
padding: 0,
fitViewPadding: 0,
fitView: true,
linkCenter: true,
defaultNode: {
shape: 'circle',
size: 5,
},
defaultEdge: {
shape: 'quadratic',
},
modes: {
default: [
'drag-node',
'zoom-canvas',
'drag-canvas',
{
type: 'tooltip',
formatText(model) {
let name = '';
let countries = '';
if (model.name) name = model.name + '<br/>';
if (model.countries) countries = '<br/>Number of Countries: ' + model.countries;
const text =
name +
'Export Value: ' +
model.exportValue +
'(1000USD)' +
'<br/>Region: ' +
model.region +
'<br/>Cluster: ' +
model.cluster +
countries;
return text;
},
shouldUpdate: e => {
return true;
},
},
],
},
});
graph.on('beforelayout', () => {
console.log(data.nodes[0].x, data.nodes[0].y, data.nodes[10].x, data.nodes[10].y);
});
graph.on('afterlayout', () => {
const tipDiv = document.getElementById('tip');
tipDiv.innerHTML = '布局完成!';
const edgeItems = graph.getEdges();
edgeItems.forEach(e => {
const lineWidth = 0.4;
const strokeOpacity = 0.2;
let stroke = 'l(0) 0:' + beginColor + ' 1:' + endColor;
const sourceModel = e.getSource().getModel();
const targetModel = e.getTarget().getModel();
if (sourceModel.x > targetModel.x) {
stroke = 'l(0) 0:' + endColor + ' 1:' + beginColor;
}
e.update({
style: {
lineWidth,
strokeOpacity,
stroke,
},
});
});
// 等布局完以后再绘制
graph.paint();
});
graph.data(data);
// 如果使用web workergraph.render是异步的立即返回在web worker里执行布局。
// 而如果不使用web workergraph.render是同步的意味着只有等布局执行完以后才会执行graph.render后面的代码。
graph.render();
graph.on('node:click', e => {
const targetItem = e.item;
const model = targetItem.getModel();
const graphEdges = graph.getEdges();
const graphNodes = graph.getNodes();
// click on the cluster node
if (model.id.substr(0, 7) === 'cluster') {
graphNodes.forEach(gn => {
const gnModel = gn.getModel();
// show the common nodes
if (gnModel.cluster === model.cluster && gnModel.id.substr(0, 7) != 'cluster') {
gn.show();
}
// remove the cluster nodes
if (gnModel.id === model.id) graph.removeItem(gn);
});
graphEdges.forEach(ge => {
const edgeModel = ge.getModel();
const sourceModel = ge.get('sourceNode').getModel();
const targetModel = ge.get('targetNode').getModel();
// show the common edges
if (
(sourceModel.cluster === model.cluster &&
sourceModel.id.substr(0, 7) !== 'cluster') ||
(targetModel.cluster === model.cluster && targetModel.id.substr(0, 7) !== 'cluster')
) {
ge.show();
// add the edges to other cluster nodes
if (!ge.get('sourceNode').get('visible') && sourceModel.cluster !== model.cluster) {
let c1 = beginColor,
c2 = endColor;
if (model.x > targetModel.x) {
c1 = endColor;
c2 = beginColor;
}
graph.addItem('edge', {
source: 'cluster' + sourceModel.cluster,
target: targetModel.id,
id: 'cluster-edge-' + edgeModel.id,
style: {
stroke: 'l(0) 0:' + c1 + ' 1:' + c2,
lineWidth: 0.4,
strokeOpacity: 0.2,
},
});
} else if (
!ge.get('targetNode').get('visible') &&
targetModel.cluster !== model.cluster
) {
let c1 = beginColor,
c2 = endColor;
if (sourceModel.x > model.x) {
c1 = endColor;
c2 = beginColor;
}
graph.addItem('edge', {
source: sourceModel.id,
target: 'cluster' + targetModel.cluster,
id: 'cluster-edge-' + edgeModel.id,
style: {
stroke: 'l(0) 0:' + c1 + ' 1:' + c2,
lineWidth: 0.4,
strokeOpacity: 0.2,
},
});
}
// hide the edges to the common nodes in other clusters
if (!ge.get('sourceNode').get('visible') || !ge.get('targetNode').get('visible')) {
ge.hide();
}
}
//do not need the following 4 lines, since the related edges will be removed while the cluster node is removing
// // remove the cluster edges.
// if (sourceModel.id === model.id || targetModel.id === model.id) {
// graph.removeItem(ge);
// }
});
} else {
// click on the common node, cllapse them
// calculate the cluster center
const center = { x: 0, y: 0, count: 0, exportValue: 0 };
nodes.forEach(n => {
if (n.cluster === model.cluster) {
center.x += n.x;
center.y += n.y;
center.count++;
center.exportValue += n.exportValue;
}
});
center.x /= center.count;
center.y /= center.count;
// add cluster node on the center
const size = center.count * 1;
const clusterNodeId = 'cluster' + model.cluster;
const color = colors[model.cluster % colors.length];
const regionStrs = model.region.split(' ');
let region = regionStrs[regionStrs.length - 1];
if (model.region === 'East Asia') region = model.region;
let labelPosition = 'center';
if (region.length > size) labelPosition = 'left';
const clusterNode = graph.addItem('node', {
x: center.x,
y: center.y,
id: clusterNodeId,
cluster: model.cluster,
region: region,
countries: center.count,
exportValue: center.exportValue,
style: {
color,
fill: color,
stroke: '#666',
lineWidth: 0.6,
},
size,
label: region,
labelCfg: {
style: { fontSize: 8.5 },
position: labelPosition,
},
});
// add edges about the cluster
graphEdges.forEach(ge => {
const edgeModel = ge.getModel();
const sourceModel = ge.get('sourceNode').getModel();
const targetModel = ge.get('targetNode').getModel();
if (!ge.get('sourceNode').get('visible') || !ge.get('targetNode').get('visible'))
return;
if (sourceModel.cluster === model.cluster && targetModel.cluster !== model.cluster) {
let c1 = beginColor,
c2 = endColor;
if (center.x > targetModel.x) {
c1 = endColor;
c2 = beginColor;
}
graph.addItem('edge', {
source: clusterNodeId,
target: targetModel.id,
id: 'cluster-edge-' + edgeModel.id,
style: {
stroke: 'l(0) 0:' + c1 + ' 1:' + c2,
lineWidth: 0.4,
strokeOpacity: 0.2,
},
});
} else if (
targetModel.cluster === model.cluster &&
sourceModel.cluster !== model.cluster
) {
let c1 = beginColor,
c2 = endColor;
if (sourceModel.x > center.x) {
c1 = endColor;
c2 = beginColor;
}
graph.addItem('edge', {
source: sourceModel.id,
target: clusterNodeId,
id: 'cluster-edge-' + ge.id,
style: {
stroke: 'l(0) 0:' + c1 + ' 1:' + c2,
lineWidth: 0.4,
strokeOpacity: 0.2,
},
});
}
});
// hide the common nodes in the cluster
graphNodes.forEach(gn => {
if (
gn.getModel().cluster === model.cluster &&
gn.getModel().id.substr(0, 7) !== 'cluster'
)
gn.hide();
});
// hide the common edges about cluster
graphEdges.forEach(ge => {
if (!ge.get('sourceNode').get('visible') || !ge.get('targetNode').get('visible'))
ge.hide();
});
}
});
});
function mapValueToProp(items, valueName, propName, range) {
const valueRange = [9999999999, -9999999999];
items.forEach(n => {
if (n[valueName] > valueRange[1]) valueRange[1] = n[valueName];
if (n[valueName] < valueRange[0]) valueRange[0] = n[valueName];
});
const valueLength = valueRange[1] - valueRange[0];
const rLength = range[1] - range[0];
const propNameStrs = propName.split('.');
if (propNameStrs[0] === 'style' && propNameStrs.length > 1) {
items.forEach(n => {
if (n.style === undefined) n.style = {};
n.style[propNameStrs[1]] =
(rLength * (n[valueName] - valueRange[0])) / valueLength + range[0];
});
} else {
items.forEach(n => {
n[propNameStrs[0]] =
(rLength * (n[valueName] - valueRange[0])) / valueLength + range[0];
});
}
}
</script>
</body>
</html>