Масштабирование по оси D3 Y и переход по пути

#d3.js

#d3.js

Вопрос:

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

Конечная цель — отображать точки данных в реальном времени.

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

 var x = d3.time.scale()
    .domain([now - (n - 2) * duration, now - duration])
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0])
    .domain([0, 100])

var axisx = svg.append('g')
    .attr('class', 'x axis')
    .attr('transform', `translate(0, ${height})`)
    .call(x.axis = d3.svg.axis().scale(x).orient('bottom').tickPadding(10));

var axisy = svg.append('g')
    .attr('class', 'y axis')
    .attr('transform', `translate(${width}, 0)`)
    .call(y.axis = d3.svg.axis().scale(y).orient('right').tickPadding(10));
  

И вот такая строка:

 var line = d3.svg.line()
    .interpolate('basis')
    .x(function(d, i) { return x(now - (n - 1 - i) * duration); })
    .y(function(d, i) { return y(d); });
  

Я добавляю функцию перехода, подобную тому, о котором говорится в связанном сообщении, для перемещения точек по горизонтали с течением времени:

 (function tick() {

    transition = transition.each(function() {

        // Update the domain of the x-axis
        now = new Date();
        x.domain([now - (n - 2) * duration, now - duration]);

        // Push the new data point
        data.push(temperature);

        // Redraw the line
        svg.select('.line')
            .attr('d', line)
            .attr('transform', null);

        // Slide the x-axis left
        axisx.call(x.axis);

        // Slide the line left
        path.transition()
            .attr('transform', `translate(${x(now - (n - 1) * duration)})`);

        // Pop the old data point off the front
        data.shift();

    }).transition().each('start', tick);
})();
  

Пока все работает отлично, когда домен оси Y фиксирован. Я хотел динамически изменять размер домена и маркировку оси Y, поэтому я добавил следующее к функции перехода:

 y.domain([
    Math.min.apply(Math, data) - 10,
    Math.max.apply(Math, data)   10
])
axisy.call(y.axis);
  

И, хотя масштаб по оси y, кажется, настраивается правильно, возникает заметный эффект сбоя, когда линия перерисовывается каждый раз при вызове функции перехода (не между каждым тиком функции перехода):

Сбой при перерисовке диаграммы

Очевидно, проблема в том, что я не анимирую вертикальное перемещение линии между фазами. Итак, первый вопрос: есть ли простой способ в D3 сделать это?

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

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

 y.domain([temperature - 10, temperature   10]);
  

Где окно исправлено. Тогда я думаю, мне придется изменить переход, чтобы он имел атрибут:

 .attr('transform', `translate(${x(now - (n - 1) * duration)}, ${y(data[1] - data[0]}))`);
  

Я вижу, что это transform вызовет соответствующую y функцию line , но я не уверен, как мне следует переопределить ее. Я пытался:

 .y(function(d, i) { return y(d / i); });
  

Но, похоже, это не работает.

Как я могу заставить эти линейные переходы работать? Большое спасибо за вашу помощь, и я приношу извинения за такой подробный и длинный вопрос.

Ссылка на полную суть кода.

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

1. Привет, Джеймс, хороший пример. Вы исправили свою суть с тех пор, как опубликовали этот вопрос? Я скопировал-вставил ваш код и не сталкиваюсь с проблемой, которую вы описываете в своем вопросе. Убедитесь сами (извините за уродливый стиль css): bl.ocks.org/phvaillant/ff9135e0b03d76d67c368b5c7477f402

2. Спасибо за ответ! Думаю, я плохо объяснил, что происходит. Возьмите ту же самую суть, которую вы только что создали, и добавьте y.domain([Math.min.apply(Math, data) - 10, Math.max.apply(Math, data) 10]); axisy.call(y.axis); в цикл перехода / анимации, и вы увидите проблему, о которой я говорю. Суть, которую я предоставил, — это просто базовый код, поверх которого я пытаюсь различными способами анимировать линию по вертикали по мере изменения области оси y (которая сама по себе имеет плавный переход).

3. Я вижу, моя ошибка. Меня также интересует ответ, если кто-то его найдет (поддержал вопрос). Этот эффект сбоя сопротивляется всем моим попыткам.

Ответ №1:

РЕДАКТИРОВАТЬ: я понял, что моя старая методология во многих случаях была неверной, и удалил пример кода / вычислений. Я обновлю, когда смогу выяснить, что пошло не так…

Это классная проблема. Пример, на который вы ссылаетесь (Майк Босток), просто интерполирует перевод в направлении X, чтобы плавно сдвигать кривую влево по мере ее обновления. Однако в вашем примере вы также хотите изменить масштаб оси Y в режиме реального времени —

Если мы подумаем об этом, это означает, что в дополнение к сдвигу кривой влево (с помощью преобразования translate в направлении X), нам также нужно немного уменьшить ее (с помощью преобразования scale в направлении Y). В дополнение к масштабированию в направлении Y, его также нужно будет немного перевести, чтобы компенсировать новое положение оси y.

Все это можно сделать с помощью [преобразования матрицы SVG][1]. Вам просто нужно выяснить, каковы ваши константы (a, b, c, d, e, f). Они могут быть рассчитаны с использованием ваших старых и новых масштабов.

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

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

2. Переводы, казалось, работали правильно, но масштабирование было отключено и вызывало артефакты, похожие на то, что вы изначально видели. Я думаю, что мой исходный образец, похоже, работал, потому что он не масштабировал ось Y, а просто сдвигал ее вверх.