沿着线图的迭代/链式过渡,具有离散点和延迟

时间:2017-05-14 13:15:50

标签: javascript select d3.js transition

我创建了一个jsfiddle here

我确实有一个图形 - 在这种情况下是一个正弦波 - 并且想沿着这条线移动一个圆圈(由点击事件触发),停在该图形上的某些x和y值对,然后继续前进到图表的最后一点再次跳转到第一点(理想情况下,这应该继续,直到我按下停止按钮)。

我目前的问题是圆圈只是水平移动而不是纵坐标方向,而且延迟只能看到一次(在一开始)。

相关代码就是这个(整个运行示例可以在上面的链接中找到):

创建圆圈:

// the circle I want to move along the graph
  var circle = svg.append("circle")
                .attr("id", "concindi")
                .attr("cx", x_scale(xval[0]))
                .attr("cy", y_scale(yval[0]))
                .attr("transform", "translate(" + (0) + "," + (-1 * padding + 15) + ")")
                .attr("r", 6)
                .style("fill", 'red');

移动过程:

var coordinates = d3.zip(xval, yval);

svg.select("#concindi").on("click", function() {

    coordinates.forEach(function(ci, indi){
      //console.log(ci[1] + ": " + indi);
      //console.log(coordinates[indi+1][1] + ": " + indi);
      if (indi < (coordinates.length - 1)){
        //console.log(coordinates[indi+1][1] + ": " + indi);
        console.log(coordinates[indi + 1][0]);
        console.log(coordinates[indi + 1][1]);
        d3.select("#concindi")
          .transition()
          .delay(2000)
          .duration(5000)
          .ease("linear")
          .attr("cx", x_scale(coordinates[indi + 1][0]))
          .attr("cy", y_scale(coordinates[indi + 1][1]));
      }

    });

我很确定我以错误的方式使用循环。我们的想法是从第一个x / y对开始,然后移动到下一个x / y对(需要5s),等待2s然后继续下一个,依此类推。目前,延迟仅在最初可见,然后才会水平移动。

如何正确完成?

1 个答案:

答案 0 :(得分:1)

为什么不使用Bostock的translateAlong功能?

function translateAlong(path) {
    var l = path.getTotalLength();
    return function(d, i, a) {
        return function(t) {
            var p = path.getPointAtLength(t * l);
            return "translate(" + p.x + "," + p.y + ")";
        };
    };
}

以下是演示:

// function to generate some data
function get_sin_val(value) {
  return 30 * Math.sin(value * 0.25) + 35;
}

var width = 400;
var height = 200;
var padding = 50;

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

var xrange_min = 0;
var xrange_max = 50;

var yrange_min = 0;
var yrange_max = 100;

var x_scale = d3.scale.linear()
  .domain([xrange_min, xrange_max])
  .range([padding, width - padding * 2]);

var y_scale = d3.scale.linear()
  .domain([yrange_min, yrange_max])
  .range([height - padding, padding]);

// create the data
var xval = d3.range(xrange_min, xrange_max, 1);
var yval = xval.map(get_sin_val);

// just for convenience
var coordinates = d3.zip(xval, yval);

//defining line graph
var lines = d3.svg.line()
  .x(function(d) {
    return x_scale(d[0]);
  })
  .y(function(d) {
    return y_scale(d[1]);
  })
  .interpolate("linear");

//draw graph
var sin_graph = svg.append("path")
  .attr("d", lines(coordinates))
  .attr("stroke", "blue")
  .attr("stroke-width", 2)
  .attr("fill", "none");

// the circle I want to move along the graph
var circle = svg.append("circle")
  .attr("id", "concindi")
  .attr("transform", "translate(" + (x_scale(xval[0])) + "," + (y_scale(yval[0])) + ")")
  .attr("r", 6)
  .style("fill", 'red');

svg.select("#concindi").on("click", function() {
  d3.select(this).transition()
    .duration(5000)
    .attrTween("transform", translateAlong(sin_graph.node()));

});

// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
  var l = path.getTotalLength();
  return function(d, i, a) {
    return function(t) {
      var p = path.getPointAtLength(t * l);
      return "translate(" + p.x + "," + p.y + ")";
    };
  };
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

你必须明白forEach几乎会瞬间循环到数组的末尾。因此,您现在无法使用您的方法将圆圈跳到另一个坐标(因此,不幸的是,您在这里是正确的:“我很确定我以错误的方式使用循环”)。

如果你想在一个点和另一个点之间添加2s等待时间,最好的想法是链接转换。这样的事情(我正在减少演示中的延迟和持续时间,所以我们可以更好地看到效果):

var counter = 0;
transit();
function transit() {
    counter++;
    d3.select(that).transition()
        .delay(500)
        .duration(500)
        .attr("transform", "translate(" + (x_scale(coordinates[counter][0])) 
            + "," + (y_scale(coordinates[counter][1])) + ")")
        .each("end", transit);
}

以下是演示:

// function to generate some data
function get_sin_val(value) {
  return 30 * Math.sin(value * 0.25) + 35;
}

var width = 400;
var height = 200;
var padding = 50;

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

var xrange_min = 0;
var xrange_max = 50;

var yrange_min = 0;
var yrange_max = 100;

var x_scale = d3.scale.linear()
  .domain([xrange_min, xrange_max])
  .range([padding, width - padding * 2]);

var y_scale = d3.scale.linear()
  .domain([yrange_min, yrange_max])
  .range([height - padding, padding]);

// create the data
var xval = d3.range(xrange_min, xrange_max, 1);
var yval = xval.map(get_sin_val);

// just for convenience
var coordinates = d3.zip(xval, yval);

//defining line graph
var lines = d3.svg.line()
  .x(function(d) {
    return x_scale(d[0]);
  })
  .y(function(d) {
    return y_scale(d[1]);
  })
  .interpolate("linear");

//draw graph
var sin_graph = svg.append("path")
  .attr("d", lines(coordinates))
  .attr("stroke", "blue")
  .attr("stroke-width", 2)
  .attr("fill", "none");

// the circle I want to move along the graph
var circle = svg.append("circle")
  .attr("id", "concindi")
  .attr("transform", "translate(" + (x_scale(xval[0])) + "," + (y_scale(yval[0])) + ")")
  .attr("r", 6)
  .style("fill", 'red');

svg.select("#concindi").on("click", function() {
  var counter = 0;
  var that = this;
transit();
  function transit() {
    counter++;
    d3.select(that).transition()
      .delay(500)
      .duration(500)
      .attr("transform", "translate(" + (x_scale(coordinates[counter][0])) + "," + (y_scale(coordinates[counter][1])) + ")")
      .each("end", transit);
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>