Home > Software design >  How do I make my d3 graph fit responsively within my grid?
How do I make my d3 graph fit responsively within my grid?

Time:01-06

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: Grid when screen size is large

When the screen width is reduced even a little bit: grid when screen width is reduced

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: Svg that gets rendered in the grid

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:

  1. On resize event, pass width and height of your grid cell to the component (by now they are constants, 700 and 600 respectively) and remove viewBox:
d3.select(...).append('svg').attr('width', this.width).attr('height', this.height);
  1. Set SVG vidth and height as 100%, make viewBox constant (for example 0 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%;
}
  •  Tags:  
  • Related