d3,Angular 2:node.getBoundingClientRect 不是函数

Posted

技术标签:

【中文标题】d3,Angular 2:node.getBoundingClientRect 不是函数【英文标题】:d3, Angular 2: node.getBoundingClientRect is not a function 【发布时间】:2017-01-19 04:00:48 【问题描述】:

我在 Angular 2 应用程序中使用 d3 绘制图表。现在我有一个多系列折线图,所以当悬停其垂直位置时,我试图在每条线上添加工具提示。

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() 

  

我在浏览器控制台上收到以下错误消息。

node.getBoundingClientRect is not a function

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

有什么建议吗?

谢谢!

【问题讨论】:

你的问题和这个answer类似。 【参考方案1】:

我猜你应该使用它:

let mouse = d3.mouse(mouseG); 

或者你可以这样写:

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

【讨论】:

第二个选项有效。但是现在我在 end = d3.select(lines[i]).node().getTotalLength() 遇到另一个错误,因为 Cannot read property 'getTotalLength' of null .我应该发布一个新问题还是更新这个问题? 我认为你需要创建一个最小的 plunker 来重现它 我不得不使用 let lines = document.getElementsByClassName('line'); 而不是 let lines = d3.select('.line');。现在我得到了每行都有圆圈的垂直线,但不是工具提示。我已经发布了一个单独的问题。***.com/questions/39438578/…【参考方案2】:

d3.mouse() 需要在 DOM 节点上调用。 d3.mouse(this) 不起作用,因为“this”是您的自定义类的一个实例。在您的情况下,您可以使用

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

您需要调用node(),因为mouseG 是一个d3 选择对象,而不是DOM 节点。 node() 返回底层的“g”节点。

【讨论】:

以上是关于d3,Angular 2:node.getBoundingClientRect 不是函数的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2.0 和 D3.js 不在 svg 上应用 Css [重复]

将 D3.js 导入 Angular 2 应用程序的正确方法

如何将 D3.js 与 Renderer API 与 Angular 2 集成

通过 npm 为 Angular 2 TypeScript 项目安装 d3.js 有啥好处吗?

在 Angular2 中使用 d3.js

升级到 Angular 8 后出现 d3.js 运行时错误