Marker animation along SVG "path" element with D3.js: animating "path" and marker movement synchronously, marker rotate according to tangent line to path.
Last active
August 20, 2023 20:26
-
-
Save KoGor/8163268 to your computer and use it in GitHub Desktop.
Marker animation along SVG <path> element with D3.js III
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" +")"; | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
path { | |
visibility: hidden; | |
fill: none; | |
stroke: #000; | |
stroke-width: 1px; | |
} |
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
Отличное решение, спасибо. Попробую использовать его для анимации движения судна по карте. Не уверен, сколько это потребует ресурсов, судно предполагается не одно, а до сотни одновременно. Буду рад помощи.