最近,我正在尝试理解D3和弦图,特别是D3v7。我得到了两个不同的数据集,并希望动态更新这些数据集。一般来说,它除了两个问题外,还能发挥作用:
。
因为D3v3和D3v7有一点变化,所以我想不出足够快的办法来帮助自己。
////////////////////////////////////////////////////////////
////////////////////// Set-Up Data /////////////////////////
////////////////////////////////////////////////////////////
let groups1 = [
{ name: "A", color: "blue" },
{ name: "B", color: "red" },
{ name: "C", color: "green" },
{ name: "D", color: "yellow" },
]
let paths1 = [
{ from: groups1.name = "A", to: groups1.name = "A", strength: 0 },
{ from: groups1.name = "A", to: groups1.name = "B", strength: 5 },
{ from: groups1.name = "A", to: groups1.name = "C", strength: 5 },
{ from: groups1.name = "A", to: groups1.name = "D", strength: 5 },
{ from: groups1.name = "B", to: groups1.name = "A", strength: 5 },
{ from: groups1.name = "B", to: groups1.name = "B", strength: 0 },
{ from: groups1.name = "B", to: groups1.name = "C", strength: 5 },
{ from: groups1.name = "B", to: groups1.name = "D", strength: 5 },
{ from: groups1.name = "C", to: groups1.name = "A", strength: 5 },
{ from: groups1.name = "C", to: groups1.name = "B", strength: 5 },
{ from: groups1.name = "C", to: groups1.name = "C", strength: 0 },
{ from: groups1.name = "C", to: groups1.name = "D", strength: 5 },
{ from: groups1.name = "D", to: groups1.name = "D", strength: 0 },
{ from: groups1.name = "D", to: groups1.name = "A", strength: 5 },
{ from: groups1.name = "D", to: groups1.name = "B", strength: 5 },
{ from: groups1.name = "D", to: groups1.name = "C", strength: 5 },
]
let groups2 = [
{ name: "E", color: "#301E1E" },
{ name: "F", color: "#083E77" },
{ name: "G", color: "#342350" },
]
let paths2 = [
{ from: groups2.name = "E", to: groups2.name = "E", strength: 0 },
{ from: groups2.name = "E", to: groups2.name = "F", strength: 5 },
{ from: groups2.name = "E", to: groups2.name = "G", strength: 5 },
{ from: groups2.name = "F", to: groups2.name = "E", strength: 5 },
{ from: groups2.name = "F", to: groups2.name = "F", strength: 0 },
{ from: groups2.name = "F", to: groups2.name = "G", strength: 5 },
{ from: groups2.name = "G", to: groups2.name = "E", strength: 5 },
{ from: groups2.name = "G", to: groups2.name = "F", strength: 5 },
{ from: groups2.name = "G", to: groups2.name = "G", strength: 0 },
]
let chord = []
let matrix = []
function getMatrix(paths, groups) {
matrix = []
var mapPaths = paths.map(item => {
const container = {}
container.from = groups.findIndex(ele => ele.name == item.from)
container.to = groups.findIndex(ele => ele.name == item.to)
container.strength = item.strength
return container
})
mapPaths.forEach(function (item) {
// initialize sub-arra if not yet exists
if (!matrix[item.to]) {
matrix[item.to] = []
}
matrix[item.to][item.from] = item.strength
})
return matrix
}
// --- --- --- --- --- ---
const vw = document.documentElement.clientWidth / 2
const vh = document.documentElement.clientHeight / 2
let innerRadius = Math.min(vw, vh) * 0.5
let outerRadius = innerRadius * 1.1;
let svg = d3.select("#chart").append("svg")
.attr("width", vw)
.attr("height", vh)
.attr("overflow", "unset")
var chordGenerator = d3.chord()
.padAngle(0.10)
.sortSubgroups(d3.ascending)
.sortChords(d3.descending)
chord = chordGenerator(getMatrix(paths1, groups1))
var arc = d3.arc()
.innerRadius(innerRadius * 1.01)
.outerRadius(outerRadius)
var ribbon = d3.ribbon()
.radius(innerRadius);
window.update = update;
update(paths1, groups1)
function update(thisPaths, thisGroups) {
let groups = thisGroups
let paths = thisPaths
getMatrix(thisPaths, thisGroups)
const chords = chordGenerator(matrix);
let wrapper = svg.append("g")
.attr("transform", "translate(" + vw / 2 + "," + vh / 2 + ")")
let ribbons = wrapper.append("g");
let g = wrapper.selectAll("g")
.data(chord.groups)
.enter()
.append("g")
.attr("class", "group")
const ribbonsUpdate = ribbons.selectAll("path")
.data(chords, ({ source, target }) => source.index + '-' + target.index)
const duration = 1000;
ribbonsUpdate
.transition()
.duration(duration)
.attr("d", ribbon)
.style("fill", function (d) { return groups[d.target.index].color; })
ribbonsUpdate
.enter()
.append("path")
.attr("opacity", 0)
.attr("d", ribbon)
.style("fill", function (d) { return groups[d.target.index].color; })
.transition()
.duration(duration)
.attr('opacity', 1)
// adding Arc Blocks
g.append("path")
.style("fill", function (d) { return groups[d.index].color; })
.attr("d", arc)
.style("opacity", 1)
// adding labels
g.append("text")
.each(function(d){ d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("dy", ".35em")
.attr("class", "titles")
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + (outerRadius + 10) + ")"
+ (d.angle > Math.PI ? "rotate(180)" : "");
})
.text(function(d,i){ return groups[i].name; })
ribbonsUpdate
.exit()
.transition()
.duration(duration)
.attr("opacity", 0)
.remove();
}
document.getElementById('data1').onclick = () => {
chord = chordGenerator(getMatrix(paths1, groups1))
update(paths1, groups1);
}
document.getElementById('data2').onclick = () => {
chord = chordGenerator(getMatrix(paths2, groups2))
update(paths2, groups2);
}body {
font-size: 16px;
font-family: 'Oswald', sans-serif;
text-align: center;
background-color: #ECF0F3;
cursor: default;
overflow: hidden;
}<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>Animated_D3v7</title>
<!-- jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<!-- D3.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js" charset="utf-8"></script>
<!-- fontawesome stylesheet https://fontawesome.com/ -->
<script src="https://kit.fontawesome.com/98a5e27706.js" crossorigin="anonymous"></script>
</head>
<style>
body {
font-size: 16px;
font-family: 'Oswald', sans-serif;
background-color: #ECF0F3;
cursor: default;
overflow: hidden;
}
</style>
<body>
<button id="data1">data 1</button>
<button id="data2">data 2</button>
<div id="chart"></div>
</body>
</html>
发布于 2022-09-13 10:36:09
问题1通过以下方式得到解决:
let g = wrapper.selectAll("g.group") // add class of 'group' to the selector否则,所选内容有一些模糊性,因为您已经在前面的语句中向g添加了一个wrapper:let ribbons = wrapper.append("g");
问题2似乎涉及得更多一些。
首先,每次update被称为g时,在svg下创建一个新的g并分配给wrapper。您只需一次就可以在wrapper之外定义update。
其次,update使用chord和chords,前者在update之外更新,后者在update中更新。您可以不使用前者,只需在chord和matrix中更新update。
第三,您可以为外部的每个弧、缎带和标签设置一个g,然后进入、更新和退出逻辑针对它们中的path和text元素工作。
在下面的工作示例中,我简化了其他几点:
const vw = document.documentElement.clientWidth / 2
const vh = document.documentElement.clientHeight / 2
const innerRadius = Math.min(vw, vh) * 0.4
const outerRadius = innerRadius * 1.1;
const duration = 1000;
const svg = d3.select("#chart").append("svg")
.attr("width", vw)
.attr("height", vh)
const wrapper = svg.append("g")
.attr("transform", "translate(" + vw / 2 + "," + vh / 2 + ")")
const ribbonsG = wrapper.append("g").attr("id", "ribbons")
const arcsG = wrapper.append("g").attr("id", "arcs")
const labelsG = wrapper.append("g").attr("id", "labels")
const chordGenerator = d3.chord()
.padAngle(0.10)
.sortSubgroups(d3.ascending)
.sortChords(d3.descending)
const arc = d3.arc()
.innerRadius(innerRadius * 1.01)
.outerRadius(outerRadius)
const ribbon = d3.ribbon()
.radius(innerRadius);
update(paths1, groups1)
function getMatrix(paths, groups) {
matrix = []
var mapPaths = paths.map(item => {
const container = {}
container.from = groups.findIndex(ele => ele.name == item.from)
container.to = groups.findIndex(ele => ele.name == item.to)
container.strength = item.strength
return container
})
mapPaths.forEach(function (item) {
// initialize sub-arra if not yet exists
if (!matrix[item.to]) {
matrix[item.to] = []
}
matrix[item.to][item.from] = item.strength
})
return matrix
}
function update(thisPaths, thisGroups) {
const groups = thisGroups
const paths = thisPaths
const chords = chordGenerator(getMatrix(thisPaths, thisGroups));
// ribbons
const ribbonsUpdate = ribbonsG
.selectAll("path.ribbon")
.data(chords, ({ source, target }) => source.index + '-' + target.index)
const ribbonsEnter = ribbonsUpdate
.enter()
.append("path")
ribbonsUpdate
.merge(ribbonsEnter)
.attr("opacity", 0)
.attr("class", "ribbon")
.transition()
.duration(duration)
.attr("d", ribbon)
.style("fill", function (d) { return groups[d.target.index].color; })
.attr('opacity', 1)
ribbonsUpdate
.exit()
.transition()
.duration(duration)
.attr("opacity", 0)
.remove();
// arcs
const arcsUpdate = arcsG
.selectAll("path.arc")
.data(chords.groups)
const arcsEnter = arcsUpdate
.enter()
.append("path")
arcsUpdate
.merge(arcsEnter)
.attr("opacity", 0)
.attr("class", "arc")
.transition()
.duration(duration)
.attr("d", arc)
.style("fill", function (d) { return groups[d.index].color; })
.attr('opacity', 1)
arcsUpdate
.exit()
.transition()
.duration(duration)
.attr("opacity", 0)
.remove();
// adding labels
const labelsUpdate = labelsG
.selectAll("text.titles")
.data(chords.groups)
const labelsEnter = labelsUpdate
.enter()
.append("text")
labelsUpdate
.merge(labelsEnter)
.attr("class", "titles")
.attr("opacity", 0)
.transition()
.duration(duration)
.each(function(d){ d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + (outerRadius + 10) + ")"
+ (d.angle > Math.PI ? "rotate(180)" : "");
})
.text(function(d,i){ return groups[i].name; })
.attr("opacity", 1)
labelsUpdate
.exit()
.remove()
}
document.getElementById('data1').onclick = () => {
update(paths1, groups1);
}
document.getElementById('data2').onclick = () => {
update(paths2, groups2);
}<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js" charset="utf-8"></script>
<div id="chart"></div>
<button id="data1">data 1</button>
<button id="data2">data 2</button>
<script>
let groups1 = [
{ name: "A", color: "blue" },
{ name: "B", color: "red" },
{ name: "C", color: "green" },
{ name: "D", color: "yellow" },
]
let paths1 = [
{ from: groups1.name = "A", to: groups1.name = "A", strength: 0 },
{ from: groups1.name = "A", to: groups1.name = "B", strength: 5 },
{ from: groups1.name = "A", to: groups1.name = "C", strength: 5 },
{ from: groups1.name = "A", to: groups1.name = "D", strength: 5 },
{ from: groups1.name = "B", to: groups1.name = "A", strength: 5 },
{ from: groups1.name = "B", to: groups1.name = "B", strength: 0 },
{ from: groups1.name = "B", to: groups1.name = "C", strength: 5 },
{ from: groups1.name = "B", to: groups1.name = "D", strength: 5 },
{ from: groups1.name = "C", to: groups1.name = "A", strength: 5 },
{ from: groups1.name = "C", to: groups1.name = "B", strength: 5 },
{ from: groups1.name = "C", to: groups1.name = "C", strength: 0 },
{ from: groups1.name = "C", to: groups1.name = "D", strength: 5 },
{ from: groups1.name = "D", to: groups1.name = "D", strength: 0 },
{ from: groups1.name = "D", to: groups1.name = "A", strength: 5 },
{ from: groups1.name = "D", to: groups1.name = "B", strength: 5 },
{ from: groups1.name = "D", to: groups1.name = "C", strength: 5 },
]
let groups2 = [
{ name: "E", color: "#301E1E" },
{ name: "F", color: "#083E77" },
{ name: "G", color: "#11AA99" },
]
let paths2 = [
{ from: groups2.name = "E", to: groups2.name = "E", strength: 0 },
{ from: groups2.name = "E", to: groups2.name = "F", strength: 5 },
{ from: groups2.name = "E", to: groups2.name = "G", strength: 5 },
{ from: groups2.name = "F", to: groups2.name = "E", strength: 5 },
{ from: groups2.name = "F", to: groups2.name = "F", strength: 0 },
{ from: groups2.name = "F", to: groups2.name = "G", strength: 5 },
{ from: groups2.name = "G", to: groups2.name = "E", strength: 5 },
{ from: groups2.name = "G", to: groups2.name = "F", strength: 5 },
{ from: groups2.name = "G", to: groups2.name = "G", strength: 0 },
]
</script>
https://stackoverflow.com/questions/73659174
复制相似问题