Skip to content

Instantly share code, notes, and snippets.

@KoGor
Last active August 20, 2023 20:26
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KoGor/8163268 to your computer and use it in GitHub Desktop.
Save KoGor/8163268 to your computer and use it in GitHub Desktop.
Marker animation along SVG <path> element with D3.js III

Marker animation along SVG "path" element with D3.js: animating "path" and marker movement synchronously, marker rotate according to tangent line to path.

<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<head>
<title>SVG path animation</title>
<link href="style.css" rel="stylesheet">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/queue.v1.min.js"></script>
</head>
<body>
<!-- start -->
<div id="pathAnimation">
<script src="pathAdvancedFollow.js"></script>
<form>
<input type="checkbox" id="pathTrigger" name="visibility" value="hidden">Show/Hide path</br>
</form>
</div>
<!-- end -->
</body>
</html>
queue()
.defer(d3.xml, "wiggle.svg", "image/svg+xml")
.await(ready);
function ready(error, xml) {
//Adding our svg file to HTML document
var importedNode = document.importNode(xml.documentElement, true);
d3.select("#pathAnimation").node().appendChild(importedNode);
var svg = d3.select("svg");
var path = svg.select("path#wiggle"),
startPoint = pathStartPoint(path);
var checkbox = d3.selectAll("input[name=visibility]");
checkbox.on("change", function() {
if (this.checked) {
path.style("visibility", "visible");
this.value = "visible";
} else {
path.style("visibility", "hidden");
this.value = "hidden";
};
});
var marker = svg.append("image")
.attr("xlink:href", "rocket.png")
.attr("transform", "translate(" + startPoint[0] + "," + startPoint[1] + ")")
.attr("width", 48)
.attr("height", 24);
transition();
//Get path start point for placing marker
function pathStartPoint(path) {
var d = path.attr("d"),
dsplitted = d.split(" ");
return dsplitted[1].split(",");
};
function transition() {
marker.transition()
.duration(17000)
.attrTween("transform", translateAlong(path.node()))
.each("end", transition);// infinite loop
};
function translateAlong(path) {
var l = path.getTotalLength();
var t0 = 0;
return function(i) {
return function(t) {
var p0 = path.getPointAtLength(t0 * l);//previous point
var p = path.getPointAtLength(t * l);////current point
var angle = Math.atan2(p.y - p0.y, p.x - p0.x) * 180 / Math.PI;//angle for tangent
t0 = t;
//Shifting center to center of rocket
var centerX = p.x - 24,
centerY = p.y - 12;
return "translate(" + centerX + "," + centerY + ")rotate(" + angle + " 24" + " 12" +")";
}
}
}
}
path {
visibility: hidden;
fill: none;
stroke: #000;
stroke-width: 1px;
}
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@Azgaar
Copy link

Azgaar commented Mar 29, 2017

Отличное решение, спасибо. Попробую использовать его для анимации движения судна по карте. Не уверен, сколько это потребует ресурсов, судно предполагается не одно, а до сотни одновременно. Буду рад помощи.

@RomanTourdyiev
Copy link

Instead of incrementing t0 variable I would've use duration-derrived time unit like so:

let tUnit = 1/17000
var p0 = path.getPointAtLength((t - tUnit) * l);//previous point
var p = path.getPointAtLength(t * l);////current point

and, maybe, defined duration of 17000 as a global variable, so the complete code should look like this:

const duration = 17000;
...
function transition() {
    marker.transition()
        .duration(duration)
        .attrTween("transform", translateAlong(path.node()))
        .each("end", transition);// infinite loop
  };
...
function translateAlong(path) {
    var l = path.getTotalLength();
    const tUnit = 1/duration;
    return function(i) {
      return function(t) {
        var p0 = path.getPointAtLength((t - tUnit) * l);//previous point
        var p = path.getPointAtLength(t * l);////current point
        ...
      }
    }
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment