javascript - d3 v4 TypeScript DefinitelyTyped Angular2 线与 X 轴上的 ScaleTime

标签 javascript angular typescript d3.js definitelytyped

我正在 angular2 组件中实现以下 block (一个简单的折线图): https://bl.ocks.org/d3noob/6f082f0e3b820b6bf68b78f2f7786084

我做过: npm install d3 --save npm install @types/d3 --save

我已经在 SystemJS 中加载了 d3.min.js 和 d3.node.js。我在下面包含了我的 component.ts 文件。

import { Component, Input, ElementRef, OnInit } from '@angular/core';
import { TimelineChartConfig } from "./timeline";
import * as D3 from 'd3';


@Component({
  moduleId: module.id,
  selector: 'timeline',
  templateUrl: 'timeline.component.html',
  styles: [`
    :host {
        width: 100%;
        display:block;
    }
    :host .axis path,
    :host .axis line {
        fill: none;
        stroke: rgba(0, 0, 0, 0.2);
        color: rgba(0, 0, 0, 0.2);
        shape-rendering: crispEdges;
    }

    :host .axis text {
        font-size: 20px;
        fill: rgba(0, 0, 0, 0.9);
    }

    :host .color-label{
        display: inline;
    }
    :host(.timeline) {
        fill: none;
        stroke: steelblue;
        stroke-width: 2px;
     }
 `],
})

export class TimelineChartComponent implements OnInit {
  private host;        // D3 object referencing host dom object
  private svg;         // SVG in which we will print our chart
  private margin;      // Space between the svg borders and the actual chart graphic
  private width;       // Component width
  private height;      // Component height
  private xScale;      // D3 scale in X
  private yScale;      // D3 scale in Y
  private xAxis;       // D3 X Axis
  private yAxis;       // D3 Y Axis
  private htmlElement: HTMLElement; // Host HTMLElement
  config: Array<TimelineChartConfig> = [];

  /* Constructor, needed to get @Injectables */
  constructor(private element: ElementRef) {
    this.htmlElement = this.element.nativeElement;
    this.host = D3.select(this.element.nativeElement);
    let incomeTimeline = new TimelineChartConfig();
    incomeTimeline.settings = {
        interpolation: 'monotone'
    }
    let data = [
            { x: "1-May-12", y: 58.13},
            { x: "30-Apr-12", y: 53.98},
            { x: "27-Apr-12", y: 67.00},
            { x: "26-Apr-12", y: 89.70},
            { x: "25-Apr-12", y: 99.00},
            { x: "24-Apr-12", y: 130.28},
            { x: "23-Apr-12", y: 166.70},
            { x: "20-Apr-12", y: 234.98},
            { x: "19-Apr-12", y: 345.44},
            { x: "18-Apr-12", y: 443.34},
            { x: "17-Apr-12", y: 543.70},
            { x: "16-Apr-12", y: 580.13},
            { x: "13-Apr-12", y: 605.23},
            { x: "12-Apr-12", y: 622.77},
            { x: "11-Apr-12", y: 626.20},
            { x: "10-Apr-12", y: 628.44},
            { x: "9-Apr-12", y: 636.23},
            { x: "5-Apr-12", y: 633.68},
            { x: "4-Apr-12", y: 624.31},
            { x: "3-Apr-12", y: 629.32},
            { x: "2-Apr-12", y: 618.63},
            { x: "30-Mar-12", y: 599.55},
            { x: "29-Mar-12", y: 609.86},
            { x: "28-Mar-12", y: 617.62},
            { x: "27-Mar-12", y: 614.48},
            { x: "26-Mar-12", y: 606.98},
        ];
        incomeTimeline.dataset = data;
        this.config.push(incomeTimeline);
  }

  ngOnInit(): void {
     if (!this.config || this.config.length === 0) return;
        this.setup();
        this.buildSVG();
        this.populate();
        this.drawXAxis();
        this.drawYAxis();
  }

  /* Will setup the chart container */
  private setup(): void {
    this.margin = { top: 20, right: 20, bottom: 40, left: 40 };
    this.width = this.htmlElement.clientWidth - this.margin.left - this.margin.right;
    this.height = this.width * 0.5 - this.margin.top - this.margin.bottom;
    this.xScale = D3.scaleTime().range([0, this.width]);
    this.yScale = D3.scaleLinear().range([this.height, 0]);
  }

  /* Will build the SVG Element */
  private buildSVG(): void {
    this.host.html('');
    this.svg = this.host.append('svg')
      .attr('width', this.width + this.margin.left + this.margin.right)
      .attr('height', this.height + this.margin.top + this.margin.bottom)
      .append('g')
      .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
  }
  /* Will draw the X Axis */
  private drawXAxis(): void {
    this.xAxis = D3.axisBottom(this.xScale);
    this.svg.append('g')
      .attr('class', 'x axis')
      .attr('transform', 'translate(0,' + this.height + ')')
      .call(this.xAxis);
  }

  /* Will draw the Y Axis */
  private drawYAxis(): void {
    this.yAxis = D3.axisLeft(this.yScale)
      .tickPadding(10);
    this.svg.append('g')
      .attr('class', 'y axis')
      .call(this.yAxis)
      .append('text')
      .attr('transform', 'rotate(-90)');
  }

  /* Will get the Maximum value in Y */
  private getMaxY(): number {
    let maxValuesOfCharts = [];
    this.config.forEach(data => maxValuesOfCharts.push(Math.max.apply(Math, data.dataset.map(d => d.y))));
    return Math.max(...maxValuesOfCharts);
  }

  /* Will populate datasets into areas*/
  private populate(): void {
    let parseTime = D3.timeParse("%d-%b-%y");
    this.config.forEach((timeline: any) => {
      console.log("timeline config", timeline);
      timeline.dataset.forEach((d: any) => d.x = parseTime(d.x));
      this.xScale.domain(D3.extent(timeline.dataset, (d: any) => d.x));
      this.yScale.domain([0, this.getMaxY()]);

      let valueline = D3.line()
        .x((d) => this.xScale(d.x))
        .y((d) => this.yScale(d.y))

      this.svg.append('path')
        .datum(timeline.dataset)
        .attr('class', 'timeline')
        .attr('d', valueline);
    });
  }
}

这是@types/d3-shape/index.d.ts 的 Line 部分,我认为这是合适的部分?但我不知道如何解释这一点。

export interface Line<Datum> {
    /**
     * Generates a line for the given array of data. Depending on this line generator’s associated curve,
     * the given input data may need to be sorted by x-value before being passed to the line generator.
     *
     * IMPORTANT: If the rendering context of the line generator is null,
     * then the line is returned as a path data string.
     *
     * @param data Array of data elements.
     */
    (data: Datum[]): string | null;
    /**
     * Generates a line for the given array of data. Depending on this line generator’s associated curve,
     * the given input data may need to be sorted by x-value before being passed to the line generator.
     *
     * IMPORTANT: If the line generator has been configured with a rendering context,
     * then the line is rendered to this context as a sequence of path method calls and this function returns void.
     *
     * @param data Array of data elements.
     */
    (data: Datum[]): void;

    /**
     * Returns the current x-coordinate accessor function, which defaults to a function returning first element of a two-element array of numbers.
     */
    x(): (d: Datum, index: number, data: Datum[]) => number;
    /**
     * Sets the x accessor to the specified number and returns this line generator.
     *
     * @param x A constant x-coordinate value.
     */
    x(x: number): this;
    /**
     * Sets the x accessor to the specified function and returns this line generator.
     *
     * When a line is generated, the x accessor will be invoked for each defined element in the input data array.
     *
     * The default x accessor assumes that the input data are two-element arrays of numbers. If your data are in a different format, or if you wish to transform the data before rendering,
     * then you should specify a custom accessor.
     *
     * @param x A coordinate accessor function which returns the x-coordinate value. The x accessor will be invoked for each defined element in the input data array,
     * being passed the element d, the index i, and the array data as three arguments.
    x(x: (d: Datum, index: number, data: Datum[]) => number): this;

    /**
     * Returns the current y-coordinate accessor function, which defaults to a function returning second element of a two-element array of numbers.
     */
    y(): (d: Datum, index: number, data: Datum[]) => number;
    /**
     * Sets the y accessor to the specified number and returns this line generator.
     *
     * @param y A constant y-coordinate value.
     */
    y(y: number): this;
    /**
     * Sets the y accessor to the specified function and returns this line generator.
     *
     * When a line is generated, the y accessor will be invoked for each defined element in the input data array.
     *
     * The default y accessor assumes that the input data are two-element arrays of numbers. If your data are in a different format, or if you wish to transform the data before rendering,
     * then you should specify a custom accessor.
     *
     * @param y A coordinate accessor function which returns the y-coordinate value. The y accessor will be invoked for each defined element in the input data array,
     * being passed the element d, the index i, and the array data as three arguments.
     */
    y(y: (d: Datum, index: number, data: Datum[]) => number): this;

图形已加载,但线路径已填充。

这是一个笨蛋:http://plnkr.co/edit/KwcgHsc7aPpE1tjv8mlb?p=preview

问题一:

为什么我会收到以下 TypeScript 错误:

错误 TS2339:类型“[number, number]”上不存在属性“x”。

错误 TS2339:类型“[number, number]”上不存在属性“y”。

问题二:

为什么我的 :host(.timeline) 样式没有被应用?

最佳答案

您需要将类型传递给 d3.line

例如,如果您的模型接口(interface)称为 Data

interface Data {
   x: number;
   y: number;
}

然后使用

d3.line<Data>()
    .x(d => scaleX(d.x))
    .y(d => scaleY(d.y));

这应该可以解决问题 #1

关于javascript - d3 v4 TypeScript DefinitelyTyped Angular2 线与 X 轴上的 ScaleTime,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44599129/

相关文章:

javascript - 这行 javascript 简写是什么意思?

javascript - 菜单边框,当前菜单边框 css

javascript - 如何将 localstorage 的值从 View 传递到 Controller 以用作另一个 View 中的 php 变量

Angular ViewChild 自定义排序选择器

angular - 默认打开 ion-fab-list

javascript - react-native-webview 只有在 react-navigation tabNavigator 中的事件选项卡上时才开始加载

Angular/Spring Boot/Azure Ingress - URL 重写

Angular 2 阅读更多指令

angular - 根据运行时标志提供 API 或模拟版本

javascript - Angular 2 - 在模板中的变量内显示变量