How to move along a path but only between two spec

2019-09-15 03:11发布

问题:

This question is about d3 version 3.x and path movements.

Imagine there is a path and a circle element, and I want the circle to follow that path in a transition, but only up to some percentage. I was asking this before and got a great answer from Gerardo Furtado here: My former question

Still, one question in this regards remains for me, and as I am a beginner, I couldn't find any working solution so far:

How can i trace this path, lets say, from a point at 25% to a point at 50%, and then later from a point at 50% to a point at 60%?

These numbers are just examples, any percentage value should be possible.

I need to avoid that the path movement always starts from position 0, but instead i want to start path movement from the current position the circle has reached already.

Hopefully I could express my question clear enough.

Thank you very much for any insight and help.

回答1:

Well, I have to agree with LeBeau, and I see that you also do. Since I answered your last question, I just needed to do some minor changes in the function. However, keep his advise in mind for the next times: when asking a question, show us some code you've tried, even if it doesn't work, because it shows effort.

Back to the question.

For this solution, I'll wrap everything inside a function named move, which accepts two arguments, the initial position and the final position (both in percentages):

function move(initialPosition, finalPosition) {

The initial position, as the name implies, set the initial position of the circle along the path. The math is this:

var start = path.node()
    .getPointAtLength(path.node().getTotalLength() * initialPosition);

Then, I slightly changed the function from my last answer to accept the initial and final positions:

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

Here is the demo. Clicking on the button calls move with 0.25 (initial position) and 0.5 (final position) as arguments:

var points = [
  [240, 100],
  [290, 200],
  [340, 50],
  [390, 150],
  [90, 150],
  [140, 50],
  [190, 200]
];

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

var path = svg.append("path")
  .data([points])
  .attr("d", d3.svg.line()
    .tension(0) // Catmull–Rom
    .interpolate("cardinal-closed"));

var color = d3.scale.category10();

var dataPositions = [{
  initial: 0.25,
  final: 0.5
}, {
  initial: 0.5,
  final: 0.6
}];


svg.selectAll(".point")
  .data(points)
  .enter().append("circle")
  .attr("r", 4)
  .attr("transform", function(d) {
    return "translate(" + d + ")";
  });

d3.select("button").on("click", function() {
  move(0.25, 0.5);
});

function move(initialPosition, finalPosition) {

  var start = path.node().getPointAtLength(path.node().getTotalLength() * initialPosition);

  var circle = svg.append("circle")
    .attr("r", 13)
    .attr("fill", function(d, i) {
      return color(i)
    })
    .attr("transform", "translate(" + start.x + "," + start.y + ")");

  circle.transition()
    .duration(1000)
    .attrTween("transform", function() {
      return translateAlong(path.node())()
    });

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

}
path {
  fill: none;
  stroke: #000;
  stroke-width: 3px;
}

circle {
  stroke: #fff;
  stroke-width: 3px;
}
<script src="//d3js.org/d3.v3.min.js"></script>
<button>Move</button>
<br>

PS: The function in this answer does not accept values bigger than 1. But you can try to change it if you need to do more than "one lap" in the path.