How to navigate a path, but only between two specified waypoints
This question is about d3 version 3.x and path movements.
Imagine that there is a path and a circle element, and I want the circle to go that path in transition, but only up to a certain percentage. I've asked this before and got a great answer from Gerardo Furtado: My previous question
However, one question in this regard remains for me, and since I'm a beginner, I haven't found any working solution yet:
How can I track this path, say, from a point at 25% to a point at 50%, and then from a point at 50% to a point at 60%?
These numbers are just examples, any percentage should be possible.
I need to avoid the fact that the movement of the path always starts from position 0, but instead I want to start the movement along the path from the current position to which the circle has already reached.
I hope I could make my question clear enough.
Thank you for your understanding and help.
source to share
Well, I have to agree with LeBeau and I see that you do that too. Since I answered your last question , I just needed to make some minor changes to the function. However, save his recommendations for next time: when asking a question, show us some code you tried, even if it doesn't work because it shows effort.
Let's get back to the question.
For this solution, I will wrap everything inside a named function move
that takes two arguments, a start position and an end position (both in percentages):
function move(initialPosition, finalPosition) {
Home position, as the name suggests, sets the starting position of the circle along the path. The math is this:
var start = path.node()
.getPointAtLength(path.node().getTotalLength() * initialPosition);
Then I slightly modified the function from my last answer to accept the start and end 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's a demo. Clicking the button invokes move
with 0.25
(start position) and 0.5
(end 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 โโgreater than 1. But you can try changing it if you need to do more than "one circle" in the path.
source to share