1 回答
TA贡献1890条经验 获得超9个赞
我认为您遇到的问题是度数和弧度之间的转换。这就是为什么你必须在例程中使用聪明的解决方案rotate - translate - rotate
。
我已将集群更改为以弧度输出,然后groups
作为文本添加为节点的父节点。我还更改了链接类,使传入和传出链接的颜色不同。如果一个链接有两个类,那么它是绿色的。
你把类link--source
和link--target
类颠倒了,我把它颠倒了。
var diameter = 900,
radius = diameter / 2,
innerRadius = radius - 180;
var cluster = d3.cluster()
.size([2 * Math.PI, innerRadius]);
var line = d3.radialLine()
.curve(d3.curveBundle.beta(0.2))
.radius(function(d) {
return d.y;
})
.angle(function(d) {
return d.x;
});
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
var link = svg.append("g").selectAll(".link"),
node = svg.append("g").selectAll(".node"),
group = svg.append("g").selectAll(".group");
let samlDeps = [
'dsi.backend.SAML-ASSERTIONS',
'dsi.frontend.OIDC'
];
var arc = d3.arc()
.innerRadius(10)
.outerRadius(15);
var classes = [{
'name': 'dsi.frontend.HELP',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.frontend.PROFILE',
'dsi.frontend.INTERACTIONS',
'dsi.frontend.SERVICES',
'dsi.frontend.SUPPORT',
'dsi.backend.APPLICATIONS',
'dsi.cache.REDIS',
'dsi.frontend.CDN',
]
},
{
'name': 'dsi.frontend.INTERACTIONS',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.frontend.PROFILE',
'dsi.frontend.HELP',
'dsi.frontend.SERVICES',
'dsi.frontend.SUPPORT',
'dsi.db.DIRECTORIES',
'dsi.frontend.OIDC',
'dsi.backend.DEVICES',
'dsi.backend.ORGANISATIONS',
'dsi.backend.APPLICATIONS',
'dsi.backend.ACCESS',
'dsi.cache.REDIS',
'dsi.frontend.CDN',
]
},
{
'name': 'dsi.frontend.MANAGE',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.frontend.INTERACTIONS',
'dsi.frontend.HELP',
'dsi.frontend.SERVICES',
'dsi.frontend.PROFILE',
'dsi.backend.ACCESS',
'dsi.backend.APPLICATIONS',
'dsi.backend.DIRECTORIES',
'dsi.db.ORGANISATION',
'dsi.cache.SEARCH',
'dsi.cache.REDIS',
'dsi.frontend.CDN',
'dsi.frontend.OIDC',
]
},
{
'name': 'dsi.frontend.OIDC',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.backend.DIRECTORIES',
'dsi.cache.REDIS',
'dsi.frontend.HELP',
'dsi.db.DIRECTORIES',
'dsi.db.ORGANISATION',
'dsi.backend.APPLICATIONS',
'dsi.frontend.CDN',
'dsi.frontend.INTERACTIONS',
]
},
{
'name': 'dsi.frontend.PUBLIC-API',
'size': 1000,
'imports': [
'dsi.cache.REDIS',
'dsi.backend.DIRECTORIES',
'dsi.backend.APPLICATIONS',
'dsi.backend.ACCESS',
'dsi.backend.ORGANISATIONS',
]
},
{
'name': 'dsi.frontend.PROFILE',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.frontend.INTERACTIONS',
'dsi.frontend.HELP',
'dsi.frontend.SERVICES',
'dsi.frontend.SUPPORT',
'dsi.backend.DIRECTORIES',
'dsi.backend.ORGANISATIONS',
'dsi.backend.APPLICATIONS',
'dsi.backend.ACCESS',
'dsi.cache.SEARCH',
'dsi.frontend.CDN',
'dsi.frontend.OIDC',
]
},
{
'name': 'dsi.frontend.SUPPORT',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.frontend.PROFILE',
'dsi.frontend.INTERACTIONS',
'dsi.frontend.HELP',
'dsi.frontend.SERVICES',
'dsi.frontend.OIDC',
'dsi.cache.SEARCH',
'dsi.cache.REDIS',
'dsi.backend.DIRECTORIES',
'dsi.backend.ORGANISATIONS',
'dsi.backend.DEVICES',
'dsi.backend.APPLICATIONS',
'dsi.backend.ACCESS',
'dsi.frontend.CDN',
]
},
{
'name': 'dsi.frontend.SERVICES',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.frontend.PROFILE',
'dsi.frontend.INTERACTIONS',
'dsi.frontend.HELP',
'dsi.frontend.SUPPORT',
'dsi.backend.DIRECTORIES',
'dsi.backend.ORGANISATIONS',
'dsi.backend.ACCESS',
'dsi.backend.APPLICATIONS',
'dsi.cache.SEARCH',
'dsi.db.ORGANISATION',
'dsi.frontend.OIDC',
'dsi.cache.REDIS',
'dsi.db.DIRECTORIES',
'dsi.db.ORGANISATION',
'dsi.frontend.CDN',
]
},
{
'name': 'dsi.frontend.CDN',
'size': 1000,
'imports': []
},
{
'name': 'dsi.backend.ACCESS',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.db.ORGANISATION',
'dsi.cache.REDIS',
]
},
{
'name': 'dsi.backend.APPLICATIONS',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.db.ORGANISATION',
'dsi.db.ORGANISATION',
]
},
{
'name': 'dsi.backend.SAML-ASSERTIONS',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.backend.DIRECTORIES',
'dsi.backend.ACCESS',
'dsi.backend.APPLICATIONS',
]
},
{
'name': 'dsi.backend.DEVICES',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.frontend.CDN',
'dsi.db.DEVICES',
]
},
{
'name': 'dsi.backend.DIRECTORIES',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.db.DIRECTORIES',
'dsi.backend.APPLICATIONS',
'dsi.cache.REDIS',
]
},
{
'name': 'dsi.backend.ORGANISATIONS',
'size': 1000,
'imports': [
'dsi.db.AUDIT',
'dsi.db.ORGANISATION',
'dsi.backend.DIRECTORIES',
'dsi.cache.REDIS',
]
},
{
'name': 'dsi.backend.sa',
'size': 1000,
'imports': [
'dsi.backend.APPLICATIONS',
]
},
{
'name': 'dsi.saml.A-FORMS',
'size': 1000,
'imports': samlDeps
},
{
'name': 'dsi.saml.ASP',
'size': 1000,
'imports': samlDeps
},
{
'name': 'dsi.saml.COLLECT',
'size': 1000,
'imports': samlDeps
},
{
'name': 'dsi.saml.DQT',
'size': 1000,
'imports': samlDeps
},
{
'name': 'dsi.saml.GIAS',
'size': 1000,
'imports': samlDeps
},
{
'name': 'dsi.saml.INFORMATION-EXCHANGE',
'size': 1000,
'imports': samlDeps
},
{
'name': 'dsi.saml.KEY-TO-SUCCESS',
'size': 1000,
'imports': samlDeps
},
{
'name': 'dsi.saml.KEY-TO-SUCCESS_SECURE-ACCESS',
'size': 1000,
'imports': samlDeps
},
{
'name': 'dsi.saml.psp',
'size': 1000,
'imports': samlDeps
},
{
'name': 'dsi.saml.SCHOOL-2-SCHOOL',
'size': 1000,
'imports': samlDeps
},
{
'name': 'dsi.db.AUDIT',
'size': 1000,
'imports': []
},
{
'name': 'dsi.db.DIRECTORIES',
'size': 1000,
'imports': []
},
{
'name': 'dsi.db.ORGANISATION',
'size': 1000,
'imports': []
},
{
'name': 'dsi.db.DEVICES',
'size': 1000,
'imports': []
},
{
'name': 'dsi.cache.REDIS',
'size': 1000,
'imports': []
},
{
'name': 'dsi.cache.SEARCH',
'size': 1000,
'imports': []
},
];
var root = packageHierarchy(classes)
.sum(function(d) {
return d.size;
});
cluster(root);
group = group
.data(root.descendants().filter(function(d) {
return d.height === 1;
}))
.enter()
.append('text')
.each(function(d) {
d.angle = (d3.sum(d.children, function(v) {
return v.x;
}) / d.children.length);
d.radius = d3.max(d.children, function(v) {
return v.y;
}) + 140;
})
.classed("group", true)
.attr('x', function(d) {
return Math.sin(d.angle) * d.radius;
})
.attr('y', function(d) {
return -Math.cos(d.angle) * d.radius;
})
.attr("text-anchor", function(d) {
return d.angle < Math.PI ? "start" : "end";
})
.text(function(d) {
return d.data.key.toUpperCase();
});
link = link
.data(packageImports(root.leaves()))
.enter().append("path")
.each(function(d) {
d.source = d[0], d.target = d[d.length - 1];
})
.attr("class", "link")
.attr("d", line);
node = node
.data(root.leaves())
.enter().append("text")
.attr("class", "node")
.attr("dy", "0.31em")
.attr('x', function(d) {
return Math.sin(d.x) * d.y;
})
.attr('y', function(d) {
return -Math.cos(d.x) * d.y;
})
.attr("transform", function(d) {
const angle = (d.x * 180 / Math.PI) - 90 + (d.x < Math.PI ? 0 : 180);
const x = Math.sin(d.x) * d.y;
const y = -Math.cos(d.x) * d.y
return "rotate(" + angle + "," + x + "," + y + ")";
})
.attr("text-anchor", function(d) {
return d.x < Math.PI ? "start" : "end";
})
.text(function(d) {
return d.data.key;
})
.on("mouseover", mouseovered)
.on("mouseout", mouseouted);
function mouseovered(d) {
node
.each(function(n) {
n.target = n.source = false;
});
link
.classed("link--source", function(l) {
if (l.target === d) return l.source.source = true;
})
.classed("link--target", function(l) {
if (l.source === d) return l.target.target = true;
})
.filter(function(l) {
return l.target === d || l.source === d;
})
.raise();
node
.classed("node--target", function(n) {
return n.target;
})
.classed("node--source", function(n) {
return n.source;
});
}
function mouseouted(d) {
link
.classed("link--target", false)
.classed("link--source", false);
node
.classed("node--target", false)
.classed("node--source", false);
}
// Lazily construct the package hierarchy from class names.
function packageHierarchy(classes) {
var map = {};
function find(name, data) {
var node = map[name],
i;
if (!node) {
node = map[name] = data || {
name: name,
children: []
};
if (name.length) {
node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
node.parent.children.push(node);
node.key = name.substring(i + 1);
}
}
return node;
}
classes.forEach(function(d) {
find(d.name, d);
});
return d3.hierarchy(map[""]);
}
// Return a list of imports for the given array of nodes.
function packageImports(nodes) {
var map = {},
imports = [];
// Compute a map from name to node.
nodes.forEach(function(d) {
map[d.data.name] = d;
});
// For each import, construct a link from the source to target node.
nodes.forEach(function(d) {
if (d.data.imports) d.data.imports.forEach(function(i) {
imports.push(map[d.data.name].path(map[i]));
});
});
return imports;
}
.node,
.group {
font: 300 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
fill: #444;
}
.group {
font-size: 16px;
}
.node:hover {
fill: #000;
}
.link {
stroke: steelblue;
stroke-opacity: 0.5;
fill: none;
pointer-events: none;
stroke-width: 1px;
stroke-linecap: round;
}
.node:hover,
.node--source,
.node--target {
font-weight: 700;
}
/* text color */
.node--source {
fill: red;
}
.node--target {
fill: blue;
}
.node--target.node-source {
fill: green;
}
.link--source,
.link--target {
stroke-opacity: 1;
stroke-width: 2px;
}
/* line color */
.link--source {
stroke: red;
}
.link--target {
stroke: blue;
}
.link--source.link--target {
stroke: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
添加回答
举报