Я работаю над визуализацией 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.info('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.info(i); update(i)} // i is 0-6
function drawChart() {
console.info(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())
}
Это немного длинновато, но по сути диаграмма создается как столбик с накоплением, а затем метод сортировки должен переставлять столбцы, вызывая update, но работает только один раз.
TIA за любую помощь или комментарии :)
это очень длинный набор данных, так что я мог легко видеть столбцы прямо сейчас - я добавил гифку, в которой он работает для вас, и ссылку на источник :)



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


основная проблема - это определение функции вашего заказа
function order(data) {
order = [+0, +1, +2, +3, +4, +5, +6]
order.splice(firstCause, 1); // remove
order.unshift(firstCause) // put at front
return order;
}
После 1 вызова вы переопределили order, он больше не является функцией. Передать это на stack.order(order) не получится.
Используйте новую переменную внутри функции, она может работать с var order, но это также добавляет путаницы.
function order(data) {
var orderNew = [+0, +1, +2, +3, +4, +5, +6]
orderNew.splice(firstCause, 1); // remove
orderNew.unshift(firstCause) // put at front
return orderNew;
}
В вашем наборе данных много дубликатов в столбце occupation. Почему? (единственная разница - пробелы)
большое спасибо! это всегда кажется чем-то таким маленьким, но теперь это имеет смысл. Все еще довольно новичок в D3. И да, его нужно немного очистить - это большая свалка, полученная из BLS, в которой много дублирования, где разные коды занятий имеют одно и то же название профессии. Еще раз спасибо!
у вас очень большой монитор, svg height = 10000, где пример csv?