【问题标题】:How to set a specific duration to interpolate along a path one point at time?如何设置特定的持续时间以在某个时间点沿路径进行插值?
【发布时间】:2016-04-26 04:21:04
【问题描述】:

我正在尝试找出沿路径插入圆的最佳方法,就像 Mike Bostock 在此示例中所做的那样:http://bl.ocks.org/mbostock/1705868。但是,我不想像他那样设置一个过渡值,而是希望能够为每个点对点插值设置一个唯一的持续时间;例如,在 x 毫秒内将圆从节点 [0] 过渡到节点 [1],在 y 毫秒内从节点 [1] 过渡到节点 [2],等等。有没有办法做到这一点,而不会将路径分成一堆较小的单独路径并沿着它们连续过渡?限制因素似乎是 path.getTotalLength() - 有没有办法只获取路径子集的长度?

transition();

function transition() {
   circle.transition()
   .duration(10000)
   .attrTween("transform", translateAlong(path.node()))
   .each("end", transition);
}

// 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 + ")";
   };
};
}

【问题讨论】:

    标签: d3.js transition interpolation


    【解决方案1】:

    实际上有一种方法,但它太丑了(因为它需要初始的蛮力计算),解决方案涉及以下内容:

    首先你需要一个包含节点之间转换时间的数组,在我的例子中是times,例如第一个元素3000对应于从[480,200][580,400]的毫秒时间

    • 计算过渡时间的总和(在整个过渡期间需要)
    • 以毫秒为单位计算到达形成这条路径的每个点的线性时间,当两点之间的路径不是一条线时,这实际上很棘手,例如一条曲线,在我的示例中,我通过蛮力计算这些时间,这使它变得丑陋,如果有一种方法可以计算到达位于路径本身的某个点所需的路径长度,那就太棒了,不幸的是,这种方法没有'据我所知不存在
    • 最后,一旦您知道线性时间,您就必须计算正确的时间,就像它遵循 times 数组中的数字列表一样,例如

    假设到达第一个点的线性时间是 50 毫秒,而我们目前的时间是 t < 50ms,我们必须将这个介于 [0 毫秒,50 毫秒] 之间的值映射到 [0 毫秒] 范围内的某个位置, 3000ms] 由公式3000 * (t ms - 0ms) / (50ms - 0ms) 给出

    var points = [
      [480, 200],
      [580, 400],
      [680, 100],
      [780, 300],
      [180, 300],
      [280, 100],
      [380, 400]
    ];
    
    var times = [3000, 100, 5000, 100, 3000, 100, 1000]
    var totalTime = times.reduce(function (a, b) {return a + b}, 0)
    
    var svg = d3.select("body").append("svg")
        .attr("width", 960)
        .attr("height", 500);
    
    var path = svg.append("path")
        .data([points])
        .attr("d", d3.svg.line()
        .tension(0) // Catmull–Rom
        .interpolate("cardinal-closed"));
    
    svg.selectAll(".point")
        .data(points)
      .enter().append("circle")
        .attr("r", 4)
        .attr("transform", function(d) { return "translate(" + d + ")"; });
    
    var circle = svg.append("circle")
        .attr("r", 13)
        .attr("transform", "translate(" + points[0] + ")");
    
    function transition() {
      circle.transition()
          .duration(totalTime)
          .ease('linear')
          .attrTween("transform", translateAlong(path.node()))
          .each("end", transition);
    }
    
    // initial computation, linear time needed to reach a point
    var timeToReachPoint = []
    var pathLength = path.node().getTotalLength();
    var pointIndex = 0
    for (var t = 0; pointIndex < points.length && t <= 1; t += 0.0001) {
      var data = points[pointIndex]
      var point = path.node().getPointAtLength(t * pathLength)
      // if the distance to the point[i] is approximately less than 1 unit
      // make `t` the linear time needed to get to that point
      if (Math.sqrt(Math.pow(data[0] - point.x, 2) + Math.pow(data[1] - point.y, 2)) < 1) {
        timeToReachPoint.push(t);
        pointIndex += 1
      }
    }
    timeToReachPoint.push(1)
    
    function translateAlong(path) {
      return function(d, i, a) {
        return function(t) {
          // TODO: optimize
          var timeElapsed = t * totalTime     
          var acc = 0
          for (var it = 0; acc + times[it] < timeElapsed; it += 1) {
            acc += times[it]
          }
          var previousTime = timeToReachPoint[it]
          var diffWithNext = timeToReachPoint[it + 1] - timeToReachPoint[it]
          // range mapping
          var placeInDiff = diffWithNext * ((timeElapsed - acc) / times[it])     
          var p = path.getPointAtLength((previousTime + placeInDiff) * pathLength)
          return "translate(" + p.x + "," + p.y + ")"
        }
      }
    }
    
    transition();
    path {
      fill: none;
      stroke: #000;
      stroke-width: 3px;
    }
    
    circle {
      fill: steelblue;
      stroke: #fff;
      stroke-width: 3px;
    }
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"&gt;&lt;/script&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-12
      • 1970-01-01
      • 1970-01-01
      • 2018-10-18
      相关资源
      最近更新 更多