Hello all!
I'd like to announce and bring your attention to a d3 extension: d3-transform. It's a simple API wrapping the creation and composition of functions to set the "transform" SVG attribute. You can get the source here, and the README has some examples that I'll go over below:
https://github.com/trinary/d3-transform
I found myself doing a lot of clumsy string manipulation to set transform attributes nearly every time I used d3, so I wrote an initial implementation of a transform API that added translate(), rotate(), etc functions to d3.selection. However, there were a number of drawbacks to this approach. More work would be required to use these functions in transitions, composition and reuse wouldn't be possible, and so on.
After discussing it a bit in the #d3js IRC channel, Spiros Eliopoulos (https://twitter.com/seliopou, https://github.com/seliopou) wrote an implementation of the same idea as a separate object, d3.svg.transform.
Creating a transform function looks like this (from the README):
var transform = d3.svg.transform()
.translate(function(d) { return [20, d.size * 10] })
.rotate(40)
.scale(function(d) { return d.size + 2 });
One can then use the resulting function to set the transform attribute:
var svg = d3.select('svg.example1').selectAll('g')
.data([{ size: 5 }, { size: 10 }])
.enter()
.append('g')
.attr('transform', transform);
Resulting in two "g" elements with a transform attribute like you'd expect:
<svg>
<g transform="translate(20,50) rotate(40) scale(7)"></g>
<g transform="translate(20,100) rotate(40) scale(12)"></g>
</svg>
For unchanging values, you can just pass in the parameters of each transform operation as arguments to the corresponding function. For dynamic values, pass in a function that returns an array of parameters to the transform operation. You can reuse it, pass it around, and compose it with other d3.svg.transform objects by passing an existing transformation into the d3.svg.transform() function:
var transform1 = d3.svg.transform()
.translate(10,20);
var transform2 = d3.svg.transform(transform1)
.scale(function(d) { return [d.size];})
transform2 takes the existing operations from transform1 and appends a data-dependent scale operation.
This lets you keep different parts of a transformation separate and re-useable, and extend them to add further transformation operations.
I think this adds a nice interface to what is very frequently an awkward but necessary step in constructing an SVG visualization, so I'd love for people to try it out. Feedback and comments are much appreciated. If you spot something you want changed or added, open an issue on GitHub! I've also added it to NPM for convenience, but I'm an NPM newbie so let me know if that can be done better. Thanks!
https://github.com/trinary/d3-transform
https://npmjs.org/package/d3-transform
--Erik (https://twitter.com/trinary, https://github.com/trinary)