Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 svg components not resizable

I'm creating a app with Angular 4 and D3. When I create a svg component from inside of a component by injecting the component to another component, it shows the component, but resizing and moving functions aren't working (Even resize and move cursors aren't show on hovering). If I use the same component and navigate to the component with routing, resizing and moving functions work. Any idea why.

Following is the component I use.

import { Component, OnInit } from '@angular/core';
import * as d3 from 'd3';

@Component({
  selector: 'app-function-box',
  templateUrl: './function-box.component.html',
  styleUrls: ['./function-box.component.css']
})
export class FunctionBoxComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

  w = 500;
  h = 350;
  r = 120;

  width = 200;
  height = 100;
  dragbarw = 10;

  svg = d3.select("body").append("svg")
    .attr("width", this.w)
    .attr("height", this.h);

  newg = this.svg.append("g")
    .data([{x: 400, y: 100}]); //position

  dragrect = this.newg.append("rect")
    .attr("id", "active")
    .attr("x", (d) => { return d.x;  })
    .attr("y", (d) => { return d.y; })
    .attr("height", this.height)
    .attr("width", this.width)
    .attr("fill-opacity", .5)
    .attr("cursor", "move")
    .call(d3.drag()
      .on("drag", this.dragmove.bind(this)));

  dragbarleft = this.newg.append("rect")
    .attr("x", (d) => { return d.x - (this.dragbarw/2); })
    .attr("y", (d) => { return d.y + (this.dragbarw/2); })
    .attr("height", this.height - this.dragbarw)
    .attr("id", "dragleft")
    .attr("width", this.dragbarw)
    .attr("fill", "lightblue")
    .attr("fill-opacity", .5)
    .attr("cursor", "ew-resize")
    .call(d3.drag()
      .on("drag", this.ldragresize.bind(this)));

  dragbarright = this.newg.append("rect")
    .attr("x", (d) => { return d.x + this.width - (this.dragbarw/2); })
    .attr("y", (d) => { return d.y + (this.dragbarw/2); })
    .attr("id", "dragright")
    .attr("height", this.height - this.dragbarw)
    .attr("width", this.dragbarw)
    .attr("fill", "lightblue")
    .attr("fill-opacity", .5)
    .attr("cursor", "ew-resize")
    .call(d3.drag()
      .on("drag", this.rdragresize.bind(this)));

  dragbartop = this.newg.append("rect")
    .attr("x", (d) => { return d.x + (this.dragbarw/2);})
    .attr("y", (d) => { return d.y - (this.dragbarw/2); })
    .attr("height", this.dragbarw)
    .attr("id", "dragleft")
    .attr("width", this.width - this.dragbarw)
    .attr("fill", "lightgreen")
    .attr("fill-opacity", .5)
    .attr("cursor", "ns-resize")
    .call(d3.drag()
      .on("drag", this.tdragresize));

  dragbarbottom = this.newg.append("rect")
    .attr("x", (d) => { return d.x + (this.dragbarw/2); })
    .attr("y", (d) => { return d.y + this.height - (this.dragbarw/2); })
    .attr("id", "dragright")
    .attr("height", this.dragbarw)
    .attr("width", this.width - this.dragbarw)
    .attr("fill", "lightgreen")
    .attr("fill-opacity", .5)
    .attr("cursor", "ns-resize")
    .call(d3.drag()
      .on("drag", this.bdragresize.bind(this)));

  dragmove(d) {

    this.dragrect
      .attr("x", d.x = Math.max(0, Math.min(this.w - this.width, d3.event.x)));
    this.dragbarleft
      .attr("x", (d) => { return d.x - (this.dragbarw/2); });
    this.dragbarright
      .attr("x", (d) => { return d.x +this.width - (this.dragbarw/2); });
    this.dragbartop
      .attr("x", (d) => { return d.x + (this.dragbarw/2); });
    this.dragbarbottom
      .attr("x", (d) => { return d.x + (this.dragbarw/2); })

    this.dragrect
      .attr("y", d.y = Math.max(0, Math.min(this.h - this.height, d3.event.y)));
    this.dragbarleft
      .attr("y", (d) => { return d.y + (this.dragbarw/2); });
    this.dragbarright
      .attr("y", (d) => { return d.y + (this.dragbarw/2); });
    this.dragbartop
      .attr("y", (d) => { return d.y - (this.dragbarw/2); });
    this.dragbarbottom
      .attr("y", (d) => { return d.y + this.height - (this.dragbarw/2); });

  }

  ldragresize(d) {
    let oldx = d.x;
    //Max x on the right is x + width - dragbarw
    //Max x on the left is 0 - (dragbarw/2)
    d.x = Math.max(0, Math.min(d.x + this.width - (this.dragbarw / 2), d3.event.x));
    this.width = this.width + (oldx - d.x);
    this.dragbarleft
      .attr("x", (d) => { return d.x - (this.dragbarw / 2); });

    this.dragrect
      .attr("x", (d) => { return d.x; })
      .attr("width", this.width);

    this.dragbartop
      .attr("x", (d) => { return d.x + (this.dragbarw/2); })
      .attr("width", this.width - this.dragbarw)
    this.dragbarbottom
      .attr("x", (d) => { return d.x + (this.dragbarw/2); })
      .attr("width", this.width - this.dragbarw)
  };

  rdragresize(d) {
    //Max x on the left is x - width
    //Max x on the right is width of screen + (dragbarw/2)
    let dragx = Math.max(d.x + (this.dragbarw/2), Math.min(this.w, d.x + this.width + d3.event.dx));

    //recalculate width
    this.width = dragx - d.x;

    //move the right drag handle
    this.dragbarright
      .attr("x", (d) => { return dragx - (this.dragbarw/2) });

    //resize the drag rectangle
    //as we are only resizing from the right, the x coordinate does not need to change
    this.dragrect
      .attr("width", this.width);
    this.dragbartop
      .attr("width", this.width - this.dragbarw)
    this.dragbarbottom
      .attr("width", this.width - this.dragbarw)
  }

  tdragresize(d) {

    let oldy = d.y;
    //Max x on the right is x + width - dragbarw
    //Max x on the left is 0 - (dragbarw/2)
    d.y = Math.max(0, Math.min(d.y + this.height - (this.dragbarw / 2), d3.event.y));
    this.height = this.height + (oldy - d.y);
    this.dragbartop
      .attr("y", (d) => { return d.y - (this.dragbarw / 2); });

    this.dragrect
      .attr("y", (d) => { return d.y; })
      .attr("height", this.height);

    this.dragbarleft
      .attr("y", (d) => { return d.y + (this.dragbarw/2); })
      .attr("height", this.height - this.dragbarw);
    this.dragbarright
      .attr("y", (d) => { return d.y + (this.dragbarw/2); })
      .attr("height", this.height - this.dragbarw);

  }

  bdragresize(d) {
    //Max x on the left is x - width
    //Max x on the right is width of screen + (dragbarw/2)
    let dragy = Math.max(d.y + (this.dragbarw/2), Math.min(this.h, d.y + this.height + d3.event.dy));

    //recalculate width
    this.height = dragy - d.y;

    //move the right drag handle
    this.dragbarbottom
      .attr("y", (d) => { return dragy - (this.dragbarw/2) });

    //resize the drag rectangle
    //as we are only resizing from the right, the x coordinate does not need to change
    this.dragrect
      .attr("height", this.height);
    this.dragbarleft
      .attr("height", this.height - this.dragbarw);
    this.dragbarright
      .attr("height", this.height - this.dragbarw);
  }
}
like image 791
pahan Avatar asked Aug 19 '18 09:08

pahan


People also ask

How to use SVG elements in visualizations using D3?

And since SVG sits in the DOM, we can use attr () and append () just like we did for HTML elements. Let's learn about some of the most used SVG elements in visualizations and how to create and apply styling to them using D3 library. An SVG line element is represented by <line> tag.

Is it possible to resize the SVG charts?

The SVG is resized, but not the bars. To solve the previous problem, one solution is to calculate the properties of each element acording to the parent width and height: It works, but it is not the best solution. D3.js provides dedicated tools for resizing charts.

Why does my SVG element cover the full window on resize?

The question was about having an svg element that keeps covering the full window on resize, which in most cases means a different aspect ratio. Just a note regarding UX: For some use cases, treating your SVG-based d3 chart like an asset with a fixed aspect ratio isn't ideal.

How do I change the dimensions of an SVG element?

It is good to have them in variables so that you can change them at one place without having to go through the entire code in case you decide to change your SVG's dimensions. Next, we select the body element and append our SVG element to it and set SVG's width and height.


1 Answers

Answer to your question can be achieved using ViewChild in Angular to access a Child Component, Directive or DOM Element.

If given component is your child component then you can add some reference variable to your html code something like this (here #chartbar)

<div #chartbar class="someclass"></div>

Now you need to use ViewChild in your Child Component

import { Component, OnInit, ViewChild, ElementRef} from '@angular/core';  //Imported ViewChild, ElementRef
import * as d3 from 'd3';

@Component({
  selector: 'app-function-box',
  templateUrl: './function-box.component.html',
  styleUrls: ['./function-box.component.css']
})
export class FunctionBoxComponent implements OnInit {
  @ViewChild('chartbar') private chartContainer: ElementRef;  //Provider defined here

  constructor() { }

  ngOnInit() {
  }

  w = 500;
  h = 350;
  r = 120;

  const element = that.chartContainer.nativeElement;     //To use DOM element of html
  width = 200;
  height = 100;
  dragbarw = 10;

  svg = d3.select(element).append("svg")          //select DOM element instead of body
    .attr("width", this.w)
    .attr("height", this.h);

For resize You will get good options here.

like image 58
Abhijeet Abnave Avatar answered Nov 12 '22 17:11

Abhijeet Abnave