Is it possible to resize a force layout in d3.js? For example, I have a layout defined by
var force = d3.layout.force()
.nodes(documents.nodes)
.linkDistance(0)
.friction(.2)
.size([width, height]);
force.start();
and I wish to resize it later in the project. Is this possible?
Shortly, on a resize event you should change the attributes of your svg object (it changes also the center of gravity) and resume force calculations:
window.onresize = function() {
width = window.innerWidth, height = window.innerHeight;
svg.attr('width', width).attr('height', height);
force.size([width, height]).resume();
};
An example for the d3 force resize is provided here.
According to the documentation, the .size() parameter of the force layout affects only the center of gravity and initial distribution of nodes, not the space available. You will have to enforce any constraints on that yourself, e.g. in the tick function:
force.on("tick", function() {
node.attr("cx", function(d) { return Math.min(maxX, Math.max(minX, d.x)); })
.attr("cy", function(d) { return Math.min(maxY, Math.max(minY, d.y)); });
}
Related
Take this timeless example of D3 zoom. Shortened code below:
var svg = d3.select("svg")
...;
var zoom = d3.zoom()
.scaleExtent([1, 40])
.translateExtent([[-100, -100], [width + 90, height + 100]])
.on("zoom", zoomed);
...
d3.select("button")
.on("click", resetted);
svg.call(zoom);
function zoomed() {
view.attr("transform", d3.event.transform);
gX.call(xAxis.scale(d3.event.transform.rescaleX(x)));
gY.call(yAxis.scale(d3.event.transform.rescaleY(y)));
}
function resetted() {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity);
}
In the D3 Zoom library one specifies the zoom behavior, then applies the zoom to the canvas or SVG target. How/where could I send a callback as a param to zoomed() without redefining the zoom behavior?
Since you asked...
How/where could I send a callback as a param?
... your question is dangerously too broad. But here is a possibility (out of many):
Suppose this simple callback function:
function callback(x){
console.log(x);
}
We can send it to the zoom function changing the listener. So, instead of:
.on("zoom", zoomed);
We can do:
.on("zoom", function(){
zoomed(callback)
});
And, in the zoom function, setting the callback argument. For instance, let's log the d3.event.transform:
function zoomed(fn) {
fn(d3.event.transform);
//rest of the code...
}
Here is a demo bl.ocks: http://blockbuilder.org/GerardoFurtado/38c77c024ba4cc42c86221117fed4164
I want to place a svg bar chart into a certain div and fail to get the calculated svg width if I set the attribute "width" as a percentage. I would like to stick to giving the size as a percentage to be able to calculate bar widths etc. from the svg size.
The div for the chart:
<div id="chart"></div>
The code for appending the SVG, place in a function before that. The function is called to put data in a bootrap modal:
var prodChart = d3.select("#chart");
var w = "100%";
var h = "100%";
// remove existing svg in div
prodChart.select("svg").remove();
var chartSVG = prodChart.append("svg")
.attr(":xmlns:svg", "http://www.w3.org/2000/svg")
.attr("id","prodchartsvg")
.style("width", w)
.style("height", h)
.attr("class", "thumbnail");
To get the calculated size in pixels, I already tried this:
$("#prodchartsvg").width(); // not working, returns 100
$("#chart").width(); // not working, returns null
prodchartsvg.node().getBBox(); // prodchartsvg.node is not a function
d3.select("#prodchartsvg").style("width"); // returns 100%
chartSVG.style("width"); // returns auto
d3.select("#prodchartsvg").node().getBoundingClientRect(); // returns 0 for all parameters
d3.select("#chart").node().getBoundingClientRect(); // returns 0 for all parameters
I realise some of the above functions I tried are unsuitable in this context (DOM, object, ...).
Can you give me an idea how to get the svg width in pixels instead of a percentage?
Thank you for the help!
As #nrabinowitz commented, your problem is most probably caused by the fact that your SVG is not visible in the screen at the moment when you attempt to retrieve its width.
Test case
In the following snippet, we compare the value returned by $("#prodchartsvg").width() for two svgs, one hidden and one displayed.
var prodChart = d3.select("#chart");
var w = "100%";
var h = "100%";
// remove existing svg in div
prodChart.select("svg").remove();
var hiddenSVG = prodChart.append("svg")
.attr(":xmlns:svg", "http://www.w3.org/2000/svg")
.attr("id","hiddenSVG")
.style('display', 'none') // not shown on screen
.style("width", w)
.style("height", h)
.attr("class", "thumbnail");
$('#result').html('hiddenSVG width is ' + $("#hiddenSVG").width())
var shownSVG = prodChart.append("svg")
.attr(":xmlns:svg", "http://www.w3.org/2000/svg")
.attr("id","shownSVG")
.style("width", w)
.style("height", h)
.attr("class", "thumbnail");
$('#result').append('<br>shownSVG width is ' + $("#shownSVG").width())
svg {background-color: #eee}
<div id='chart'></div>
<div id='result'></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
How to fix
Try one of these solutions:
make sure the chart is displayed on screen whenever you compute the width
make sure the DOM is loaded whenever you compute the width. Either move you script at the end of the html body, or wrap your code inside a jQuery DOM ready function...
I'm trying to create a world map using d3.geoChoroplethChart. My problem is that I can't stretch it to the full screen. I'm setting the width of the chart, and svg is picking up the setting, but the underlying g element does not. I assume it uses the projection together with the set width to calculate its size.
The projection I'm using
var projection = d3.geo.mercator()
.center([0, 55])
.scale(180);
Here is chart code
mapChart.width(2000)
.height(1000)
.dimension(countryDim)
.group(countryDimGroup)
.colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]))
.projection(projection)
.valueAccessor(function(d) { return d.value; })
.overlayGeoJson(africaJson.features, "Countries", function(d) {
return d.properties.admin.toUpperCase();
});
I use geoJSON from here http://geojson-maps.kyd.com.au/
Am I missing something?
As we can see, a div can be draggable, resizeable using javascript.
http://jqueryui.com/draggable/
svg is also a html element,
Is it possible that I use css+ jquery to drag, resize the svg.
Here, svg is the whole svg, not the children of svg elements.
I know if I can use group of svg element(g) to translate. But the svg is not changed, position is not changed.
I tried set left, top.
I also tried set x, y.
var svg = d3.select("body").append("svg")
.attr("left", 200)
.attr("top", 200);
.attr("width", 200)
.attr("height", 200);
var svg = d3.select("body").append("svg")
.attr("x", 200)
.attr("y", 200);
.attr("width", 200)
.attr("height", 200);
Is it possible to make it draggable and resizeable?
Div is an DOM element, it can be resizable, dragable.
svg is also a Dom element, can it be resizeable and dragable?
Although, I still has above question, thanks Alvin K: it solves my problem: http://jsfiddle.net/ynternet/bf3zck06/1/
thanks for your time.
A demo for scale and drag: http://plnkr.co/edit/sSXYpwjvVB8m1GVuE40A?p=preview
var svg = d3.select("#chart1").append('svg');
svg.attr("width", 200)
.attr("height", 200)
.attr("viewBox", "0 0 200 200");
var circle = svg.append('circle')
.attr({
r: 30,
cx: 100,
cy: 100
})
.style({
fill: "green",
stroke: "red"
});
var open = false;
circle.on('click', function(e) {
open = !open;
if (open) {
svg.attr("viewBox", "0 0 200 200");
} else {
svg.attr("viewBox", "0 0 400 400");
}
})
circle.on("mousemove", function() {
circle.attr("cx", d3.mouse(this)[0]);
circle.attr("cy", d3.mouse(this)[1]);
});
Yes, you should be able to work it just as other DOM.
If you are using d3, d3 is full capable for the resize and scale. no need to involve jQuery to make things more complicated. You could attach your code in jsbin or plunker
BTW, You need to get to know the different of d3selection.attr and d3selection.style.
attr is used for attribute pairs
style is used for css style.
In the browser war days, they are kind of mess up, but now, we have much better standard for this.
I have some text I append to an svg object with D3.js using append('text').
My code looks like this:
var countries = svg.append("svg:g")
.attr("id", "countries");
var stateTexts = svg.append('rect')
.attr('x', xstateText)
.attr('y', ystateText)
.attr('width', 'auto')
.attr('height', 'auto')
var stateText = svg.append('text')
.attr('x', xstateText)
.attr('y', ystateText)
.style("font-family", "Arial")
.style("font-size", "14px")
.style("font-weight", 'bold');
What I'd like is to put that text "inside" a rect which changes size based on the length of the text I append. The rect would have a stroke of 1px so as to give the appearance of a box.
How can I accomplish this? Obviously, width and height can't be set to auto (css properties). I need something else there that can work native to D3.
Edit: Confused by the downvote..
You can't do this automatically in SVG -- the dimensions of the text have to be computed and the rectangle added accordingly. Fortunately, this is not too difficult. The basic idea is illustrated in this function:
function mkBox(g, text) {
var dim = text.node().getBBox();
g.insert("rect", "text")
.attr("x", dim.x)
.attr("y", dim.y)
.attr("width", dim.width)
.attr("height", dim.height);
}
Given a container and a text element, compute the dimensions of the text element (the text must be set for this to work correctly) and add a rect to the container with those dimensions. If you want to get a bit fancier, you could add another argument that allows you to specify padding so that the text and the border are not immediately next to each other.
Complete demo here.