Reputation: 14309
I have a reports page containing three D3 JS v3 plots and some tabular data. The page looks great while the window is maximized but when the user resizes the browser windows it starts to behave oddly i.e. each plot overflows the containing div
etc.
After a bit of research I found that I just need to set the viewBox
attribute of the root svg for each plot i.e.
var margin = {top: 40, right: 20, bottom: 20, left: 20},
width = 450 - margin.left - margin.right,
height = 370 - margin.top - margin.bottom;
var chartSvg = d3.select("#myPlotDivId")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("viewBox", "0 0 " + (width + margin.left + margin.right) + " " + (height + margin.top + margin.bottom))
.attr("preserveAspectRatio", "xMidYMid meet")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
This I can do within the specific AngularJS controller and is no problem. However, I also need to update all three plot svg
s each time the window is resized. There is a nice JSFiddle demonstrating this:
var aspect = chartSvg.width() / chartSvg.height(),
container = chartSvg.parent();
$(window).on("resize", function() {
var targetWidth = container.width();
chartSvg.attr("width", targetWidth);
chartSvg.attr("height", Math.round(targetWidth / aspect));
}).trigger("resize");
However, I'd prefer to do this within the controller and the function from where I create the plots ... How can I do this? I would not like to have a Window event hook out in the page calling my controllers or creating a global directive just for this. Any ideas?
Upvotes: 0
Views: 812
Reputation: 14309
Finally I did it as follows i.e. injecting the window component into my controller and defining an onresize
event handler:
class ReportCtrl {
// 1) inject the window into my controller
constructor(private $window: IWindowService, ...) {
// etc ...
// 2) reusable function to resize a d3 js plot
var resizePlot = function (divId: string) {
var container = d3.select(divId);
var svg = container.select("svg");
var aspect = svg.attr("width") / svg.attr("height");
var targetWidth = container.node().getBoundingClientRect().width;
svg.attr("width", targetWidth);
svg.attr("height", Math.round(targetWidth / aspect));
};
// 3) hook on the window onresize event
$window.onresize = function (ev: UIEvent) {
resizePlot("#hist");
resizePlot("#scatter1");
resizePlot("#scatter2");
};
}
}
Upvotes: 1
Reputation: 9476
First of all, I hope you have some angular component/directive wrapping d3 code, if not - this is something you should do. Then this component should accept width and height:
app.component('myd3Plot', ...
width: '<',
height: '<'
And in parent controller/component you have listener for window resize and adjust child plots sizes:
window.on('resize', function() {
vm.child1.height = ...
vm.child2.height = ...
})
<myd3-plot height="$ctrl.child1.height"...
<myd3-plot height="$ctrl.child2.height"...
Upvotes: 2