I have a d3
generated force layout diagram, I need to export it to png
with the current (user-selected) scaling intact.
By my reasoning this will then grow the SVG
width and height, so if the svg
is 1920x1080
and it is 'zoomed' in, the exported svg
should probably have a much larger width and height to accommodate this.
I have tried everything, and I am missing something, I just can't seem to dynamically calculate the correct values for the output I require.
Here is an example of my exported SVG
, note that there is a lot more information, it is just not visible at that scale.
EDIT
Here is my basic exporting code, mostly adapted from highcharts:
serializeSvg: function() {
/**
* serialize a xml object to string
* @param {type} xmlNode the node to use
* @returns {String|@exp;xmlNode@pro;xml|@exp;window@pro;XMLSerializer@call;@call;serializeToString}
*/
function serializeXmlNode(xmlNode) {
if (typeof window.XMLSerializer !== 'undefined') {
return (new window.XMLSerializer()).serializeToString(xmlNode);
} else if (typeof xmlNode.xml !== 'undefined') {
return xmlNode.xml;
}
return '';
}
var svg = serializeXmlNode(document.getElementsByTagName('svg')[0]),
factor = 2;
svg = '<svg'
+ ' xmlns="http://www.w3.org/2000/svg"' // xml namespace
+ ' version="1.1"'
+ ' xmlns:xlink="http://www.w3.org/1999/xlink"' // for images
+ ' ' + svg.substring(svg.indexOf('<svg ') + 5);
// highcharts svg sanitizer
svg = svg.replace(/width="([^"]+)"/, function(m, width) {
return 'width="' + (width * factor) + '"';
}).replace(/height="([^"]+)"/, function(m, height) {
return 'height="' + (height * factor) + '"';
}).replace(/<rect class="drag"[^<]+<\/rect>/, '')
// IE specific
.replace(/<IMG /g, '<image ')
.replace(/height=([^" ]+)/g, 'height="$1"')
.replace(/width=([^" ]+)/g, 'width="$1"')
.replace(/id=([^" >]+)/g, 'id="$1"')
.replace(/class=([^" ]+)/g, 'class="$1"')
.replace(/ transform /g, ' ')
.replace(/:(path|rect)/g, '$1')
.replace(/style="([^"]+)"/g, function(s) {
return s.toLowerCase();
});
return svg;
}
and the main zoom/scaling startup for the d3 layout:
var layout = d3.layout.force();
var DEFAULT_SIZE = 64;
var GROWTH_SCALE = 1.15;
var SHRINK_SCALE = 1.05;
// creates a new force layout
var force = layout
.size([w, h])
.gravity(.06)
.distance(110)
//.friction(0.6)
//.linkStrength(0.4)
.charge(-((DEFAULT_SIZE * GROWTH_SCALE) * 10))
.on('tick', tick);
// creates the svg context
var svg = d3.select('.la-container').append('svg:svg')
.attr('width', w)
.attr('height', h)
.attr('pointer-events', 'all') // set for the pan/zooming
.append('svg:g') // add a g element for capturing zoom and pan
.call(d3.behavior.zoom().scaleExtent([0.6, 6.0]).on('zoom', redraw))
.append('svg:g');
svg.append('svg:rect')
.attr('class', 'drag')
.attr('width', w)
.attr('height', h)
.attr('fill', 'white');
So, in your d3 startup code, you call d3.behaviour.zoom.on()
to bind the "redraw" function to the "zoom" action. To get the scale and translation level, you want to capture the d3.scale
and d3.translate
values in this function, something like this (as you haven't posted the code for the redraw function):
var scale = 1;
var xtranslate=0;
var ytranslate=0;
var redraw = function(){
scale= d3.event.scale;
xtranslate=d3.event.translate[0];
ytranslate=d3.event.translate[1];
//your actual code to redraw stufff
}
That will get you the scaling and translation values from d3: then you need to apply the appropriate changes to the exported svg: I haven't actually done this, but presumably, you scale the "height" and "width" attributes appropriately (I think this would be just by multiplying the attribute values by the scale value, but I can't be sure).
Apply a view box and multiply it by scale value,
for example 1920x1080, when unzoomed then viewbox=0 0 1920 1080
at scale = 0.5 1920*0.5 1080*0.5 viewbox = 0 0 960 540
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With