D3.js: получение информации об оси X во время выполнения перехода

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

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

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

Я пробовал все, включая игры с функциями tween и attrTween, но не смог заставить это работать. В идеале я хотел бы, чтобы индикатор начинался и останавливался в пределах кисти.

svg.append("g")
    .attr("class", "brush")
    .call(brush)
    .call(brush.move, x.range());

svg.append('line')
    .attr("class", "timeline-indicator")
    .attr("stroke-width", 2)
    .attr("stroke", "black")
    .attr("x1", 0)
    .attr("y1", 0)
    .attr("x2", 0)
    .attr("y2", height)
    .transition()
        .duration(9000)
        .attr("x1", 500)
        .attr("y1", 0)
        .attr("x2", 500)
        .attr("y2", height);

person user3389819    schedule 19.06.2017    source источник


Ответы (2)


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

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

В моем примере я извлекаю свойство x (ну, свойство cx) круга, когда он перемещается по экрану, используя функцию твина:

 .tween("attr.fill", function() {
        var node = this;
        return function(t) { 
         console.log(node.getAttribute("cx"));
        }
      })

Вот фрагмент работы:

var svg = d3.select("body").append("svg")
  .attr("width",400)
  .attr("height",400);
  
var circle = svg.append("circle")
  .attr("cx",20)
  .attr("cy",20)
  .attr("r",10);
 
circle.transition()
  .attr("cx",380)
  .attr("cy",20)
  .tween("attr.fill", function() {
    var node = this;
    return function(t) { 
     console.log(node.getAttribute("cx"));
    }
  })
  .duration(1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>

person Andrew Reid    schedule 19.06.2017
comment
Хороший ответ. Однако это: ... и, насколько мне известно, единственный способ вызывать функцию каждый тик неверно. Взгляните на d3.timer. - person Gerardo Furtado; 20.06.2017
comment
Большое спасибо. Это сработало! Мне просто нужно было заменить «cx» на «x1» и использовать scaleLinear для преобразования пикселей в мои значения по оси X. Я тоже посмотрю на d3.timer. - person user3389819; 20.06.2017
comment
@ user3389819 Я только что написал ответ, используя d3.timer. Однако придерживайтесь решения Эндрю: оно намного проще. - person Gerardo Furtado; 20.06.2017

Ответ Эндрю хороший и, вероятно, традиционный способ сделать это.

Однако, просто ради любопытства, вот альтернативный ответ с использованием d3.timer.

С математикой, конечно, немного сложнее. Кроме того, имейте в виду, что прошедшее время очевидно:

Точные значения могут различаться в зависимости от среды выполнения JavaScript и того, что еще делает ваш компьютер. Обратите внимание, что первое прошедшее время составляет 3 мс: это время, прошедшее с момента запуска таймера, а не с момента его запуска по расписанию.

Проверьте демо:

var svg = d3.select("body")
  .append("svg")
  .attr("width", 500)
  .attr("height", 100);

var circle = svg.append("circle")
  .attr("cx", 30)
  .attr("cy", 50)
  .attr("r", 20)
  .attr("fill", "tan")
  .attr("stroke", "dimgray");

var timer = d3.timer(move);

function move(t) {
  if (t > 2000) timer.stop();
  console.log("position now is: " + ~~(30 + (t / 2000) * 440))
  circle.attr("cx", 30 + (t / 2000) * 440)
}
.as-console-wrapper { max-height: 30% !important;}
<script src="https://d3js.org/d3.v4.min.js"></script>

person Gerardo Furtado    schedule 19.06.2017
comment
Ах, я пропустил эту реализацию при обновлении своего ответа, я кратко (не видя этого ответа) реализовал таймер с transition.on, сохраняя переход отдельно от функции таймера, которая выполняла фактическую проверку свойства cx круга. С тех пор я вырезал свою имплементацию d3.timer, так как это лучшее использование d3.timer. - person Andrew Reid; 20.06.2017