#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, а просто сдвигал ее вверх.