Skip to content

Instantly share code, notes, and snippets.

@tmcw
Created November 15, 2012 16:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save tmcw/4079745 to your computer and use it in GitHub Desktop.
Save tmcw/4079745 to your computer and use it in GitHub Desktop.
One-way markers demo

SVG has line markers, but they have many failings. This example emulates markers-along-a-path by filling a textPath with the right number of glyphs.

The original version of this failed because Firefox does not support letter-spacing on SVG elements. The second version failed because it has a bug in getComputedTextLength. The third failed because it does not preserve space according to xml:space.

Hence this is not compatible with Firefox currently. It does work, imperfectly, in Opera & Chrome, though their kerning and interpretation of the getComputedTextLength API is much different than Chrome's.

<!DOCTYPE html>
<meta charset="utf-8">
<title>SVG Swarm</title>
<style>
svg {
position: absolute;
top: 0;
}
use {
fill:none;
stroke:#000;
stroke-width:2;
}
</style>
<div id="fps">FPS: <span>?</span></div>
<input type='checkbox' id='reverse' /><label for='reverse'>reverse</label>
<script src="http://d3js.org/d3.v2.min.js?2.9.1"></script>
<script>
var data = [];
for (var t = 0; t < 12; t += 0.1) {
data.push([
Math.cos(t * 0.5),
Math.sin(t)
]);
}
var width = 960,
height = 500;
var x = d3.scale.linear()
.domain([-2, 2])
.range([0, width]);
var y = d3.scale.linear()
.domain([-2, 2])
.range([0, height]);
var time0 = Date.now(),
time1;
var fps = d3.select("#fps span");
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var defs = svg.append('defs');
var path = defs.selectAll('path')
.data([data])
.enter()
.append('path').attr('id', 'p')
.attr("d", function(d) {
return 'M' + d.map(function(a) {
return [x(a[0]), y(a[1])];
}).join('L');
});
var p = svg.selectAll('use')
.data([data])
.enter()
.append("use").attr('xlink:href', '#p');
var arrow = svg.append('text');
var alength = arrow.text('► ►').node().getComputedTextLength() - arrow.text('►').node().getComputedTextLength();
arrow.remove();
var tp = svg.append("text").attr('dy', 7).append('textPath');
var fpsqueue = [];
var forward = true;
var z = 0.5;
var rev = d3.select('#reverse');
d3.timer(function() {
data = [];
var n = 0;
for (var t = 0; t < 12; t += 0.001, n++) {
data[n] = [
Math.cos(t * z),
Math.sin(t)
];
}
z += 0.002;
if (z > 4) z = 0.5;
if (rev.node().checked) data.reverse();
tp.attr('xlink:href', '#p').text(function() {
return (new Array(Math.floor(path.node().getTotalLength() / alength))).join('► ');
});
path.data([data]).attr("d", function(d) {
return 'M' + d.map(function(a) {
return [x(a[0]), y(a[1])];
}).join('L');
});
time1 = Date.now();
if (fpsqueue.length === 200) { fps.text(d3.mean(fpsqueue).toFixed(3)); fpsqueue = []; }
fpsqueue.push(Math.round(1000 / (time1 - time0)));
time0 = time1;
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment