我正在使用带有角度4的d3 V4来开发分组条形图,我能够成功地完成它。现在我需要增强分组条形图以启用像 this示例那样的平移和缩放。但我怀疑如何将其应用于我的代码。
// imports;
let d3: any = D3;
@Component({
selector: 'grouped-bar-chart',
template: '<ng-content></ng-content>'
})
export class GroupedBarChartComponent {
@Input('chartConfig') config: GroupedBarChartModel;
htmlElement: HTMLElement;
host;
width;
height;
innerWidth;
innerHeight;
margin;
x0Scale;
x1Scale;
yScale;
zScale; // color scale
svg;
tooltip;
breakPoint; // used to check if we are on small screens or not
keys;
dataset;
disabledSeries;
constructor(private element: ElementRef) {
if (this.config == null) {
this.config = new GroupedBarChartConfigModel();
}
this.htmlElement = this.element.nativeElement;
this.host = d3.select(this.element.nativeElement);
this.innerWidth = window.innerWidth;
this.innerHeight = window.innerHeight;
this.breakPoint = 768;
this.keys = []; // this holds bars contains in one cluster
this.disabledSeries = [];
this.margin = this.config.margin;
this.ngOnChanges();
}
@HostListener('window:resize', ['$event'])
onResize(event) {
this.ngOnChanges();
}
/**
* Every time the @Input is updated, rebuild the chart
**/
ngOnChanges(): void {
if (!this.config || !this.host || this.config.dataset.length === 0) return;
this.init();
this.setup();
this.buildSVG();
this.scaleAxises();
this.drawXAxis();
this.drawYAxis();
this.populate();
this.drawLegend();
}
/**
* Initialize chart dataset
*/
init(): void {
let data;
//////////////////
this.dataset = data;
}
/**
* Basically we get the window size and build the container configs
* also we create the xScale, yScale & zScale(color scale) ranges depending on calculations
**/
setup(): void {
this.keys = [];
// this.width = this.innerWidth * this.config.width;
// this.height = this.innerHeight * this.config.height;
this.width = this.config.width - this.margin.left - this.margin.right;
this.height = this.config.height - this.margin.top - this.margin.bottom;
// determine the geometry of the bars and spaces the individual bars within a cluster
this.x0Scale = d3.scaleBand().rangeRound([0, this.width]).paddingInner(0.01);
// spaces the different clusters of bars across the page
this.x1Scale = d3.scaleBand().padding(0.05);
this.yScale = d3.scaleLinear().rangeRound([this.height, 0]);
this.zScale = d3.scaleOrdinal()
.range(GraphColorPalette.PALETTE);
this.dataset[0].values.map((d: any) => {
if (this.keys.indexOf(d.key) === -1) {
this.keys.push(d.key);
}
});
}
/**
* build SVG element using configurations
**/
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 + ')');
this.svg.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', this.width)
.attr('height', this.height)
.style('fill', '#eee')
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
}
/**
**/
scaleAxises(): void {
this.x0Scale.domain(this.dataset.map((d: any) => {
return d.key;
}));
this.x1Scale.domain(this.keys).rangeRound([0, this.x0Scale.bandwidth()]);
let maxDomain = d3.max(this.dataset, (d: any) => {
return d3.max(this.keys, (key: any, i: number) => {
return d.values[i].value;
})
});
this.yScale.domain([0, maxDomain + 1]).nice();
}
/**
* Populate the chart using
**/
populate(): void {
this.drawColumns(this.x1Scale, this.keys, this.dataset);
this.createToolTips();
// add a title to the graph
this.svg.append("text")
.attr("x", (this.width / 2))
.attr("y", 0 - (this.margin.top / 2))
.attr("text-anchor", this.config.titlePosition)
.attr("class", "title")
.style("font-size", "18px")
.style("text-decoration", "underline")
.text(this.config.title);
}
drawColumns(x1Scale: any, keys: Array<string>, data: any): void {
this.svg.append("g")
.attr("class", "chart-layer")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", (d: any) => {
return "translate(" + this.x0Scale(d.key) + ",0)";
})
.selectAll("rect")
.data((d: any) => {
return keys.map((key, i) => {
return {id: d.key, key: key, name: d.name, value: d.values[i].value};
});
})
.enter().append("rect")
.attr("fill", (d) => {
return this.zScale(d.key);
})
.attr("class", "bar")
.style('cursor', 'pointer')
.on("mousemove", (d: any) => {
this.showTooltip(d);
})
.on("mouseout", () => {
this.hideTooltip();
})
.attr("x", (d: any) => {
return x1Scale(d.key);
})
.attr("y", this.height)
.attr("width", x1Scale.bandwidth())
.transition()
.ease(d3.easeLinear)
.duration(1000)
.delay((d, i) => {
return i * 50;
})
.attr("y", (d) => {
return this.yScale(d.value);
})
.attr("height", (d) => {
return Math.abs(this.height - this.yScale(d.value));
})
}
/**
* Create X-axis
**/
drawXAxis(): void {
this.svg.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + this.height + ")")
.call(d3.axisBottom(this.x0Scale)
.tickSize(0)
.tickPadding(6));
// Add title to x axis
this.svg.append("text")
.attr("class", "axis-x--label")
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", this.config.xLabelPosition)
.attr("transform", () => {
switch (this.config.xLabelPosition) {
case ChartLabelPositions.START:
return "translate(" + (0) + "," + (this.height - (this.margin.bottom / 10) + 40) + ")";
case ChartLabelPositions.MIDDLE:
return "translate(" + (this.width / 2) + "," + (this.height - (this.margin.bottom / 10) + 40) + ")";
case ChartLabelPositions.END:
return "translate(" + (this.width) + "," + (this.height - (this.margin.bottom / 10) + 40) + ")";
default:
//do nothing
break;
}
}
)
.text(this.config.xLabel);
}
/**
* Create Y-axis
**/
drawYAxis(): void {
this.svg.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(this.yScale).tickSize(-this.width).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", this.yScale(this.yScale.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", this.config.yLabelPosition)
.text(this.config.yLabel);
}
}
非常感谢任何建议。
谢谢!