Последний добавленный элемент не анимируется - PullRequest
0 голосов
/ 08 февраля 2019

Я пытаюсь использовать библиотеку d3.js для анимации графика.В конце концов я буду получать данные асинхронно из веб-сокета, но для примера я просто использую комбинацию setInterval + getRandomInt.Мне нужно добавить новые данные к уже известным данным и соответствующим образом обновить график.Важным моментом является то, что я не хочу стирать старые данные - вся история должна отображаться на графике бесконечно.

Мне удалось анимировать график, но моя проблема в том, что последний добавленный элемент (правый конец графика) просто добавляется без анимации.Это выглядит странно, и я хотел бы это исправить.

Я, вероятно, должен отметить, что я хочу отображать самые последние данные, как только они доходят до браузера, поэтому скрыть последний элемент невозможно.

<html>
  <head>
    <title>Simple Line Graph using SVG and d3.js</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
  </head>
  <body>
    <script>
      const margins = [80, 80, 80, 80]; // margins
      const width = 1000 - margins[1] - margins[3]; // width
      const height = 400 - margins[0] - margins[2]; // height

      const svg = d3
        .select("body")
        .append("svg")
        .attr("width", width + margins[1] + margins[3])
        .attr("height", height + margins[0] + margins[2])
        .append("svg:g")
        .attr("transform", "translate(" + margins[3] + "," + margins[0] + ")");

      const data = [1, 5, 2, 13, 0];

      const x = d3.scale
        .linear()
        .domain([0, data.length])
        .range([0, width]);

      const y = d3.scale
        .linear()
        .domain([d3.min(data), d3.max(data)])
        .range([height, 0]);

      const lineFunction = d3.svg
        .line()
        .x((_, i) => x(i))
        .y(d => y(d))
        .interpolate("cardinal");

      svg
        .append("path")
        .attr("d", lineFunction(data))
        .attr("stroke-width", 5)
        .attr("stroke", "red")
        .attr("fill", "none");

      setInterval(() => {
        data.push(getRandomInt(1, 10));

        x.domain([0, data.length]);
        y.domain([d3.min(data), d3.max(data)]);

        svg
          .transition()
          .duration(750)
          .select("path")
          .attr("d", lineFunction(data));
      }, 1000);

      function getRandomInt(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min;
      }
    </script>
  </body>
</html>

1 Ответ

0 голосов
/ 08 февраля 2019

Самый простой способ решить эту проблему - возможно, дублировать последний узел, а затем перевести его туда, куда он должен идти.Однако, поскольку вы полагаетесь на индекс, вам также потребуется дополнительный параметр для x (иначе вы не можете иметь две точки перекрытия).

<html>
  <head>
    <title>Simple Line Graph using SVG and d3.js</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
  </head>
  <body>
    <script>
      const margins = [80, 80, 80, 80]; // margins
      const width = 1000 - margins[1] - margins[3]; // width
      const height = 400 - margins[0] - margins[2]; // height

      const svg = d3
        .select("body")
        .append("svg")
        .attr("width", width + margins[1] + margins[3])
        .attr("height", height + margins[0] + margins[2])
        .append("svg:g")
        .attr("transform", "translate(" + margins[3] + "," + margins[0] + ")");

      const data = [{
        y: 10,
        x: 0
      }];

      const x = d3.scale
        .linear()
        .domain([0, data.length])
        .range([0, width]);

      const y = d3.scale
        .linear()
        .domain([d3.min(data), d3.max(data)])
        .range([height, 0]);

      const lineFunction = d3.svg
        .line()
        .x((d, i) => x(i + d.x))
        .y(d => y(d.y))
        .interpolate("cardinal");

      svg
        .append("path")
        .attr("d", lineFunction(data))
        .attr("stroke-width", 5)
        .attr("stroke", "red")
        .attr("fill", "none");

      setInterval(() => {        
        data.push({
          y: data[data.length - 1].y,
          x: 0
        });   
        var path = svg
          .select("path");
        path
          .datum(data)
          .attr("d", lineFunction);    
        data[data.length - 1] = {
          y: getRandomInt(1,10),
          x: 1
        };
        x.domain([0, data.length]);
        y.domain([d3.min(data, function(d) { return d.y; }), 
                  d3.max(data, function(d) { return d.y; })]);
        
        path
          .datum(data)
          .transition()
          .duration(750)
          .attr("d", lineFunction);
      }, 1000);

      function getRandomInt(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min;
      }
    </script>
  </body>
</html>

Ну, это подход DYI.Я бы, наверное, вместо этого взглянул на такой плагин: https://github.com/pbeshai/d3-interpolate-path.

...