Skip to content

Instantly share code, notes, and snippets.

@KoGor
Last active August 20, 2023 20:26
Show Gist options
  • 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
Loading
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