2016-09-11 2 views
4

Ich zeichne Diagramme mit d3 in meiner Angular 2-Anwendung. Jetzt habe ich ein Liniendiagramm mit mehreren Reihen, also versuche ich, Werkzeugspitzen an jeder Linie hinzuzufügen, wenn ich seine vertikale Position schwebe.d3, Winkel 2: node.getBoundingClientRect ist keine Funktion

export class LineGraphDirective { 
    private host; 
    private svg; 
    private margin; 
    private width; 
    private height; 
    private xScale; // D3 scale in X 
    private yScale; // D3 scale in Y 
    private zScale; // D3 color scale 
    private xAxis; 
    private yAxis; 
    private line; 
    private htmlElement:HTMLElement; 
    private parseDate; 
    private ds; 

    constructor(private element:ElementRef) { 
    this.htmlElement = this.element.nativeElement; 
    this.host = d3.select(this.element.nativeElement); 
    this.parseDate = d3.timeParse('%Y-%m-%d'); 
    let data = []; 
    this.ngOnChanges(data); 
    } 

    /** 
    * Every time the @Input is updated, rebuild the chart 
    **/ 
    ngOnChanges(data):void { 
    this.setup(data); 
    this.initData(data); 
    this.buildSVG(); 
    this.scaleAxis(data); 
    this.populate(); 
    this.drawXAxis(); 
    this.drawYAxis(); 
    this.zoomEventHandler(); 
    this.addVerticalLineTooltip(); 
    } 

    private setup(data):void {} 

    private initData(data) {} 

    /** 
    * build SVG element using the configurations 
    **/ 
    private buildSVG():void {} 

    private scaleAxis(data) {} 

    /** 
    * Create x axis 
    **/ 
    private drawXAxis():void {} 

    /** 
    *create y axis 
    **/ 
    private drawYAxis():void {} 

    /** 
    * Populate the graphs 
    **/ 
    private populate():void {} 

    private addVerticalLineTooltip() { 
    // append a g for all the mouse over nonsense 
    let mouseG = this.svg.append("g") 
     .attr("class", "mouse-over-effects"); 

    // this is the vertical line 
    mouseG.append("path") 
     .attr("class", "mouse-line") 
     .style("stroke", "black") 
     .style("stroke-width", "1px") 
     .style("opacity", "0"); 

    // keep a reference to all our lines 
    let lines = d3.select('.line'); 

    // here's a g for each circle and text on the line 
    var mousePerLine = mouseG.selectAll('.mouse-per-line') 
     .data(this.ds) 
     .enter() 
     .append("g") 
     .attr("class", "mouse-per-line"); 

    // the circle 
    mousePerLine.append("circle") 
     .attr("r", 7) 
     .style("stroke", (d) => { 
     return this.zScale(d.name); 
     }) 
     .style("fill", "none") 
     .style("stroke-width", "1px") 
     .style("opacity", "0"); 

    // the text 
    mousePerLine.append("text") 
     .attr("transform", "translate(10,3)"); 

    // rect to capture mouse movements 
    mouseG.append('svg:rect') 
     .attr('width', this.width) 
     .attr('height', this.height) 
     .attr('fill', 'none') 
     .attr('pointer-events', 'all') 
     .on('mouseout',() => { // on mouse out hide line, circles and text 
     d3.select(".mouse-line") 
      .style("opacity", "0"); 
     d3.selectAll(".mouse-per-line circle") 
      .style("opacity", "0"); 
     d3.selectAll(".mouse-per-line text") 
      .style("opacity", "0"); 
     }) 
     .on('mouseover',() => { // on mouse in show line, circles and text 
     d3.select(".mouse-line") 
      .style("opacity", "1"); 
     d3.selectAll(".mouse-per-line circle") 
      .style("opacity", "1"); 
     d3.selectAll(".mouse-per-line text") 
      .style("opacity", "1"); 
     }) 
     .on('mousemove',() => { // mouse moving over canvas 
     let mouse = d3.mouse(this); // this is the line I am getting error 

     // move the vertical line 
     d3.select(".mouse-line") 
      .attr("d",() => { 
      let d = "M" + mouse[0] + "," + this.height; 
      d += " " + mouse[0] + "," + 0; 
      return d; 
      }); 

     // position the circle and text 
     d3.selectAll(".mouse-per-line") 
      .attr("transform", (d, i) => { 
      let beginning = 0, 
       end = d3.select(lines[i]).node().getTotalLength(), 
       target, 
       pos; 

      while (true) { 
       target = Math.floor((beginning + end)/2); 
       pos = d3.select(lines[i]).node().getPointAtLength(target); 
       if ((target === end || target === beginning) && pos.x !== mouse[0]) { 
       break; 
       } 
       if (pos.x > mouse[0])  end = target; 
       else if (pos.x < mouse[0]) beginning = target; 
       else break; //position found 
      } 

      // update the text with y value 
      d3.select(this).select('text') 
       .text(this.yScale.invert(pos.y).toFixed(2)); 

      // return position 
      return "translate(" + mouse[0] + "," + pos.y + ")"; 
      }); 
     }); 
    } 

    private zoomEventHandler() { 
    let zoom = d3.zoom() 
     .scaleExtent([1, 2]) 
     .translateExtent([[0, -100], this.width + 90, this.height + 100]).on("zoom",() => this.zoomed()); 
    this.svg.call(zoom); 
    } 

    private zoomed() { 

    } 
} 

Ich erhalte folgende Fehlermeldung auf der Browser-Konsole.

node.getBoundingClientRect is not a function 

Line: let mouse = d3.mouse(this); 

Irgendwelche Vorschläge?

Vielen Dank!

+0

Ihr Problem ähnelt diesem [Antwort] (https://stackoverflow.com/a/48231153/2414015). – jredd

Antwort

6

Ich denke, man sollte es verwenden:

let mouse = d3.mouse(mouseG); 

oder können Sie schreiben wie:

let mouse = d3.mouse(d3.event.currentTarget); 
+0

zweite Option funktioniert. Aber jetzt bekomme ich einen weiteren Fehler bei ** end = d3.select (lines [i]). Node(). GetTotalLength() ** as ** Kann die Eigenschaft 'getTotalLength' von null ** nicht lesen. Soll ich eine neue Frage posten oder diese aktualisieren? – Rose18

+0

Ich denke, Sie müssen eine minimale Plunker erstellen, um es zu reproduzieren – yurzui

+0

Ich musste verwenden ** let lines = document.getElementsByClassName ('line'); ** statt ** let lines = d3.select ('. Line') ; **. Jetzt bekomme ich eine vertikale Linie mit Kreisen in jeder Linie, aber nicht im Tooltip. Ich habe eine separate Frage geschrieben.http: //stackoverflow.com/questions/39438578/d3-tooltips-on-multi-series-line-chart-at-each-line-when-mouse-hover-event – Rose18

0

d3.mouse() muss auf einem DOM-Knoten aufgerufen werden. d3.mouse (this) funktioniert nicht, weil "this" eine Instanz Ihrer benutzerdefinierten Klasse ist. In Ihrem Fall können Sie

let mouse = d3.mouse(mouseG.node()); 

Sie rufen verwenden müssen node(), weil wiederum mouseG ein d3 Auswahlobjekt, kein DOM-Knoten. node() gibt den zugrunde liegenden "g" -Knoten zurück.

Verwandte Themen