I am trying to make my d3 multi-series line chart responsive follwoing this reference and here is the full code of this reference.
I add the following resize function to the bottom of my code:
$(window).on("resize", function() {
//update width
var main_width = parseInt(d3.select('#myChart').style('width'), 10);
main_width = main_width - main_margin.left - main_margin.right;
//resize the chart
main_x.range([0, main_width]);
mini_x.range([0, main_width]);
d3.select('#myChart').append("svg")
.attr("width", main_width + main_margin.left + main_margin.right)
.attr("height", main_height + main_margin.top + main_margin.bottom);
svg.selectAll('defs.clipPath.rect')
.attr("width", main_width);
svg.selectAll('rect.overlay')
.attr("width", main_width);
}).trigger("resize");
But nothing is changed when I adjust the screen. No idea why, please advise!
Thanks a lot.
Just detect all svg elements and attributes that have something to do with width main_width, because responsive mean the width will be adjusted when screen resize.
here is the resize() function that works well:
//----------- responsive d3, resize functionality -----------
$(window).on("resize", function() {
//update width
main_width = parseInt(d3.select('#myChart').style('width'), 10);
main_width = main_width - main_margin.left - main_margin.right;
//resize main and min time axis range
main_x.range([0, main_width]);
mini_x.range([0, main_width]);
//pointpoint the 'rect' element about brush functionality and adjust its width
svg.selectAll('rect.brushrect').attr("width", main_width);
//pinpoint the 'rect' element about mousemove and adjust its width
svg.selectAll('rect.overlay').attr("width", main_width);
//update x axis
svg.selectAll("g.x.axis").transition().call(main_xAxis);
//update right y axis
svg.selectAll('g.y.axisRight').attr("transform", "translate(" + main_width + ", 0)").call(main_yAxisRight);
// update main line0 and line4
svg.selectAll("path.line.line0").datum(data).transition().attr("d", main_line0);
svg.selectAll("path.line.line4").datum(data).transition().attr("d", main_line4);
//update min line0 and line4
mini.selectAll("path.mini_line0").datum(data).transition().attr("d", mini_line0);
mini.selectAll("path.mini_line4").datum(data).transition().attr("d", mini_line4);
}).trigger("resize");
Two things to note
The rect attribute with respect to brushing, i.e,
svg.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", main_width)
.attr("height", main_height);
does not have a name, so the following class attribute should be added for pinpointing:
.attr('class', "brushrect")
The class for mini lines are changed to mini_line0 and mini_line4, some the codes have some tiny update related to that.
then all done.
Related
I created a polar scatter plot using D3.js (based on this post) .
I would like to add the functionality to zoom and pan. I've seen examples for rectangular plots, but nothing for zooming/panning on circular plots.
I am just a beginner with using D3 so I'm a little lost. Can anyone help/offer suggestions?
I'm not entirely sure what your goal is, but I tried something below.
First you should add zoom behaviour. I used the r scale for both your x and y directions like:
var zoomBeh = d3.behavior.zoom()
.x(r)
.y(r)
.on("zoom", zoom);
And call the zoom behaviour into your svg:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.call(zoomBeh);
Finally you should make a zoom function.
function zoom() {
var t = svg.transition().duration(750);
svg.selectAll(".point").transition(t)
.attr("transform", function(d) {
var coors = line([d]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
})
}
Here is an updated fiddle. It's a little bit staggering, I'm not sure why yet.
I'm setting up a page with bootstrap. I have the layout working perfectly but one of the elements is a zoomable map of the US (using d3). The zoom function I am using requires the width and height of the div in pixels in order to calculate how to translate and scale the map. I have tried using percentages but I can't get anything going that way. Is there any way to dynamically get the height and width of the div. I have searched all over but the search terms are too generic (or I'm not clever enough to phrase it correctly).
Alternatively, how else might I get the necessary values.
Here is my implementation using hard coded width and height (which won't work if the page resizes).
//make the map element
var width = 1000;
var height = 1000;
var svg = d3.select("#Map")
.append("svg")
.attr("id", "chosvg")
.attr("height", height)
//.attr("viewBox", "0 0 600 600")
.attr("width", width)
.style("preserveAspectRatio", "true");
cbsa = svg.append("g");
d3.json("data/us-cbsa.json", function(json) {
cbsa.selectAll("path")
.attr("id", "cbsa")
.data(json.features)
.enter()
.append("path")
.attr("class", data ? quantize : null) //data ? value_if_true : value_if_false -- ternary operator
.attr("d", path)
.on("click", clicked);
});
in the clicked() function, I have the zoom like this which works, but only
with a certain window width
cbsa.transition()
.duration(750)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
.style("stroke-width", 1.5 / k + "px");
For clarity, I am ideally loooking for something like:
var width = column.width() //or something using percentages
I can include my html as well if it helps.
You can get the width of the column by calling:
var bb = document.querySelector ('#Map')
.getBoundingClientRect(),
width = bb.right - bb.left;
Depending on the browser, the bb might already have an width property. Keep in mind that the column might appear wider because the initial size of the svg is too big, so its parent column might bee, too.
https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
again I am struggling with d3.js. I have a working Line Chart and partially working mouseover. The goal is to limit the mouseover solely to the svg element, like Mark has it working in his answer Multiseries line chart with mouseover tooltip
I have created a Plunker with it. My is-situation is like that.
http://plnkr.co/edit/Jt5jZhnPQy4VpjwY3YBv?p=preview
And I have tried things like:
http://plnkr.co/edit/lRMfa0OiDWEXWYBAjoPd?p=preview
by adding:
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
But it's always pushing the circles and the bar out of the chart, I am fiddling for some days now and would be extremely glad if someone happens to point me in the right direction.
Thank you in advance.
Here is the plunker:
http://plnkr.co/edit/MEtbBqN5qr82yr0CNhUN?p=preview
I simply changed the size of your rectangle:
mouseG.append('rect')
.attr("x", margin.left)
.attr("y", margin.top)
.attr('width', w - margin.left - margin.right)
.attr('height', height - margin.bottom - margin.top)
PS: I don't know if you want the line limited to the chart area as well, but if you want, this is the plunker: http://plnkr.co/edit/RP4uYKBYnHtX1SvYsLKq?p=preview
Instead of giving width as width :
mouseG.append('svg:rect')
.attr('width', width)
do this (give the width of the group same as domain x for the line chart)
mouseG.append('svg:rect')
.attr('width', w - padding * 2)
Reason:
var xScale = d3.time.scale()
.domain([xExtents[0], xExtents[1]])
.range([padding, w - padding * 2]);
Your width of the x scale is w - padding * 2 so the width of the group listening to the mouse event should be same.
working code here
I'm trying to get a multi-line date based chart to pan nicely across the X date axis and I simply cannot figure out what the problem is.
I have the zoom behaviour set up in the code but it's just not performing as expected. If you click on a point in a line and scroll it appears to be scrolling the axis, if it click on the labels in the axis it also scrolls but the actual visualisation of data doesn't scroll.
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([1, 1])
.on("zoom", function () {
svg.select(".x.axis").call(xAxis);
svg.select(".lines").call(xAxis);
});
svg.call(zoom);
Also if you click directly on the back ground the mouse event doesn't seem to make it's way to the control at all.
I read a few examples on this and each seem to take a vastly different approach which I've tried but none have worked for my chart.
There are possibly a number of issues that exist as barriers to getting this working so I thought the best way to illustrate the problem was in a JsFiddle.
D3 Chart Panning Fiddle
What I'm trying to achieve is when there is a lot of data to visualise the chart can adapt to the data set and allow the data to extend beyond the bounds of the chart.
Currently clicking on the background does not allow panning because you have applied zoom behavior to the g element not to the svg.
var svg = d3.select('#host')
.data(plotData)
.append("svg")
.attr("id", "history-chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(zoom);
Right now on zoom you have updated x and y axes but not the visualization. So you have update the lines and circles also like this.
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([1, 1])
.on("zoom", function () {
svg.select(".x.axis").call(xAxis);
svg.select(".lines").call(xAxis);
svg.selectAll("path.lines")
.attr("d", function(d) { return line(d.values); });
svg.selectAll("circle")
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.value); });
});
Since you are panning the map you will have to use clip path for restricting visualization from moving outside the chart
var clip = svg.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
Apply clip path to the g elment which contains lines and cicrles.
var attribute = svg.selectAll(".attribute")
.data(plotData)
.enter().append("svg:g")
.attr("clip-path", "url(#clip)")
.attr("class", "attribute");
I am trying to use an svg-clippath with d3.js and the zoom behaviour.
The following code creates a rectangle, which will then be clipped by a rectangualar clipping region.
<svg class="chart"></svg>
<script>
var width = 800;
var height = 600;
var svg = d3.select(".chart")
.attr("width", width)
.attr("height", height)
.append("g");
var clip = svg.append("defs")
.append("clipPath")
.attr("id","clip")
.append("rect")
.attr("width",200)
.attr("height",200)
.attr("x",100)
.attr("y",100);
var zoom = d3.behavior.zoom().
on("zoom",zoomed);
function zoomed(){
container.attr("transform", "translate(" + d3.event.translate
+")scale(" + d3.event.scale + ")");
container.attr("clip-path","url(#clip)");
}
svg.call(zoom);
var container = svg.append("g")
.attr("clip-path","url(#clip)");
var rect = container.append("rect")
//.attr("clip-path","url(#clip)")
.attr("class","bar")
.attr("x",150)
.attr("y",150)
.attr("width",350)
.attr("height",350);
</script>
What I want is for the clipping to be applied again after zooming / moving (so that I cannot
move the rectangle outh of the clipping region, which right now i can do without any problems.) How do I do that?
I am assuming that the current behaviour is caused by the fact that the clipping is applied before the transformation.
I had the same problem and spent the last couple of hours trying to figure out a solution. Apparently, the clip-path operates on the object prior to transformation. So I tried to reverse-transform the clip object when performing the zoom transformation, and this worked !
It is something in the spirit of:
var clip_orig_x = 100, clip_orig_y = 100;
function zoomed() {
var t = d3.event.translate;
var s = d3.event.scale;
// standard zoom transformation:
container.attr("transform", "translate(" + t +")scale(" + s + ")");
// the trick: reverse transform the clip object!
clip.attr("transform", "scale(" + 1/s + ")")
.attr("x", clip_orig_x - t[0])
.attr("y", clip_orig_y - t[1]);
}
where clip is the rectangle in the clipPath. Because of interactions between zooming and translation, you need to set "x" and "y" explicitly instead of using transform.
I am sure experienced d3 programmers out there will come up with a better solution, but this works !