Можно ли анимировать выбор «ввода» для линейного графика?

У меня есть линейный график, который очень хорошо обновляется. Пока в вопрос не поступают новые данные. В этот момент выбор «обновить» анимируется, как и должно быть, но новые данные — нет.

Трудно объяснить, что происходит, но гифка стоит тысячи слов (смотрите, когда она переходит вниз):

введите здесь описание изображения

Я обернул график в класс с помощью метода обновления и рендеринга. Вот метод рендеринга, который я написал.

render(){
    // Update both Axes
    this.xAxis.transition()
        .call( d3.axisBottom( this.xScale ) );
    this.yAxis.transition()
        .call( this.createYAxis );

    // Exit
    this.line.exit().remove();

    // Enter
    this.line
      .enter().append( 'path' )
        .attr( 'class', 'valueLine' )
        .transition()
        .attr( 'd', this.valueLine );

    // Update
    this.line.transition().attr( 'd', this.valueLine );

    // Allows method chaining
    return this;
}

Почему «новые» данные не анимируются?


person Native Coder    schedule 18.11.2017    source источник


Ответы (1)


Короткий ответ

Да, но вы должны определить начальное значение для атрибута d.

Длинный ответ

Переход идет от начального значения к целевому значению. Прямо сейчас в вашем выборе «введите» у вас нет начального значения (то есть до transition()) для атрибута d:

// Enter
this.line
    .enter().append( 'path' )
    .attr( 'class', 'valueLine' )
    .transition()//no 'd' attribute before this line
    .attr( 'd', this.valueLine );

Следовательно, атрибут d для этого пути равен null (подробнее об этом читайте в этом ответе).

Давай увидим это:

var data = [10, 180, 30, 60, 190, 180, 50, 110, 40, 90, 90];
var svg = d3.select("svg");
var lineGenerator = d3.line()
  .y(function(d) {
    return d
  })
  .x(function(d, i) {
    return i * 60
  });
var path = svg.selectAll(null)
  .data([data])
  .enter()
  .append("path");
  
console.log(path.attr("d"))
path {
  fill: none;
  stroke-width: 2px;
  stroke: teal;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="600" height="200"></svg>

Вот тот же код, установка атрибута d:

var data = [10, 180, 30, 60, 190, 180, 50, 110, 40, 90, 90];
var svg = d3.select("svg");
var lineGenerator = d3.line()
  .y(function(d) {
    return d
  })
  .x(function(d, i) {
    return i * 60
  });
var path = svg.selectAll(null)
  .data([data])
  .enter()
  .append("path")
  .attr("d", lineGenerator);
path {
  fill: none;
  stroke-width: 2px;
  stroke: teal;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="600" height="200"></svg>

Вернемся к вашему вопросу:

Вы не можете перенести атрибут d с null на свою строку. Это невозможно. Однако есть некоторые возможные обходные пути.

Один из них — создание атрибута d по умолчанию перед переходом. Например:

var data = [10, 180, 30, 60, 190, 180, 50, 110, 40, 90, 90];
var svg = d3.select("svg");
var lineGenerator = d3.line()
  .y(function(d) {
    return d
  })
  .x(function(d, i) {
    return i * 60
  });
var path = svg.selectAll(null)
  .data([data])
  .enter()
  .append("path")
  .attr("d", "M0,100 L600,100")
  .transition()
  .duration(2000)
  .attr("d", lineGenerator);
path {
  fill: none;
  stroke-width: 2px;
  stroke: teal;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="600" height="200"></svg>

Но это ужасный переход! Проблема в том, что в начальном атрибуте d слишком мало точек. Из-за этого интерполятор строк не может создать желаемую интерполяцию.

Итак, мы можем использовать пользовательскую функцию, например эту от Майка Бостока. Вот демо:

var data = [10, 180, 30, 60, 190, 180, 50, 110, 40, 90, 90];
var svg = d3.select("svg");
var lineGenerator = d3.line()
  .y(function(d) {
    return d
  })
  .x(function(d, i) {
    return i * 60
  });
var path = svg.selectAll(null)
  .data([data])
  .enter()
  .append("path")
  .attr("d", "M0,100 L600,100")
  .transition()
  .duration(2000)
  .attrTween("d", function(d) {
    return pathTween(lineGenerator(d), 4, this)()
  });

function pathTween(d1, precision, self) {
  return function() {
    var path0 = self,
      path1 = path0.cloneNode(),
      n0 = path0.getTotalLength(),
      n1 = (path1.setAttribute("d", d1), path1).getTotalLength();

    // Uniform sampling of distance based on specified precision.
    var distances = [0],
      i = 0,
      dt = precision / Math.max(n0, n1);
    while ((i += dt) < 1) distances.push(i);
    distances.push(1);

    // Compute point-interpolators at each distance.
    var points = distances.map(function(t) {
      var p0 = path0.getPointAtLength(t * n0),
        p1 = path1.getPointAtLength(t * n1);
      return d3.interpolate([p0.x, p0.y], [p1.x, p1.y]);
    });

    return function(t) {
      return t < 1 ? "M" + points.map(function(p) {
        return p(t);
      }).join("L") : d1;
    };
  };
}
path {
  fill: none;
  stroke-width: 2px;
  stroke: teal;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="600" height="200"></svg>

person Gerardo Furtado    schedule 18.11.2017
comment
Возможно, я задал неправильный вопрос, потому что это не решает проблему. Я принял ваш ответ, потому что он действительно отвечает на вопрос, который я задал. Вы видите в GIF, как линия справа уже есть, когда происходит переход? И Он не двигается, левая сторона линии оживает вниз, чтобы встретить его? Я пытаюсь оживить «правую» сторону линии, чтобы каждая сторона встречалась друг с другом. - person Native Coder; 18.11.2017
comment
Кроме того, это все равно не будет работать в моем классе, потому что мне нужен контекст объекта для вызова this.lineGenerator - person Native Coder; 18.11.2017
comment
Знаете, какой вопрос я должен задать? Потому что я не совсем понимаю вопрос. - person Native Coder; 18.11.2017
comment
Не пытайтесь изменить выбор ввода: рассчитайте всю новую строку и сделайте переход в выборе обновления, используя функцию, которой я поделился. Однако для правильного ответа я советую вам создать работающий код, воспроизводящий ваш GIF, и опубликовать его как новый вопрос (потому что, как вы сказали, вы задали здесь неправильный вопрос). - person Gerardo Furtado; 19.11.2017