我正在angular2组件中实现以下块(简单折线图): https://bl.ocks.org/d3noob/6f082f0e3b820b6bf68b78f2f7786084
我做过:
npm install d3 --save
npm install @types/d3 --save
我已经加载了d3.min.js和d3.node.js vi SystemJS。我在下面包含了我的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
问题1:
为什么我收到以下TypeScript错误:
error TS2339: Property 'x' does not exist on type '[number, number]'.
error TS2339: Property 'y' does not exist on type '[number, number]'.
问题2:
为什么我的:host(.timeline)
样式没有被应用?
答案 0 :(得分:3)
您需要将类型传递给d3.line
例如,如果您的模型界面名为Data
interface Data {
x: number;
y: number;
}
然后使用
d3.line<Data>()
.x(d => scaleX(d.x))
.y(d => scaleY(d.y));
那应该解决问题#1
答案 1 :(得分:0)
#2的答案是我需要将角度分量的ViewEncapsulation设置为none。