This example demonstrates how to use the getTotalLength and getPointAtLength methods on SVG path elements to interpolate a point along a Catmull–Rom spline.
A related technique is stroke dash interpolation.
forked from mbostock's block: Point-Along-Path Interpolation
forked from antonyross's block: Point-Along-Path Interpolation
forked from antonyross's block: Point-Along-Path Interpolation
forked from antonyross's block: Point-Along-Path Interpolation
forked from antonyross's block: Point-Along-Path Interpolation
forked from antonyross's block: Point-Along-Path Interpolation
forked from antonyross's block: Point-Along-Path Interpolation
forked from antonyross's block: Point-Along-Path Interpolation
forked from antonyross's block: Point-Along-Path Interpolation
forked from antonyross's block: Point-Along-Path Interpolation
xxxxxxxxxx
<meta charset="utf-8">
<body>
<style>
path {
fill: none;
stroke: #000;
stroke-width: 3px;
}
circle {
/*fill: steelblue; */
stroke: #fff;
stroke-width: 3px;
}
</style>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var points = [
[200, 200],
[500, 200],
[500, 400],
[200, 400],
];
var points2 = [
[100, 200],
[400, 200],
[400, 400],
[100, 400],
];
var w = 960;
var h = 500;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
// Text to show "stopwatch"
svg.append("text").text("hey").attr("x", 700).attr("y", 50)
/////////////// VECTOR ///////////////////////
class Vector
{
constructor(x,y)
{
this.x = x;
this.y = y;
}
static add(v1, v2)
{
return new Vector(v1.x + v2.x, v1.y + v2.y)
}
static subtract(v1, v2)
{
return new Vector(v1.x - v2.x, v1.y - v2.y)
}
copy()
{
return new Vector(this.x, this.y);
}
limit(max)
{
var magnitude = this.magnitude();
if(magnitude > max)
{
this.normalize();
this.multiply(max);
}
}
normalize()
{
var magnitude = this.magnitude();
this.x/= magnitude;
this.y/= magnitude;
}
multiply(n)
{
this.x *= n;
this.y *= n;
}
divide(n)
{
this.x /= n;
this.y /= n;
}
add(v)
{
this.x += v.x;
this.y += v.y;
}
magnitude()
{
return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
}
dot(v)
{
return (this.x * v.x) + (this.y * v.y);
}
static angleBetween(v1, v2)
{
dot_product = v1.dot(v2);
theta = Math.acos(dot_product/(v1.magnitude() + v2.magmitude()));
return theta;
}
}
/////////////// RUNNER ///////////////////////
class Runner
{
constructor(r, color, path, coordinates)
{
this.r = r;
this.color = color;
this.path = path;
this.location = new Vector(coordinates[0], coordinates[1]);
this.velocity = new Vector(0, 0);//Math.random() * 5;
this.acceleration = new Vector(-.001, .01);
this.maxspeed = 10;
this.maxforce = 10;
this.mass = 10;
}
get_random_color()
{
var colors = ["red", "green", "blue", "orange"]
var random_color = Math.floor(Math.random()*colors.length);
return colors[random_color];
}
checkEdges()
{
if(this.location.x > w)
{
this.location.x = 0;
}
else if(this.location.x < 0)
{
this.location.x = w;
}
if(this.location.y > h)
{
this.location.y = 0;
}
else if(this.location.y < 0)
{
this.location.y = h;
}
}
applyForce(force)
{
//force.divide(mass);
this.acceleration.add(force);
}
update()
{
this.velocity.add(this.acceleration);
this.velocity.limit(this.topspeed);
this.location.add(this.velocity);
// this.acceleration.multiply(0);
}
followPath(target)
{
var desired = Vector.subtract(target, location);
desired.normalize();
desired.multiply(maxspeed);
var steer = Vector.subtract(desired, location);
steer.limit(maxforce);
this.applyForce(steer);
}
}
///////////////////////////////////////////////
var dataset = [];
var path = svg.append("path")
.data([points])
.attr("d", d3.svg.line()
.tension(0) // Catmull–Rom
.interpolate("cardinal-closed"));
var path2 = svg.append("path")
.attr("d", "M70 60 C 70 180, 370 180, 370 60")
.attr({fill:"transparent", stroke:"black", "stroke-width":4});
d3.select("svg").on("click", function()
{
// get the mouse position coordinates
var coordinates = [0, 0];
coordinates = d3.mouse(this);
dataset.push(new Runner(15, "blue", 1, coordinates));
var circles = svg.selectAll("circle") //Select all circles
.data(dataset);
circles.enter()
.append("circle")
.attr({cx:0, cy:0, fill: function(d){return d.get_random_color()}})
.attr("r", function(d){return d.r})
//.transition()
//.duration(2000)
//.attr("transform", translator()); //"translate(" + points[0] + ")")
//.each("end", transition);
transition();
function transition()
{
circles.transition()
.duration(10000)
//.attrTween("transform", translateAlong(path.node(),path2.node()))
.attrTween("transform", mover())
.each("end", transition);
}
/*
// Returns an attrTween for translating along the specified path element.
function translateAlong(path1, path2)
{
var length_1 = path1.getTotalLength();
var length_2 = path2.getTotalLength();
return function(d, i, a)
{
return function(t)
{
var p1 = path1.getPointAtLength(t * length_1);
var p2 = path2.getPointAtLength(t * length_2);
svg.select("text").text(Math.floor(t * 100))
if (t < .3)
{
p = p2
}
else
{
p = p1
}
return "translate(" + p.x + "," + p.y + ")";
};
};
}
*/
function mover()
{
return function(d, i, a)
{
return function(t)
{
svg.select("text").text(Math.floor(t * 100))
d.checkEdges(); // make sure objects remain on screen; bounce back
d.update(); // update the object's position
// return the updated translated position
return "translate(" + d.location.x + "," + d.location.y + ")";
};
};
}
});
</script>
https://d3js.org/d3.v3.min.js