D3.js:在转换为 运行 时检索 x 轴信息

D3.js: Retrieve x-axis information while transition is running

我正在尝试使用 D3.js v4 创建时间轴。我已经成功地创建了带有轴和画笔的散点图,以允许用户定义特定时间段。

我想让用户能够 'play' 时间轴,就像 audio/video 播放器有一个指示器,该指示器会使用可自定义的持续时间从左向右移动。为了实现这一点,我放置了一条带有过渡的垂直线作为指示器。

我的问题是在转换 运行 时无法检索 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);

您应该能够在过渡中使用补间函数来完成此操作。补间函数将在过渡的每个刻度上触发,并且是每个刻度调用函数的一种方法。

tween 方法需要属性名称(因为它旨在为属性提供自定义插值),但这可以是虚拟属性,也可以是未更改的属性(如我下面的示例所示)。该方法的文档是 here

在我的示例中,我使用补间函数拉动圆在屏幕上移动时的 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>

答案很好,而且可能是传统 方法。

但是,出于好奇,这里有一个使用 d3.timer 的替代答案。

数学当然要复杂一些。另外,请记住经过的时间是 apparent:

The exact values may vary depending on your JavaScript runtime and what else your computer is doing. Note that the first elapsed time is 3ms: this is the elapsed time since the timer started, not since the timer was scheduled.

查看演示:

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>