vertical-bar.chart-type.ts 4 KB
Newer Older
Léonard Treille's avatar
Léonard Treille committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import { Injectable } from '@angular/core';
import * as d3 from 'd3';
import { ChartType, Dataset, ChartContext, Point, ChartPadding } from '../chart.model';
import { BaseChartType } from './BaseChartType';

@Injectable()
export class VerticalBarChartType extends BaseChartType implements ChartType {

	protected radius = 2;
	protected staggerTime = 100;

	get type(): string {
		return 'vertical-bar';
	}

	getXScale(dataset: Dataset, context: ChartContext): d3.Scale {
		return d3.scaleBand()
			.domain(this.getXDomain(dataset))
			.range([8, context.width - this.getPadding(ChartPadding.RIGHT, dataset) * 2]);
	}

	getYScale(dataset: Dataset, context: ChartContext): d3.Scale {
		return d3.scaleLinear()
			.domain([0, d3.max(this.getYDomain(dataset))])
			.range([context.height - (this.getPadding(ChartPadding.BOTTOM, dataset) * 2), this.getPadding(ChartPadding.TOP, dataset) * 2]);
	}

	getYAxis(dataset: Dataset, context: ChartContext) {
		const axis = super.getYAxis(dataset, context);
		axis.ticks(4);
		return axis;
	}

	udpate(dataset: Dataset, context: ChartContext): void {
		const grouped = dataset.axis && dataset.axis.x && dataset.axis.x.grouped;
		const scaleX = this.getXScale(dataset, context);
		const scaleY = this.getYScale(dataset, context);
		const barWidth = grouped ? scaleX.bandwidth() / dataset.points.length : scaleX.bandwidth();

		// Create axis:
		this.buildAxis(dataset, context, this.getXAxis(dataset, scaleX), this.getYAxis(dataset, scaleY));

		const x = (p: Point, index: number) => {
			let _x = scaleX(p.x) + barWidth / 2;
			if (grouped) {
				_x += index * (this.getPadding(ChartPadding.LEFT) * 2);
			}
			return _x;
		};

		// Create bars:
		dataset.points.forEach((points: Point[], i: number) => {
			const barsSelection = context.chart.selectAll(`.${this.type}-${i}`).data(points);
			barsSelection
				.transition(this.transitionDuration * 4)
				.attr('d', (p: Point) => {
					const height = context.height - scaleY(p.y) - this.getPadding(ChartPadding.BOTTOM, dataset) * 2;
					return this.topRoundedColumn(x(p, i), scaleY(p.y), height, barWidth);
Léonard Treille's avatar
Léonard Treille committed
59
60
61
62
63
64
65
66
67
				})
				.attr('class', p => {
					let classes = `${this.type} ${this.type}-${i} clickable`;
					if (dataset.options.ghost) {
						classes += ' ghost';
					}
					return classes;
				})
				.style('opacity', dataset.options.ghost ? 0.3 : 0.9);
Léonard Treille's avatar
Léonard Treille committed
68
69
			barsSelection.enter()
				.append('path')
Léonard Treille's avatar
Léonard Treille committed
70
71
72
73
74
75
76
				.attr('class', p => {
					let classes = `${this.type} ${this.type}-${i} clickable`;
					if (dataset.options.ghost) {
						classes += ' ghost';
					}
					return classes;
				})
Léonard Treille's avatar
Léonard Treille committed
77
78
79
80
81
82
83
84
				.style('cursor', 'pointer')
				.attr('data-point', p => JSON.stringify(p))
				.attr('d', (p: Point) => {
					return this.topRoundedColumn(x(p, i), context.height - this.getPadding(ChartPadding.BOTTOM, dataset) * 2, 0, barWidth);
				})
				.style('opacity', 0)
				.transition(this.transitionDuration * 4)
				// .delay((point: Point, index: number) => index * this.staggerTime)
Léonard Treille's avatar
Léonard Treille committed
85
				.style('opacity', dataset.options.ghost ? 0.3 : 0.9)
Léonard Treille's avatar
Léonard Treille committed
86
87
88
89
90
91
92
93
				.attr('d', (p: Point) => {
					const height = context.height - scaleY(p.y) - this.getPadding(ChartPadding.BOTTOM, dataset) * 2;
					return this.topRoundedColumn(x(p, i), scaleY(p.y), height, barWidth);
				});
			barsSelection.exit()
				.transition(this.transitionDuration * 4)
				.style('opacity', 0)
				.attr('d', (p: Point) => {
Léonard Treille's avatar
Léonard Treille committed
94
					return this.topRoundedColumn(x(p, i) || 0, context.height - this.getPadding(ChartPadding.BOTTOM, dataset) * 2, 0, barWidth);
Léonard Treille's avatar
Léonard Treille committed
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
				})
				.remove();
		});
	}

	destroy(context: ChartContext): void {
		context.chart.selectAll(`.${this.type}`)
			.transition(this.transitionDuration)
			.attr('r', 0)
			.remove();
	}

	private topRoundedColumn(x: number, y: number, height: number, width: number) {
		const path = d3.path();
		path.moveTo(x, y + height);
		path.lineTo(x, y + this.radius);
		path.quadraticCurveTo(x, y, x + this.radius, y);
		path.lineTo(x + width - this.radius, y);
		path.quadraticCurveTo(x + width, y, x + width, y + this.radius);
		path.lineTo(x + width, y + height);
		path.closePath();
		return path;
	}
}