D3沿路径段转换并在坐标值处暂停

时间:2017-11-23 06:14:11

标签: d3.js path jquery-animate geojson voronoi

我希望能够点击一个圆圈(坐标点);将标记带到圆圈的位置并暂停在圆圈的位置,然后沿路径再次恢复。

此外,我想在标记暂停时激活一个圆圈 - 点击它们(或点击它们的Voronoi单元格)。我的目的是最终为圆坐标的href点击函数。

我认为我需要将路径坐标的索引传递给translateAlong函数而不是时间变量,但无法解决如何执行此操作。

我不确定Voronoi细胞是否必要 - 我试图添加这个想法我可以暂停我的过渡并用Voronoi细胞激活我的圈子。在任何情况下,我都无法用Voronoi单元激活圆圈。

最近我在Stackoverflow d3 on click on circle pause and resume transition of marker along line上得到了很大的帮助 我希望再次获得帮助

<!DOCTYPE html>
<html lang="en">
    <head>
<meta charset="utf-8">
<title>basic_animateBetweenCircles</title>

<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
path {
  stroke: #848484;
    fill: none;
}
circle {
    fill: steelblue;
    stroke: steelblue;
    stroke-width: 3px;
}

.line {
    fill: none;
    stroke: #FE642E;
    stroke-width: 4;
    stroke-dasharray: 4px, 8px;
}
.point{
    fill:#DF013A;
}
</style>
</head>
<body>

<script>

var width = 960,
    height = 500;

var data = [
        [480, 200],
        [580, 400],
        [680, 100],
        [780, 300],
        [180, 300],
        [280, 100],
        [380, 400]
    ];

    //check index of path data
      for (var i = 0; i < data.length; i++) {
            var coordindex = i + " " + data[i];
            console.log("Coordindex: " + coordindex);
            //return coordindex;
      };

var duration = 20000;

var line = d3.line()
    .x(function(d) {return (d)[0];})
    .y(function(d) {return (d)[1];});

var voronoi = d3.voronoi()
  .extent([[0, 0], [width, height]]);

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

//path to animate - marker transitions along this path
var path = svg.append("path")
    .data([data])
    .attr("d", line)
    .attr('class', 'line')
    .attr("d", function(d) {
        return line(d)
    });

//voronoi
var voronoiPath = svg.append("g")
    .selectAll("path")
    .data(voronoi.polygons(data))
    .enter().append("path")
    .attr("d", polygon)
    .on("touchmove mousemove", function() {
        d3.select(this)
         .style("fill", "purple");
 });

//Want to activate circles when marker paused on them / in voronoi cell - intention is to have on click to href
 svg.selectAll("circle")
        .data(data)
    .enter()
        .append("circle")
        .attr("class", "point")
        .attr("r", 10)
    .attr("transform", function(d) { return "translate(" + d + ")"; })
        .on('click', function(d, i) {
        d3.select(this)
            .style("fill", "green");
        if (d3.active(this)) {
            marker.transition();
            setTimeout(function() {
                pauseValues.lastTime = pauseValues.currentTime;
                //console.log(pauseValues);
            }, 100);
        } else {
            transition();
        }
    });

var pauseValues = {
    lastTime: 0,
    currentTime: 0
};

//marker to transition along path
var marker = svg.append("circle")
    .attr("r", 19)
    .attr("transform", "translate(" + (data[0]) + ")")
    .on('click', function(d, i) {
        if (d3.active(this)) {
            marker.transition();
            setTimeout(function() {
                pauseValues.lastTime = pauseValues.currentTime;
                //console.log(pauseValues);
            }, 100);
        } else {
            transition();
        }
    });

function transition() {
    marker.transition()
        .duration(duration - (duration * pauseValues.lastTime))
        .attrTween("transform", translateAlong(path.node()))
        .on("end", function() {
            pauseValues = {
                lastTime: 0,
                currentTime: 0
            };
            transition()
        });
}

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

function polygon(d) {
  return "M" + d.join("L") + "Z";
}

</script>
</body>

1 个答案:

答案 0 :(得分:3)

如果你想在点暂停,我不会在整个路径上运行一次转换。相反,我会将其分解为N个过渡,从一个点移动到另一个点。在开始下一圈的圈子之前,你可以暂停一段时间。要做到这一点,我只想用一个小代数过渡每个线段:

// copy our data
transData = data.slice();

function transition() {
  marker.transition()
    .ease(d3.easeLinear)
    .duration(duration)
    .attrTween("transform", function(){

      // get our two points
      // slope between them
      // and intercetp
      var p0 = transData.shift(),
          p1 = transData[0];
          m = (p0[1] - p1[1]) / (p0[0] - p1[0]),
          b = p0[1] - (m * p0[0]),
          i = d3.interpolateNumber(p0[0], p1[0]);

        // move the point along the line
        return function(t){
          var x = i(t),
              y = m*x + b;
          return "translate(" + x + "," + y + ")";
        }
    })
    // one line segment is complete
    .on("end", function(){
      // if no more movements, stop
      if (transData.length <= 1) return;
      iter++;  
      // determine if this is a "pause"        
      setTimeout(transition, pausePoints.indexOf(iter) !== -1 ? pauseTime : 0);
    });

运行代码,点击一个点开始你可以暂停多个点:

&#13;
&#13;
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>basic_animateBetweenCircles</title>

  <script src="https://d3js.org/d3.v4.min.js"></script>
  <style>
    path {
      stroke: #848484;
      fill: none;
    }
    
    circle {
      fill: steelblue;
      stroke: steelblue;
      stroke-width: 3px;
    }
    
    .line {
      fill: none;
      stroke: #FE642E;
      stroke-width: 4;
      stroke-dasharray: 4px, 8px;
    }
    
    .point {
      fill: #DF013A;
    }
  </style>
</head>

<body>

  <script>
    var width = 960,
      height = 500;

    var data = [
      [480, 200],
      [580, 400],
      [680, 100],
      [780, 300],
      [180, 300],
      [280, 100],
      [380, 400]
    ];


    var duration = 20000/data.length,
        pauseTime = 2000;

    var line = d3.line()
      .x(function(d) {
        return (d)[0];
      })
      .y(function(d) {
        return (d)[1];
      });

    var voronoi = d3.voronoi()
      .extent([
        [0, 0],
        [width, height]
      ]);

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

    //path to animate - marker transitions along this path
    var path = svg.append("path")
      .data([data])
      .attr("d", line)
      .attr('class', 'line')
      .attr("d", function(d) {
        return line(d)
      });

    //voronoi
    var voronoiPath = svg.append("g")
      .selectAll("path")
      .data(voronoi.polygons(data))
      .enter().append("path")
      .attr("d", polygon);

    //Want to activate circles when marker paused on them / in voronoi cell - intention is to have on click to href
    svg.selectAll("circle")
      .data(data)
      .enter()
      .append("circle")
      .attr("class", "point")
      .attr("r", 10)
      .attr("transform", function(d) {
        return "translate(" + d + ")";
      })
      .on('click', function(d, i) {
        d3.select(this)
          .style("fill", "green");
        pausePoints.push(i);
        if (pausePoints.length === 1)
          transition();    
      });

    //marker to transition along path
    var marker = svg.append("circle")
      .attr("r", 19)
      .attr("transform", "translate(" + (data[0]) + ")");

    var pausePoints = [],
        iter = 0,
        transData = data.slice();
    
    function transition() {
      marker.transition()
        .ease(d3.easeLinear)
        .duration(duration)
        .attrTween("transform", function(){
          var p0 = transData.shift(),
              p1 = transData[0];
              m = (p0[1] - p1[1]) / (p0[0] - p1[0]),
              b = p0[1] - (m * p0[0]),
              i = d3.interpolateNumber(p0[0], p1[0]);
              
            return function(t){
              var x = i(t),
                  y = m*x + b;
              return "translate(" + x + "," + y + ")";
            }
        })
        .on("end", function(){
          if (transData.length <= 1) return;
          iter++;          
          setTimeout(transition, pausePoints.indexOf(iter) !== -1 ? pauseTime : 0);
        });
    }

    function polygon(d) {
      return "M" + d.join("L") + "Z";
    }
  </script>
</body>
&#13;
&#13;
&#13;