如何使用补间函数更新 d3.js 中的文本内容?

How do I update text content in d3.js using tween function?

我正在尝试通过单击更新我的文本元素以像键入它一样展开。当我单击文本时,它应该从数据数组中拉出下一个文本元素并逐个字符地打印直到完成,但是,没有任何反应。虽然没有错误被抛出。下面附上代码。

    var data = [
        "Hello",
        "World!",
        "What's up?"
       ];
    var i = 0;
    var body = d3.select("body");
    var element = body.append("svg");

   element.append("text")
       .data(data)
       .text(function(d) {return d})
       .attr("x", 150)
       .attr("y", 75)
       .attr("text-anchor", "middle")
       .attr("fill", "white")
           .on("click", function() {
               d3.select(this).transition()
                  .duration(5000)
                  .ease(d3.easeLinear)
                  .tween("text", function () {
                      var newText = data[i];
                      var textLength = newText.length;
                      return function (t) {
                          this.textContent = newText.slice(0, Math.round(t * textLength));
              };
          });

      //wrap around function for the data
      i = (i + 1) % data.length;
    });

更新的答案:

D3 version 5.8.0 引入了一个重要的变化:

Tween functions can now use this to refer to the current node.

因此,使用 D3 v5.8.0 或更高版本,您的代码可以正常工作


旧答案(适用于 v5.8.0 之前的版本):

问题出在最内层函数this的意思上

与大多数 D3 方法一样,this 是当前的 DOM 元素。 transition.tween 没有区别:

When the transition starts, the value function is evaluated for each selected element, in order, being passed the current datum d and index i, with the this context as the current DOM element. (emphasis mine)

但是,在该内部函数内部,this 只是 window.

最简单的解决方案是在外部函数中使用 var self = this

.tween("text", function() {
    var self = this;
    var newText = data[i];
    var textLength = newText.length;
    return function(t) {
        self.textContent = newText.slice(0, Math.round(t * textLength));
    };
});

这是您更新后的代码:

var data = [
  "Hello",
  "World!",
  "What's up?"
];
var i = 0;
var body = d3.select("body");
var element = body.append("svg");

element.append("text")
  .data(data)
  .text(function(d) {
    return d
  })
  .attr("x", 150)
  .attr("y", 75)
  .attr("text-anchor", "middle")
  .on("click", function() {
    d3.select(this).transition()
      .duration(5000)
      .ease(d3.easeLinear)
      .tween("text", function() {
        var self = this;
        var newText = data[i];
        var textLength = newText.length;
        return function(t) {
          self.textContent = newText.slice(0, Math.round(t * textLength));
        };
      });

    //wrap around function for the data
    i = (i + 1) % data.length;
  });
<script src="https://d3js.org/d3.v4.min.js"></script>