最近我試圖理解 D3 和弦圖,尤其是 D3v7。我有兩個不同的資料集,想動態更新這些資料集。一般來說,它可以解決兩個問題:
每個資料集中的第一個物件將被忽略。我不知道為什么,因為任何 console.log 都顯示現有資料,但它根本沒有繪制。例如,基于 groups1 和 paths1 的資料集 1 缺少物件 A,而在 dataset2 中缺少物件 E。
一旦圖表更新,舊元素仍然存在,并且圖表會創建新路徑而不洗掉舊路徑。每次單擊和更改都會增加瀏覽器中的 D3 元素,而不是替換它們。
由于 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>
uj5u.com熱心網友回復:
問題 1 通過以下方式解決:
let g = wrapper.selectAll("g.group") // add class of 'group' to the selector
否則選擇有一些歧義,因為您已經在前面的陳述句中添加了一個gto :wrapperlet ribbons = wrapper.append("g");
問題 2 似乎涉及更多。
首先,每次update呼叫g都會在 下創建一個新的svg并分配給wrapper。您可以在一次wrapper之外定義。update
其次,update使用兩者chord,chords前者在外部更新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://www.uj5u.com/shujuku/508886.html
