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)


Би трябвало да можете да постигнете това, като използвате функция за междувременно движение във вашия преход. Функция tween ще се задейства при всеки тик на прехода и е един от начините за извикване на функция на всеки тик.

Методът tween се нуждае от име на атрибут (тъй като е предназначен да осигури персонализирана интерполация за атрибут), но това може да бъде фиктивен атрибут или такъв, който не се променя (както в моя пример по-долу). Документация за метода е тук.

В моя пример изтеглям свойството x (добре, свойството cx) на кръг, докато се движи по екрана, използвайки функция tween:

 .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 и какво друго прави компютърът ви. Обърнете внимание, че първото изминало време е 3ms: това е изминалото време от стартирането на таймера, а не откакто таймерът е бил планиран.

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

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