Я работаю над визуализацией D3, которая использует составную панель.Я пытаюсь добавить анимацию, чтобы читатель мог отсортировать все столбцы в порядке убывания значений из определенной вложенной панели, а выбранная вспомогательная панель перемещается ближе всего к оси.В конце концов я хотел бы иметь что-то вроде изображения.
Сортировка баров в целом работает нормально каждый раз, когда я вызываю мой метод сортировки, однако сортировка подпрограммы-бары, чтобы выбранная мера была ближе всего к оси, обновляется только один раз.Я понятия не имею, почему, но я огляделся и подумал, что это может быть связано с ключевой функцией, но я не уверен, как интегрировать это в макет стека.
Вот как это выглядит прямо сейчас - как вы можете видеть, фактический порядок уложенных столбцов изменяется только один раз.
Данные поступают из CSV-файла - вот мой код:
var dataset;
var causes = ["f_trans_rate", "f_violence_rate", "f_fireExp_rate", "f_fallSlipTrip_rate", "f_exposure_rate", "f_contact_rate", "f_allOther_rate"]
var firstCause = 0;
var margin = { top: 20, right: 20, bottom: 30, left: 300 };
var width = 1000;
var height = 10000;
svg = d3.select('body')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var y = d3.scaleBand().range([0, height]) // set y scale
var x = d3.scaleLinear().range([0, width]); // set x scale
// set the colors
var z = d3.scaleOrdinal().range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
// load the csv and create the chart
d3.csv("data/data.csv", function (data, i) {
// calculate rates per 100k
data.id = +i;
data.f_violence_rate = +data.f_violence / +data.totEmp * +100000
data.f_trans_rate = +data.f_trans / +data.totEmp * +100000
data.f_fireExp_rate = +data.f_fireExp / +data.totEmp * +100000
data.f_fallSlipTrip_rate = +data.f_fallSlipTrip / +data.totEmp * +100000
data.f_exposure_rate = +data.f_exposure / +data.totEmp * +100000
data.f_contact_rate = +data.f_contact / +data.totEmp * +100000
data.f_allOther_rate = +data.f_allOther / +data.totEmp * +100000
data.f_total_rate = +data.f_violence_rate + +data.f_trans_rate + +data.f_fireExp_rate +
+data.f_fallSlipTrip_rate + +data.f_exposure_rate + +data.f_contact_rate + +data.f_allOther_rate;
return data;
}, function (error, data) {
if (error) throw error;
data.sort((a, b) => d3.descending(a.f_total_rate, b.f_total_rate));
y.domain(data.map(function (d) { return d.occupation; }));
x.domain([0, d3.max(data, function (d) { return d.f_total_rate; })]).nice();
z.domain(causes);
dataset = data;
drawChart();
});
function order(data) {
order = [+0, +1, +2, +3, +4, +5, +6]
order.splice(firstCause, 1); // remove
order.unshift(firstCause) // put at front
return order;
}
function update(fCause) {
firstCause = fCause;
dataset = [...dataset];
var sortFn;
switch (firstCause){
case +0: {
sortFn = (a, b) => d3.descending(a.f_trans_rate, b.f_trans_rate);
break;
}
case +1: {
sortFn = (a, b) => d3.descending(a.f_violence_rate, b.f_violence_rate);
break;
}
case +2: {
sortFn = (a, b) => d3.descending(a.f_fireExp_rate, b.f_fireExp_rate);
break;
}
case +3: {
sortFn = (a, b) => d3.descending(a.f_fallSlipTrip_rate, b.f_fallSlipTrip_rate);
break;
}
case +4: {
sortFn = (a, b) => d3.descending(a.f_exposure_rate, b.f_exposure_rate);
break;
}
case +5: {
sortFn = (a, b) => d3.descending(a.f_contact_rate, b.f_contact_rate);
break;
}
case +6: {
sortFn = (a, b) => d3.descending(a.f_allOther_rate, b.f_allOther_rate);
break;
}
default: console.log('default')
}
const yCopy = y.domain(dataset.sort(sortFn).map(d => d.occupation)).copy();
// define what will do the transition
const t = d3.transition().duration(1000);
const t2 = d3.transition().duration(1000);
const groups = d3.selectAll("g.bar-group")
.data(d3.stack().keys(causes).order(order)(dataset, firstCause)
.attr("fill", function (d) { return z(d.key); });
const bars = groups.selectAll(".bar")
.data(d => d, d => d.data.occupation)
.sort((a, b) => yCopy(a.data.occupation) - yCopy(b.data.occupation))
// sort order of stack
t.selectAll("g.bar-group")
.selectAll(".bar")
.attr("x", function (d) {
return x(d[0]);
})
.delay(0)
// sort data y axis - needs seperate transition so bars move first
t2.selectAll("g.bar-group")
.selectAll(".bar")
.attr("y", function (d) { return yCopy(d.data.occupation) })
.delay(1000)
// sort label y axis
t.select(".axis.y")
.call(d3.axisLeft(y))
.selectAll("g")
.delay(1000)
g.selectAll("g.bar-group").transition()
.filter(function() {
return !this.classList.contains('bar')
})
.duration(200)
.attr("opacity", 175)
}
function sortBy(i){console.log(i); update(i)} // i is 0-6
function drawChart() {
console.log(dataset)
g.append("g")
.selectAll("g")
.data(d3.stack().keys(causes)
.order(d3.stackOrderAscending)(dataset))
.enter().append("g")
.classed("bar-group", true)
.attr("fill", function (d) { return z(d.key); })
.selectAll("rect")
.data(function (d) { return d; })
.enter().append("rect")
.classed("bar", true)
.attr("y", function (d) {
return y(d.data.occupation);
})
.attr("x", function (d) {
return x(d[0]);
})
.attr("width", function (d) {
return x(d[1]) - x(d[0]);
})
.attr("height", y.bandwidth())
}
Это немного длинно, но, по сути, диаграмма создается как столбчатая диаграмма, а затемМетод sort должен переставлять столбцы, вызывая update, но работает только один раз.
TIA для любой помощи или комментариев :)