D3 обновляет каждый путь только тогда, когда выполняется select() вместо selectAll()

#d3.js #data-binding #data-visualization

#d3.js #привязка данных #визуализация данных

Вопрос:

Я пытаюсь обновить диаграмму аккордов в соответствии с изменениями в данных.

Я создал группы для каждого элемента и обновил привязку данных. Однако по какой-то причине он соответствующим образом обновляет данные, только когда я использую «select» вместо «selectAll», что для меня довольно странно, потому что каждый раз, когда я обновляю привязку данных, я использовал selectAlll только для обновления каждого связанного элемента.

Мой код выглядит следующим образом.

-Создание начальной диаграммы-

 var g = svg.selectAll('g.groups')
    .data(figureCalculation.groups)
    .join('g')
    .attr('class', (d, i) => { return `group ${nameArray[i]}` })




g.append('path')
    .attr('class', (d) => { return `arc ${d.value}` })
    .attr('d', arc)
    .style('fill', 'grey')
    .style('stroke', 'pink')
    .style("opacity", 0)
    .transition().duration(1000)
    .style("opacity", 0.8);



var chords = svg.append('g')
    .selectAll('path')
    .data(figureCalculation.chords)
    .join('path')
    .attr('class', 'chords')
    .attr('d', d3.ribbon().radius(innerRadius))
    .style('fill', 'green')
    .style('stroke', 'red')
 

-обновите привязку данных-

 setTimeout(updateData, 2500)

function updateData() {


    figureCalculation = d3.chord()
        .padAngle(0.05)
        .sortSubgroups(d3.descending)(matrix1)


    figureCalculation.chords = [];
    figureCalculation.forEach((d) => {
        figureCalculation.chords.push(d)
    })

    g.select('path').data(figureCalculation.groups)
        .join('path').attr('d', arc)
        .style('fill', 'grey')
        .style('stroke', 'pink')

    chords.select('path').data(figureCalculation.chords)
        .join('path')
        .attr('d', d3.ribbon().radius(innerRadius))
        .style('fill', 'green')
        .style('stroke', 'red')

}
 

Полный код находится по следующей ссылке.

https://codepen.io/jotnajoa/pen/qBaXKVW

Ответ №1:

Ваша SVG структура странно структурирована.

Сначала для ваших групп вы создаете a g с одним дочерним элементом path . Ваше обновление не работает, потому что вы выполняете a selectAll путей g только с одним дочерним элементом.

Тогда для ваших аккордов эта переменная уже является коллекцией path . Вы рассматриваете его так, как будто это g элемент, содержащий path .

Я бы переписал ваш код следующим образом:

 let margin = { top: 50, bottom: 50, left: 20, right: 20 }

let width = 600 - margin.left - margin.right;
let height = 600 - margin.top - margin.bottom;
let innerRadius = Math.min(width, height) * 0.4;
let outterRadius = innerRadius * 1.2
let svg = d3.select('#graph').append('svg')
    .attr('width', width   margin.left   margin.right)
    .attr('height', height   margin.top   margin.bottom)
    .append('g')
    .attr('transform', `translate(${width / 2   margin.left}, ${height / 2   margin.top})`)


var nameArray = ['A', 'B', 'C', 'D'];
var matrix = [
    [11975, 5871, 8916, 2868],
    [1951, 10048, 2060, 6171],
    [8010, 16145, 8090, 8045],
    [1013, 990, 940, 6907]
];

var matrix1 = [
    [175, 571, 916, 868],
    [1951, 1248, 2060, 5471],
    [8010, 14145, 4390, 4245],
    [1213, 990, 540, 1207]
];



let figureCalculation = d3.chord()
    .padAngle(0.05)
    .sortSubgroups(d3.descending)(matrix)


figureCalculation.chords = [];
figureCalculation.forEach((d) => {
    figureCalculation.chords.push(d)
})



var arc = d3.arc().innerRadius(innerRadius).outerRadius(outterRadius)

svg
    .append('g')
    .attr('class', 'groups')
    .selectAll('path')
    .data(figureCalculation.groups)
    .join('path')
    .attr('class', (d, i) => { return `group ${nameArray[i]}` })
    .attr('d', arc)
    .style('fill', 'grey')
    .style('stroke', 'pink')
    .style("opacity", 0)
    .transition().duration(1000)
    .style("opacity", 0.8);

svg
    .append('g')
    .attr('class', 'chords')
    .selectAll('path')
    .data(figureCalculation.chords)
    .join('path')
    .attr('class', 'chords')
    .attr('d', d3.ribbon().radius(innerRadius))
    .style('fill', 'green')
    .style('stroke', 'red')

function updateData() {

    figureCalculation = d3.chord()
        .padAngle(0.05)
        .sortSubgroups(d3.descending)(matrix1)

    figureCalculation.chords = [];
    figureCalculation.forEach((d) => {
        figureCalculation.chords.push(d)
    })

    svg.select('.groups')
        .selectAll('path').data(figureCalculation.groups)
        .join('path').attr('d', arc)
        .style('fill', 'grey')
        .style('stroke', 'pink')

    svg.select('.chords')
        .selectAll('path').data(figureCalculation.chords)
        .join('path')
        .attr('d', d3.ribbon().radius(innerRadius))
        .style('fill', 'green')
        .style('stroke', 'red')

} 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.3.1/d3.min.js"></script>
<button onclick="updateData()">Update Data</button>
<div id="graph"></div> 

Комментарии:

1. Большое спасибо за исправленный код. Да, я думаю, что ваш код очень прост для отслеживания. Я пытался понять код ‘Visual Cinammon’ visualcinnamon.com/portfolio/phone-brand-switching но я не мог понять, почему она определила группы таким образом. Возможно, мне следует придерживаться того, что вы предлагаете. Большое вам спасибо

Ответ №2:

Я думаю, вам вообще не следует выбирать updateData :

 g.data(figureCalculation.groups).join(...)
chords.data(figureCalculation.chords).join(...)
 

Комментарии:

1. Я думаю, что независимо от того, выбираю я для аккордов или нет, это работает, но, если я не выбираю arc, это не работает. Разве общий шаблон обновления не похож на выбор всего, связанного с данными, обновление данных и повторное рисование?