I have customised a donut chart for displaying with a grid layout. The grid has 3 different cards of same height in one row. But when i change the svg size manually from within the d3 component, the child component in the grid overflows the grid-area I have specified for it, which is manageable at bigger screen sizes. But on laptop screens the edges of the svg get cut off as the edges are outside the boundaries of the grid view port.
I want the graph to scale down/up depending on how much of the grid viewport is available. I have also added the attribute preserveAspectRatio to the svg but that is not very helpful as the component width is too much.
Expected Grid behaviour:

When the screen width is reduced even a little bit:

How do I make the d3 graph fit responsively within the grid?
EDIT: the code used;
grid.component.html
<div >
<!-- Grid 1 -->
<mat-card >
<mat-card-title-group>
<mat-card-title>
<div >Grid 1</div>
</mat-card-title>
</mat-card-title-group>
<mat-card-content >
<app-d3-donut ></app-d3-donut>
</mat-card-content>
</mat-card>
</div>
grid-component.scss
.card-grid {
display: grid;
grid-template-columns: repeat(6, 0.5fr);
grid-template-rows: repeat(8, 1fr);
height: calc(100% - 40px);
}
.card-grid-graph {
grid-area: 3 / 1 / 5 / 7;
justify-self: center;
align-self: center;
}
The component I am using is app-d3-donut to render the donut graphs in the card.
d3-donut.component.html
<div #donut></div>
d3-donut.component.scss
div{
width: 300px;
position: relative;
zoom: 140%;
}
d3-donut.component.ts
import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
import { D3Service } from '../services/d3.service';
@Component({
selector: 'app-d3-donut',
templateUrl: './d3-donut.component.html',
styleUrls: ['./d3-donut.component.scss']
})
export class D3DonutComponent implements AfterViewInit{
@Input('data') private data: SimpleDataModel[] = [
{ name: "Test String 1 25%", value: '25', color: '#254C66' },
{ name: `Test string 2 75%`, value: '75', color: '#49A0CC' },
];
legend: any;
private svg: any;
private colors: any;
@ViewChild('donut') figureElement: ElementRef;
private d3: any;
private margin = { top: 10, right: 0, bottom: 30, left: 0 };
private width = 700;
private height = 600;
private radius = Math.min(this.width, this.height) / 2 - this.margin.left;
graphGroup: any;
constructor(private d3Service: D3Service) {
this.d3 = d3Service.d3;
}
ngAfterViewInit(): void {
this.createSvg();
this.createColors(this.data);
this.createLegend();
this.drawChart();
}
private createSvg(): void {
this.d3
.select(this.figureElement.nativeElement)
.append('svg')
.attr('viewBox', `0 0 ${this.width} ${this.height}`)
.attr("preserveAspectRatio", "xMinYMin meet");
this.svg = this.d3
.select(this.figureElement.nativeElement)
.select('svg');
this.legend = this.svg
.append('g')
.attr('id','legend');
this.graphGroup = this.svg
.append('g')
.attr(
'transform',
'translate(' this.width / 2 ',' this.height / 2 ')'
);
}
private createLegend(): void {
//code for creating the vertical legend at the top
}
private createColors(data): void {
//code for generating random colors
}
private drawChart(): void {
const pie = this.d3
.pie()
.sort(null) // Do not sort group by size
.value(d => {
return d.value;
});
const data_ready = pie(this.data);
// The arc generator
const arc = this.d3
.arc()
.innerRadius(this.radius * 0.4)
.outerRadius(this.radius * 0.48);
// code for building pie chart
// code to add the lines between chart and labels:
// code to add the labels:
}
The svg when inspected after resizing page:

CodePudding user response:
D3 graphs are usually rendered as SVG. You could add some CSS to make them responsive:
svg {
height: auto;
max-width: 100%;
vertical-align: center;
}
Pretty much the same as for other images
CodePudding user response:
You have two options:
- On resize event, pass
widthandheightof your grid cell to the component (by now they are constants, 700 and 600 respectively) and removeviewBox:
d3.select(...).append('svg').attr('width', this.width).attr('height', this.height);
- Set SVG vidth and height as 100%, make
viewBoxconstant (for example0 0 100 100) and set the donut radius constant (relative to the viewbox):
d3.select(...).append('svg').attr('viewBox', '0 0 100 100')
CSS:
svg {
width: 100%;
height: 100%;
}
