Обновляемая диаграмма аккордов - PullRequest
0 голосов
/ 21 ноября 2018

Я работаю над диаграммой аккордов D3 и хочу иметь функцию обновления.

Это код, который у меня есть на данный момент

// Genres, check de readme daar staat visueel hoe je dit uitleest. 
var data = [
  [9962, 1196, 94, 93, 18],
  [1196, 9102, 11, 343, 169],
  [94, 11, 7143, 138, 32],
  [93, 343, 138, 6440, 75],
  [18, 169, 32, 75, 4886]
]

var genres = ["Psychologischverhaal", "Thriller", "Detective", "Romantischverhaal", "Sciencefiction"]

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    outerRadius = Math.min(width, height) * 0.5 - 40,
    innerRadius = outerRadius - 30;

// Zet getallen naar 1K ipv 1000
var formatValue = d3.formatPrefix(",.0", 1e3);

var chord = d3.chord()
    .padAngle(0.05)
    .sortSubgroups(d3.ascending);

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

var ribbon = d3.ribbon()
    .radius(innerRadius);

var color = d3.scaleOrdinal()
    .range(["#ed0b0b", "#03aa24", "#f2ae04", "#1f03f1", "#e1ed04"]);

var g = svg.append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
    .datum(chord(data));

var group = g.append("g")
    .attr("class", "groups")
  .selectAll("g")
  .data(function(chords) { return chords.groups; })
  .enter().append("g");

group.append("path")
    .style("fill", function(d) { return color(d.index); })
    .style("stroke", function(d) { return d3.rgb(color(d.index)).darker(); })
    .attr("id", function(d, i) { return "group" + d.index; })
    .attr("d", arc)
    .on("mouseover", fade(.1)) 
    .on("mouseout", fade(1));

group.append("title").text(function(d) {
        return groupTip(d);
});

group.append("text")
        .attr("x", 6)
        .attr("dy", 15)
      .append("textPath")
        .attr("xlink:href", function(d) { return "#group" + d.index; })
        .text(function(chords, i){return genres[i];})
        .style("fill", "black");

var groupTick = group.selectAll(".group-tick")
  .data(function(d) { return groupTicks(d, 1e3); })
  .enter().append("g")
    .attr("class", "group-tick")
    .attr("transform", function(d) { return "rotate(" + (d.angle * 180 / Math.PI - 90) + ") translate(" + outerRadius + ",0)"; });

groupTick.append("line")
    .attr("x1", 1)
    .attr("y1", 0)
    .attr("x2", 5)
    .attr("y2", 0)
    .style("stroke", "#000")

groupTick
  .filter(function(d) { return d.value % 1e3 === 0; })
  .append("text")
    .attr("x", 8)
    .attr("dy", ".35em")
    .attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180) translate(-16)" : null; })
    .style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
    .text(function(d) { return formatValue(d.value); });

var ribbons = g.append("g")
    .attr("class", "ribbons")
  .selectAll("path")
  .data(function(chords) { return chords; })
  .enter().append("path")
    .attr("d", ribbon)
    .style("fill", function(d) { return color(d.target.index); })
    .style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })

ribbons.append("title").
    text(function(d){return chordTip(d);});


// Returns an array of tick angles and values for a given group and step.
function groupTicks(d, step) {
  var k = (d.endAngle - d.startAngle) / d.value;
  return d3.range(0, d.value, step).map(function(value) {
    return {value: value, angle: value * k + d.startAngle};
  });
}

function fade(opacity) {
  return function(d, i) {
    ribbons
        .filter(function(d) {
          return d.source.index != i && d.target.index != i;
        })
      .transition()
        .style("opacity", opacity);
  };
}

function chordTip(d){
  var j = d3.formatPrefix(",.0", 1e1)
     return "Aantal boeken met genres:\n"
        + genres[d.target.index] + " en " + genres[d.source.index] + ": " + j(d.source.value)
}

function groupTip(d) {
        var j = d3.formatPrefix(",.0", 1e1)
        return "Totaal aantal boeken met het genre " + genres[d.index] + ":\n" + j(d.value)
}

и

<body>
<h1>Boeken met enkele & dubbele genres</h1>
<button id="doubleGenre">Alleen dubbele genres</button>
<button id="reset">Reset</button>

<svg width="960" height="900"></svg>

<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="assets/index.js"></script>

Это результат, который я имею сейчас: enter image description here

Я хочу обновить аккорд, когда пользователь нажимает кнопку Alleen dubbele жанров , поэтому это выглядит так:

enter image description here

Поэтому я хочу самостоятельно удалить аккорды, чтобы данные выглядели следующим образом:

var data = [
  [1196, 94, 93, 18],
  [1196, 11, 343, 169],
  [94, 11, 138, 32],
  [93, 343, 138, 75],
  [18, 169, 32, 75]
]

И если пользователь нажимает кнопку Сброс Я хочувернитесь к исходному виду.Есть ли кто-нибудь, кто может мне помочь с этим?

1 Ответ

0 голосов
/ 21 ноября 2018

Вот ссылка на упрощенную версию вашего примера с переходами: https://stackblitz.com/edit/q53412789

Gif with animation

Если вы хотите удалить аккорды самостоятельно,вы должны заменить значения по диагонали на нули (не просто удалить их: матрица должна оставаться квадратной):

const data2 = [
  [0, 1196, 94, 93, 18],
  [1196, 0, 11, 343, 169],
  [94, 11, 0, 138, 32],
  [93, 343, 138, 0, 75],
  [18, 169, 32, 75, 0]
]

Как и предположил @ rioV8, первый шаг - обернуть код для рисования и обновления диаграммы в функцию(Я опустил код для галочек и меток для краткости, принцип для них тот же):

var g = svg.append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")

var ribbons = g.append("g");

function update(data) {
  const chords = chord(data);

  const ribbonsUpdate = ribbons.selectAll("path")
    .data(chords, ({source, target}) => source.index + '-' + target.index)

  const duration = 3000;

  ribbonsUpdate
    .transition()
      .duration(duration)
      .attr("d", ribbon)
      .style("fill", function(d) { return color(d.target.index); })
      .style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })

  ribbonsUpdate
    .enter()
      .append("path")
      .attr("opacity", 0)
      .attr("d", ribbon)
      .style("fill", function(d) { return color(d.target.index); })
      .style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })
      .transition()
        .duration(duration)
        .attr('opacity', 1)

  ribbonsUpdate
    .exit()
      .transition()
        .duration(duration)
        .attr("opacity", 0)
        .remove();
}

update(data1);

Я заменил комбинацию .datum(val) и .data(fun) в вашем коде на single .data(vals).Это не обязательно, но, по моему мнению, последнее более распространено в сообществе d3.

Второй аргумент .data - ключевая функция.Это гарантирует, что при переходе каждый путь отображается на себя в новых данных (количество аккордов не равно: мы удалили аккорды для себя).

Если вам не нужны анимированные переходы, код становится еще проще:

function simpleUpdate(data) {
  const chords = chord(data);

  const ribbonsUpdate = ribbons.selectAll("path")
    .data(chords, ({source, target}) => source.index + '-' + target.index)

  ribbonsUpdate
    .enter().append("path")
    .merge(ribbonsUpdate)
      .attr("d", ribbon)
      .style("fill", function(d) { return color(d.target.index); })
      .style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })

  ribbonsUpdate.exit().remove();
}
...