Как отобразить несколько строк на линейном графике d3?

#javascript #d3.js

#javascript #d3.js

Вопрос:

Я хочу нарисовать несколько строк на линейном графике d3 и преобразовать каждую строку анимацией. Вот рабочий пример, но с отображением 1 строки за раз.

https://www.d3-graph-gallery.com/graph/line_change_data.html

Я пытаюсь расширить его, чтобы показывать несколько строк одновременно. А затем при нажатии кнопки Dataset 2 отображается новый набор строк и выполняется переход к каждой из них. Моя попытка здесь

 // Create 2 datasets
var data1 = [
  [{
      ser1: 0.3,
      ser2: 4
    },
    {
      ser1: 2,
      ser2: 16
    },
    {
      ser1: 3,
      ser2: 8
    }
  ],
  [{
      ser1: 1,
      ser2: 7
    },
    {
      ser1: 4,
      ser2: 1
    },
    {
      ser1: 6,
      ser2: 8
    }
  ]
];

var data2 = [
  [{
      ser1: 4,
      ser2: 7
    },
    {
      ser1: 2,
      ser2: 5
    },
    {
      ser1: 2,
      ser2: 3
    }
  ],
  [{
      ser1: 1,
      ser2: 10
    },
    {
      ser1: 5,
      ser2: 8
    },
    {
      ser1: 7,
      ser2: 3
    }
  ]
];

// set the dimensions and margins of the graph
var margin = {
    top: 10,
    right: 30,
    bottom: 30,
    left: 50
  },
  width = 460 - margin.left - margin.right,
  height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .append("svg")
  .attr("width", width   margin.left   margin.right)
  .attr("height", height   margin.top   margin.bottom)
  .append("g")
  .attr("transform",
    "translate("   margin.left   ","   margin.top   ")");

// Initialise a X axis:
var x = d3.scaleLinear().range([0, width]);
var xAxis = d3.axisBottom().scale(x);
svg.append("g")
  .attr("transform", "translate(0,"   height   ")")
  .attr("class", "myXaxis")

// Initialize an Y axis
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y);
svg.append("g")
  .attr("class", "myYaxis")

// Create a function that takes a dataset as input and update the plot:
function update(data) {

  // Create the X axis:
  x.domain([0, d3.max(data, function(d) {
    return d3.max(d, function(d) {
      return d.ser1;
    });
  })]);
  svg.selectAll(".myXaxis").transition()
    .duration(3000)
    .call(xAxis);

  // create the Y axis
  y.domain([0, d3.max(data, function(d) {
    return d3.max(d, function(d) {
      return d.ser2;
    });
  })]);
  svg.selectAll(".myYaxis")
    .transition()
    .duration(3000)
    .call(yAxis);

  data.forEach(function(data, i) {

    // Create a update selection: bind to the new data
    var u = svg.selectAll(`.lineTest${i}`)
      .data([data], function(d) {
        return d.ser1
      });

    // Update the line
    u
      .enter()
      .append("path")
      .attr("class", `.lineTest${i}`)
      .merge(u)
      .transition()
      .duration(3000)
      .attr("d", d3.line()
        .x(function(d) {
          return x(d.ser1);
        })
        .y(function(d) {
          return y(d.ser2);
        }))
      .attr("fill", "none")
      .attr("stroke", "steelblue")
      .attr("stroke-width", 2.5);

  });
}

// At the beginning, I run the update function on the first dataset:
update(data1);  
 <script src="https://d3js.org/d3.v5.min.js"></script>

<!-- Add 2 buttons -->
<button onclick="update(data1)">Dataset 1</button>
<button onclick="update(data2)">Dataset 2</button>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>  

Но проблема в том, что когда я нажимаю кнопку Dataset 2, она рисует новые строки, старые строки все еще отображаются, и между ними нет перехода.

Кто-нибудь знает, что здесь не так?

Спасибо

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

1. Я думаю, что это работает так, как вы хотите здесь: jsfiddle.net/kqjcobah . Однако есть пара вещей: объединения данных позаботятся о рекурсии, поэтому нет необходимости forEach . Когда вы устанавливаете класс, не ставьте точку — ставьте точку только при выборе класса. Я также перенес функцию line в переменную. Надеюсь, это поможет!

Ответ №1:

Пара замечаний:

  1. Как отметил @Ryan Morton, просто передайте весь набор строк в d3, он позаботится обо всем остальном. Нет необходимости в forEach ;
  2. Вы использовали attr('class', '.lineTest') , но это дало ему класс, который начинался с точки. Это сделало невозможным выбор с помощью d3.select('.lineTest') . Удаление точки исправило это;
  3. Поскольку ваша ключевая функция выбирает d.ser1 , но d представляет собой массив объектов, ser1 не определено. Я обновил функцию ключа, чтобы вместо этого использовать индекс.

 // Create 2 datasets
var data1 = [
  [{
      ser1: 0.3,
      ser2: 4
    },
    {
      ser1: 2,
      ser2: 16
    },
    {
      ser1: 3,
      ser2: 8
    }
  ],
  [{
      ser1: 1,
      ser2: 7
    },
    {
      ser1: 4,
      ser2: 1
    },
    {
      ser1: 6,
      ser2: 8
    }
  ]
];

var data2 = [
  [{
      ser1: 4,
      ser2: 7
    },
    {
      ser1: 2,
      ser2: 5
    },
    {
      ser1: 2,
      ser2: 3
    }
  ],
  [{
      ser1: 1,
      ser2: 10
    },
    {
      ser1: 5,
      ser2: 8
    },
    {
      ser1: 7,
      ser2: 3
    }
  ]
];

// set the dimensions and margins of the graph
var margin = {
    top: 10,
    right: 30,
    bottom: 30,
    left: 50
  },
  width = 460 - margin.left - margin.right,
  height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .append("svg")
  .attr("width", width   margin.left   margin.right)
  .attr("height", height   margin.top   margin.bottom)
  .append("g")
  .attr("transform",
    "translate("   margin.left   ","   margin.top   ")");

// Initialise a X axis:
var x = d3.scaleLinear().range([0, width]);
var xAxis = d3.axisBottom().scale(x);
svg.append("g")
  .attr("transform", "translate(0,"   height   ")")
  .attr("class", "myXaxis")

// Initialize an Y axis
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y);
svg.append("g")
  .attr("class", "myYaxis")

// Create a function that takes a dataset as input and update the plot:
function update(data) {

  // Create the X axis:
  x.domain([0, d3.max(data, function(d) {
    return d3.max(d, function(d) {
      return d.ser1;
    });
  })]);
  svg.selectAll(".myXaxis").transition()
    .duration(3000)
    .call(xAxis);

  // create the Y axis
  y.domain([0, d3.max(data, function(d) {
    return d3.max(d, function(d) {
      return d.ser2;
    });
  })]);
  svg.selectAll(".myYaxis")
    .transition()
    .duration(3000)
    .call(yAxis);

  // Create a update selection: bind to the new data
  var lines = svg.selectAll(`.lineTest`)
    .data(data, function(d, i) {
      return i;
    });

  lines.exit()
    .remove();

  // Update the line
  lines.enter()
    .append("path")
    .attr("class", "lineTest")
    .merge(lines)
    .transition()
    .duration(3000)
    .attr("d", d3.line()
      .x(function(d) {
        return x(d.ser1);
      })
      .y(function(d) {
        return y(d.ser2);
      }))
    .attr("fill", "none")
    .attr("stroke", "steelblue")
    .attr("stroke-width", 2.5);
}

// At the beginning, I run the update function on the first dataset:
update(data1);  
 <script src="https://d3js.org/d3.v5.min.js"></script>

<!-- Add 2 buttons -->
<button onclick="update(data1)">Dataset 1</button>
<button onclick="update(data2)">Dataset 2</button>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>