第一次加載資料時,圖形繪制正確,但是當我加載不同的資料集時,圖形保持不變。
我使用按鈕在資料集之間切換。無論我單擊哪個按鈕,第一次單擊始終會正確繪制圖形。但是我無法在通過單擊另一個按鈕繪制圖形后更新圖形。非常感謝任何幫助,謝謝!
const dataA = [
{ population: 50, size: 100 },
{ population: 100, size: 100 },
];
const dataB = [
{ money: 4, currency: "usd" },
{ money: 10, currency: "eur" },
];
function drawChart(dataSet, prop) {
let width = 900;
let height = 200;
let x = d3.scale.ordinal().rangeRoundBands([0, width], 0.9);
let y = d3.scale
.linear()
.domain([dataSet[0][prop] - 39, dataSet[dataSet.length - 1][prop]])
.range([height, 0]);
let chart = d3.select("#chart").attr("width", width).attr("height", height);
let barWidth = width / dataSet.length;
let div = d3.select("body").append("div").attr("class", "tooltip");
let bar = chart
.selectAll("g")
.data(dataSet)
.enter()
.append("g")
.attr("transform", function (d, i) {
return "translate(" i * barWidth ",0)";
});
bar
.append("rect")
.attr("y", function (d) {
return y(d[prop]);
})
.attr("height", function (d) {
return height - y(d[prop]);
})
.attr("width", barWidth);
}
function drawDataA() {
drawChart(dataA, "population");
}
function drawDataB() {
drawChart(dataB, "money");
}
d3.select("#dataA").on("click", drawDataA);
d3.select("#dataB").on("click", drawDataB);
<!DOCTYPE html>
<html>
<meta charset="utf-8" />
<head>
<script src="https://d3js.org/d3.v4.js"></script>
</head>
<body>
<div id="app">
<svg class="chart" id="chart"></svg>
<button id="dataA">data1</button>
<button id="dataB">data2</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="./index.js"></script>
</body>
</html>
代碼筆:https ://codepen.io/rfripp2/pen/porpaLL
uj5u.com熱心網友回復:
這是預期的行為。讓我們看看你的代碼:
let bar = chart
.selectAll("g")
.data(dataSet)
.enter()
.append("g")
select all 陳述句選擇與選擇器匹配的所有現有元素,這些元素是選擇中元素的子元素chart。
data 方法將一個新的資料陣列系結到這個選擇。
enter 方法回傳一個新選擇,其中包含資料陣列中每個在選擇中沒有對應元素的專案的占位符。
append 方法為它被呼叫的選擇中的每個元素回傳一個新附加的子元素。
運行代碼
第一次呼叫 draw 函式時,您沒有g元素,因此選擇為空。您將資料系結到此空選擇。然后使用回車選擇。因為選擇中有兩個資料項并且沒有元素,所以 enter 包含兩個占位符/元素。然后使用 append 添加這些元素。
第二次呼叫 draw 函式時,您有兩個g元素,因此選擇中有兩個元素。您將資料系結到此選擇。然后使用回車選擇。因為您已經有兩個元素并且只有兩個資料點,所以輸入選擇為空。因此, append 不會創建任何新元素。
您可以通過使用 selection.size() 看到這一點:
顯示代碼片段
const dataA = [
{ population: 50, size: 100 },
{ population: 100, size: 100 },
];
const dataB = [
{ money: 4, currency: "usd" },
{ money: 10, currency: "eur" },
];
function drawChart(dataSet, prop) {
let width = 900;
let height = 200;
let x = d3.scale.ordinal().rangeRoundBands([0, width], 0.9);
let y = d3.scale
.linear()
.domain([dataSet[0][prop] - 39, dataSet[dataSet.length - 1][prop]])
.range([height, 0]);
let chart = d3.select("#chart").attr("width", width).attr("height", height);
let barWidth = width / dataSet.length;
let div = d3.select("body").append("div").attr("class", "tooltip");
let bar = chart
.selectAll("g")
.data(dataSet)
.enter()
.append("g")
.attr("transform", function (d, i) {
return "translate(" i * barWidth ",0)";
});
console.log("The enter selection contains: " bar.size() "elements")
bar
.append("rect")
.attr("y", function (d) {
return y(d[prop]);
})
.attr("height", function (d) {
return height - y(d[prop]);
})
.attr("width", barWidth);
}
function drawDataA() {
drawChart(dataA, "population");
}
function drawDataB() {
drawChart(dataB, "money");
}
d3.select("#dataA").on("click", drawDataA);
d3.select("#dataB").on("click", drawDataB);
<!DOCTYPE html>
<html>
<meta charset="utf-8" />
<head>
<script src="https://d3js.org/d3.v4.js"></script>
</head>
<body>
<div id="app">
<svg class="chart" id="chart"></svg>
<button id="dataA">data1</button>
<button id="dataB">data2</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="./index.js"></script>
</body>
</html>
解決方案
我們想同時使用更新和輸入選擇(如果資料集的大小發生變化,我們可能也需要退出選擇)。我們可以使用 .join() 來簡化這一點,join 移除退出選擇中的元素(沒有對應資料項的剩余元素),并回傳合并的進入選擇(剩余資料項的新元素)和更新選擇(預先存在的元素)。
在這里不需要將元素嵌套到父 g 和子 rect 中 - 并且需要額外的修改。通過直接定位條形,我們避免了對父 g 的需要:
顯示代碼片段
const dataA = [
{ population: 50, size: 100 },
{ population: 100, size: 100 },
];
const dataB = [
{ money: 4, currency: "usd" },
{ money: 10, currency: "eur" },
];
function drawChart(dataSet, prop) {
let width = 400;
let height = 200;
let x = d3.scaleBand().range([0, width], 0.9);
let y = d3.scaleLinear()
.domain([dataSet[0][prop] - 39, dataSet[dataSet.length - 1][prop]])
.range([height, 0]);
let chart = d3.select("#chart").attr("width", width).attr("height", height);
let barWidth = width / dataSet.length;
let div = d3.select("body").append("div").attr("class", "tooltip");
let bar = chart
.selectAll("rect")
.data(dataSet)
.join("rect")
.attr("transform", function (d, i) {
return "translate(" i * barWidth ",0)";
}).attr("y", function (d) {
return y(d[prop]);
})
.attr("height", function (d) {
return height - y(d[prop]);
})
.attr("width", barWidth);
}
function drawDataA() {
drawChart(dataA, "population");
}
function drawDataB() {
drawChart(dataB, "money");
}
d3.select("#dataA").on("click", drawDataA);
d3.select("#dataB").on("click", drawDataB);
<!DOCTYPE html>
<html>
<meta charset="utf-8" />
<head>
</head>
<body>
<div id="app">
<svg class="chart" id="chart"></svg>
<br />
<button id="dataA">data1</button>
<button id="dataB">data2</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>
<script src="./index.js"></script>
</body>
</html>
這需要從 v3 更新您的 D3 版本(您實際上正在使用兩個版本的 D3,v3 和 v4,它們都相當過時,并且都具有不同的方法名稱,實際上處理輸入選擇的方式不同)。
如果你想使用d3v4,那么join方法是不可用的,但是我們可以合并回車和更新:
當我說更新選擇時,我指的是初始選擇:
// update selection:
let bar = chart
.selectAll("rect")
.data(dataSet);
// enter selection:
bar.enter().append("rect")
顯示代碼片段
const dataA = [
{ population: 50, size: 100 },
{ population: 100, size: 100 },
];
const dataB = [
{ money: 4, currency: "usd" },
{ money: 10, currency: "eur" },
];
function drawChart(dataSet, prop) {
let width = 400;
let height = 200;
let x = d3.scaleBand().range([0, width], 0.9);
let y = d3.scaleLinear()
.domain([dataSet[0][prop] - 39, dataSet[dataSet.length - 1][prop]])
.range([height, 0]);
let chart = d3.select("#chart").attr("width", width).attr("height", height);
let barWidth = width / dataSet.length;
let div = d3.select("body").append("div").attr("class", "tooltip");
let bar = chart
.selectAll("rect")
.data(dataSet);
bar.enter().append("rect")
.merge(bar)
.attr("transform", function (d, i) {
return "translate(" i * barWidth ",0)";
}).attr("y", function (d) {
return y(d[prop]);
})
.attr("height", function (d) {
return height - y(d[prop]);
})
.attr("width", barWidth);
}
function drawDataA() {
drawChart(dataA, "population");
}
function drawDataB() {
drawChart(dataB, "money");
}
d3.select("#dataA").on("click", drawDataA);
d3.select("#dataB").on("click", drawDataB);
<!DOCTYPE html>
<html>
<meta charset="utf-8" />
<head>
</head>
<body>
<div id="app">
<svg class="chart" id="chart"></svg>
<br />
<button id="dataA">data1</button>
<button id="dataB">data2</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.1.0/d3.min.js"></script>
<script src="./index.js"></script>
</body>
</html>
最后,如果您希望保留 d3v3(我們已經在 v7 上),我們可以依賴更新的隱式合并并在輸入時輸入(修改更新選擇)。這個“魔法”在 v4 中被洗掉了,部分原因是它不明確。為此,我們需要打破您的方法鏈,以便bar包含
顯示代碼片段
const dataA = [
{ population: 50, size: 100 },
{ population: 100, size: 100 },
];
const dataB = [
{ money: 4, currency: "usd" },
{ money: 10, currency: "eur" },
];
function drawChart(dataSet, prop) {
let width = 400;
let height = 200;
let x = d3.scale.ordinal().rangeRoundBands([0, width], 0.9);
let y = d3.scale.linear()
.domain([dataSet[0][prop] - 39, dataSet[dataSet.length - 1][prop]])
.range([height, 0]);
let chart = d3.select("#chart").attr("width", width).attr("height", height);
let barWidth = width / dataSet.length;
let div = d3.select("body").append("div").attr("class", "tooltip");
// update selection:
let bar = chart
.selectAll("rect")
.data(dataSet);
// enter selection:
bar.enter().append("rect")
bar.attr("transform", function (d, i) {
return "translate(" i * barWidth ",0)";
}).attr("y", function (d) {
return y(d[prop]);
})
.attr("height", function (d) {
return height - y(d[prop]);
})
.attr("width", barWidth);
}
function drawDataA() {
drawChart(dataA, "population");
}
function drawDataB() {
drawChart(dataB, "money");
}
d3.select("#dataA").on("click", drawDataA);
d3.select("#dataB").on("click", drawDataB);
<!DOCTYPE html>
<html>
<meta charset="utf-8" />
<head>
</head>
<body>
<div id="app">
<svg class="chart" id="chart"></svg>
<br />
<button id="dataA">data1</button>
<button id="dataB">data2</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="./index.js"></script>
</body>
</html>
注意:d3v4 對破壞 v3 代碼的方法名稱進行了更改。這需要在使用 v4 和 7(分別使用合并和連接)的片段中對 d3.scale.linear / d3.scale.ordinal 進行更改。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/348476.html
標籤:d3.js
