我一直在尝试为Force Directed Graph中的链接添加标签。我的代码基于this answer。最终结果应如下所示:
我设法为文字制作图表。它在我查看页面源时出现。但由于某些原因,它不会在视觉上显示出来。它只是隐藏在SVG的某个地方。
这是我的小提琴:https://jsfiddle.net/1dm5bm7b/
以下是代码的相关部分:
var labelElements = g.append("g")
.attr("class", "label")
.selectAll("text")
.data(links)
.enter().append("text")
.attr("font-size", 12)
.attr("font-family", "sans-serif")
.attr("fill", "#333")
.attr("x", "50")
.attr("y", "-20")
.attr("text-anchor", "start")
.append("textPath")
.attr("xlink:href",function(d,i) { return "#linkId_" + i; })
.text(function(link) {
return link.value;
})
simulation.nodes(nodes).on('tick', () => {
nodeElements
.attr('cx', function(node) {
return node.x
})
.attr('cy', function(node) {
return node.y
})
textElements
.attr('x', function(node) {
return node.x
})
.attr('y', function(node) {
return node.y
})
linkElements
.attr('x1', function(link) {
return link.source.x
})
.attr('y1', function(link) {
return link.source.y
})
.attr('x2', function(link) {
return link.target.x
})
.attr('y2', function(link) {
return link.target.y
})
labelElements
.attr('x', function(link) {
return link.target.x
})
.attr('y', function(link) {
return link.target.y
})
})
答案 0 :(得分:2)
正如comments中所述,您要追加<line>
个元素,而不是<path>
元素。
解决方案很简单:添加<path>
元素而不是行,并更改tick
函数:
linkElements.attr("d", function(link) {
return "M" + link.source.x + "," + link.source.y
+ " L" + link.target.x + "," + link.target.y;
});
以下是您更改的代码:
function getNeighbors(node) {
return links.reduce(function(neighbors, link) {
if (link.target.id === node.id) {
neighbors.push(link.source.id)
} else if (link.source.id === node.id) {
neighbors.push(link.target.id)
}
return neighbors
}, [node.id])
}
function isNeighborLink(node, link) {
return link.target.id === node.id || link.source.id === node.id
}
function getNodeColor(node, neighbors) {
// If is neighbor
if (Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1) {
return 'rgba(251, 130, 30, 1)'
// return node.level === 1 ? '#9C4A9C' : 'rgba(251, 130, 30, 1)'
} else {
// Check the node level
if (node.level === 0) {
return '#E72148'
} else if (node.level === 1) {
return '#9C4A9C'
} else {
return '#D8ABD8'
}
}
//return node.level === 0 ? '#91007B' : '#D8ABD8'
}
function getLinkColor(node, link) {
return isNeighborLink(node, link) ? 'rgba(251, 130, 30, 1)' : 'rgba(251, 130, 30, 0.25)'
}
function getTextColor(node, neighbors) {
return Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1 ? '#333' : '#bbb'
}
var width = window.innerWidth
var height = window.innerHeight
var svg = d3.select('svg')
// svg.attr('width', width).attr('height', height)
svg.attr("width", '100%')
.attr("height", '500px')
.attr('viewBox', '250 0 800 600')
//.attr('viewBox','0 0 '+Math.min(width,height)+' '+Math.min(width,height))
.attr('preserveAspectRatio', 'xMidYMid')
.append("g")
.attr("transform", "translate(" + Math.min(width, height) / 2 + "," + Math.min(width, height) / 2 + ")");
//add zoom capabilities
var zoom_handler = d3.zoom()
.scaleExtent([1 / 2, 8])
.on("zoom", zoom_actions);
zoom_handler(svg);
function zoom_actions() {
g.attr("transform", d3.event.transform)
}
function button_zoom_in() {
zoom_handler.scaleBy(svg, 2);
}
function button_zoom_out() {
zoom_handler.scaleBy(svg, 0.5);
}
// simulation setup with all forces
var linkForce = d3
.forceLink()
.id(function(link) {
return link.id
})
// Alternative: using the distance from the data "strength"
//.distance(50).strength(function (link) { return link.strength })
// If don't want to use this, use default here:
.distance(50).strength(.7)
var simulation = d3
.forceSimulation()
.force('link', linkForce)
.force('charge', d3.forceManyBody().strength(-1500))
.force('radial', d3.forceRadial(function(d) {
return d.level * 50
}, width / 2, height / 2))
.force('center', d3.forceCenter(width / 2, height / 2))
var dragDrop = d3.drag().on('start', function(node) {
node.fx = node.x
node.fy = node.y
}).on('drag', function(node) {
simulation.alphaTarget(0.7).restart()
node.fx = d3.event.x
node.fy = d3.event.y
}).on('end', function(node) {
if (!d3.event.active) {
simulation.alphaTarget(0)
}
node.fx = null
node.fy = null
})
function selectNode(selectedNode) {
var neighbors = getNeighbors(selectedNode)
// we modify the styles to highlight selected nodes
nodeElements.attr('fill', function(node) {
return getNodeColor(node, neighbors)
})
textElements.attr('fill', function(node) {
return getTextColor(node, neighbors)
})
linkElements.attr('stroke', function(link) {
return getLinkColor(selectedNode, link)
})
}
// Enables zooming
var g = svg.append("g")
.attr("class", "everything");
// Enables zooming end
// Create circling orbit
var circles = g.selectAll(null) // use g.selectAll instead of svg.selectAll to enable zoom
.data([200, 350]) // sets the circle radius
.enter()
.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("r", d => d)
.style("fill", "none")
.style("stroke", "#ddd");
var linkElements = g.append("g") // use g.append instead of svg.append to enable zoom
.attr("class", "links")
.selectAll("line")
.data(links)
.enter().append("path")
.attr("id", function(d, i) {
return "linkId_" + i;
})
.attr("stroke-width", function(link) {
var linkWidthNormalize = link.value / 1000000000; // in milyar
// under assumption that smallest 10 milyar, largest > 40 triliun
if (linkWidthNormalize >= 40001) {
return 12;
} else if (linkWidthNormalize >= 20001 && linkWidthNormalize <= 40000) {
return 10;
} else if (linkWidthNormalize >= 9001 && linkWidthNormalize <= 20000) {
return 8;
} else if (linkWidthNormalize >= 4001 && linkWidthNormalize <= 9000) {
return 6;
} else if (linkWidthNormalize >= 10 && linkWidthNormalize <= 4000) {
return 4;
} else {
return 2;
}
// return linkWidthNormalize;
})
.attr("stroke", "rgba(251, 130, 30, 0.5)")
var labelElements = g.append("g")
.attr("class", "label")
.selectAll("text")
.data(links)
.enter().append("text")
.attr("font-size", 12)
.attr("font-family", "sans-serif")
.attr("fill", "#333")
.attr("x", "50")
.attr("y", "-20")
.attr("text-anchor", "start")
.append("textPath")
.attr("xlink:href", function(d, i) {
return "#linkId_" + i;
})
.text(function(link) {
return link.value;
})
var nodeElements = g.append("g") // use g.append instead of svg.append to enable zoom
.attr("class", "nodes")
.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", 12)
.attr("fill", getNodeColor)
.attr("stroke", "#fff")
.attr('stroke-width', 2)
.call(dragDrop)
//.on('click', selectNode) // alternative
.on('mouseover', selectNode)
var textElements = g.append("g") // use g.append instead of svg.append to enable zoom
.attr("class", "texts")
.selectAll("text")
.data(nodes)
.enter().append("text")
.text(function(node) {
return node.label
})
.attr("font-size", 10)
.attr("font-family", "sans-serif")
.attr("text-anchor", "middle")
.attr("fill", "#333")
.attr("style", "font-weight:bold; -webkit-text-stroke: 1px #fff; text-shadow: 3px 3px 0 #fff, -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff")
.attr("dx", 0)
.attr("dy", 20)
simulation.nodes(nodes).on('tick', () => {
nodeElements
.attr('cx', function(node) {
return node.x
})
.attr('cy', function(node) {
return node.y
})
textElements
.attr('x', function(node) {
return node.x
})
.attr('y', function(node) {
return node.y
})
linkElements.attr("d", function(link) {
return "M" + link.source.x + "," + link.source.y + " L" + link.target.x + "," + link.target.y;
});
labelElements
.attr('x', function(link) {
return link.target.x
})
.attr('y', function(link) {
return link.target.y
})
})
simulation.force("link").links(links)
&#13;
<div class="buttons" id="sunburst-buttons">
<button class="btn btn-default" onclick="button_zoom_in()">+</button>
<button class="btn btn-default" onclick="button_zoom_out()">-</button>
</div>
<svg height="600" id="sunburst-area" width="960"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- Dataset -->
<script>
var nodes = [{
id: "pusat",
group: 0,
label: "Pusat",
level: 0
}, {
id: "dki",
group: 1,
label: "Prov. DKI",
level: 1
}, {
id: "jaksel",
group: 1,
label: "Kota Jakarta Selatan",
level: 2
}, {
id: "jakpus",
group: 1,
label: "Kota Jakarta Pusat",
level: 2
}, {
id: "jabar",
group: 2,
label: "Prov. Jawa Barat",
level: 1
}, {
id: "sumedang",
group: 2,
label: "Kab. Sumedang",
level: 2
}, {
id: "bekasi",
group: 2,
label: "Kota Bekasi",
level: 2
}, {
id: "bandung",
group: 2,
label: "Kota Bandung",
level: 2
}, {
id: "jatim",
group: 3,
label: "Prov. Jawa Timur",
level: 1
}, {
id: "malang",
group: 3,
label: "Kota Malang",
level: 2
}, {
id: "lamongan",
group: 3,
label: "Kota Lamongan",
level: 2
}, {
id: "diy",
group: 4,
label: "Prov. DIY",
level: 1
}, {
id: "sleman",
group: 4,
label: "Kab. Sleman",
level: 2
}, {
id: "jogja",
group: 4,
label: "Kota Yogyakarta",
level: 2
}, {
id: "bali",
group: 5,
label: "Prov. Bali",
level: 1
}, {
id: "bali1",
group: 5,
label: "Kota Denpasar",
level: 2
}, {
id: "bali2",
group: 5,
label: "Kab. Buleleng",
level: 2
}, {
id: "ntt",
group: 6,
label: "Prov. NTT",
level: 1
}, {
id: "ntt1",
group: 6,
label: "Kab. Alor",
level: 2
}, {
id: "ntt2",
group: 6,
label: "Kab. Manggarai Timur",
level: 2
}, {
id: "ntb",
group: 7,
label: "Prov. NTB",
level: 1
}, {
id: "kabima",
group: 7,
label: "Kab. Bima",
level: 2
}, {
id: "kobima",
group: 7,
label: "Kota Bima",
level: 2
}, {
id: "kaltara",
group: 8,
label: "Prov. Kaltara",
level: 1
}, {
id: "kubar",
group: 8,
label: "Kab. Kutai Barat",
level: 2
}, {
id: "kutim",
group: 8,
label: "Kab. Kutai Timur",
level: 2
}, {
id: "kaltim",
group: 9,
label: "Prov. Kaltim",
level: 1
}, {
id: "bpp",
group: 9,
label: "Kota Balikpapan",
level: 2
}, {
id: "samarinda",
group: 9,
label: "Kota Samarinda",
level: 2
}, {
id: "kalsel",
group: 10,
label: "Prov. Kalsel",
level: 1
}, {
id: "banjar",
group: 10,
label: "Kota Banjarmasin",
level: 2
}, {
id: "tapin",
group: 10,
label: "Kab. Tapin",
level: 2
}, {
id: "kalbar",
group: 11,
label: "Prov. Kalbar",
level: 1
}, {
id: "melawi",
group: 11,
label: "Kab. Melawi",
level: 2
}, {
id: "sambas",
group: 11,
label: "Kab. Sambas",
level: 2
}]
var links = [
// Pusat-Provinsi
{
source: "pusat",
target: "dki",
strength: .5,
value: 100000000000
}, {
source: "pusat",
target: "jabar",
strength: .5,
value: 30000000000
}, {
source: "pusat",
target: "jatim",
strength: .5,
value: 100000000000
}, {
source: "pusat",
target: "diy",
strength: .5,
value: 1000000000000
}, {
source: "pusat",
target: "bali",
strength: .5,
value: 10000000000
}, {
source: "pusat",
target: "ntt",
strength: .5,
value: 1000000000
}, {
source: "pusat",
target: "ntb",
strength: .5,
value: 1000000000
}, {
source: "pusat",
target: "kaltim",
strength: .5,
value: 5000000000000
}, {
source: "pusat",
target: "kaltara",
strength: .5,
value: 5000000000000
}, {
source: "pusat",
target: "kalsel",
strength: .5,
value: 10000000000000
}, {
source: "pusat",
target: "kalbar",
strength: .5,
value: 1000000000
},
// Provinsi-Kab/Kota
{
source: "dki",
target: "jaksel",
strength: .7,
value: 2000000000
}, {
source: "dki",
target: "jakpus",
strength: .7,
value: 4000000000000
}, {
source: "jabar",
target: "sumedang",
strength: .7,
value: 400000000000
}, {
source: "jabar",
target: "bekasi",
strength: .7,
value: 40000000000
}, {
source: "jabar",
target: "bandung",
strength: .7,
value: 40000000000
}, {
source: "jatim",
target: "malang",
strength: .7,
value: 300000000000
}, {
source: "jatim",
target: "lamongan",
strength: .7,
value: 100000000000
}, {
source: "diy",
target: "sleman",
strength: .7,
value: 4500000000000
}, {
source: "diy",
target: "jogja",
strength: .7,
value: 6700000000000
}, {
source: "bali",
target: "bali1",
strength: .7,
value: 100000000000000
}, {
source: "bali",
target: "bali2",
strength: .7,
value: 2400000000000
}, {
source: "ntt",
target: "ntt1",
strength: .7,
value: 60000000000000
}, {
source: "ntt",
target: "ntt2",
strength: .7,
value: 100000000000
}, {
source: "ntb",
target: "kabima",
strength: .7,
value: 126000000000
}, {
source: "ntb",
target: "kobima",
strength: .7,
value: 1000000000000
}, {
source: "kaltara",
target: "kubar",
strength: .7,
value: 12420000000000
}, {
source: "kaltara",
target: "kutim",
strength: .7,
value: 14400000000000
}, {
source: "kaltim",
target: "bpp",
strength: .7,
value: 1470000000000
}, {
source: "kaltim",
target: "samarinda",
strength: .7,
value: 1000000000000000
}, {
source: "kalsel",
target: "banjar",
strength: .7,
value: 137000000000
}, {
source: "kalsel",
target: "tapin",
strength: .7,
value: 5050000000000
}, {
source: "kalbar",
target: "melawi",
strength: .7,
value: 2400000000000
}, {
source: "kalbar",
target: "sambas",
strength: .7,
value: 5500000000000
}
];
</script>
&#13;