[C3.js][TypeScript] Draw line charts 1
Masui Masanori
Posted on November 14, 2021
Intro
This time, I will try drawing line charts with specific design.
I have tried drawing line charts with Chart.js.
But because I couldn't find how to draw additional lines with specific format like dashed lines.
So I will try C3.js in this time.
Environments
- Node.js ver.17.0.1
- TypeScript ver.4.4.4
- C3 ver.0.7.20
- PostCSS ver.8.3.11
- Webpack ver.5.61.0
- @types/c3 ver.0.7.6
- postcss-cli ver.9.0.2
- postcss-import ver.14.0.2
- ts-loader ver.9.2.6
- ts-node ver.10.4.0
- webpack-cli ver.4.9.1
Draw line charts
First, I will try drawing line charts with sample data.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>chart sample</title>
<meta charset="utf-8">
<link href="css/chart.page.css" rel="stylesheet" />
</head>
<body>
<div id="chart_root"></div>
<script src="./js/main.page.js"></script>
<script>Page.init();</script>
</body>
</html>
chart.page.css
@import url("../../node_modules/c3/c3.min.css");
I must load the CSS file of C3.
Because I don't want to copy it by myself, I use "postcss-import" to import it.
chart.type.ts
export enum LineType {
default = 0,
dashedLine,
};
export type ChartValues = {
type: LineType,
values: readonly SampleValue[],
};
export type SampleValue = {
x: number,
y: number,
};
main.page.ts
import { LineType } from "./charts/chart.type";
import { MainPageView } from "./main.page.view";
let view: MainPageView;
export function init(): void {
view = new MainPageView();
view.updateValues([{
type: LineType.default,
values: [{ x: 0.1, y: 0 },
{ x: 1, y: 1.2 },
{ x: 3.2, y: 2.5 },
{ x: 5.6, y: 6.7 },
{ x: 7, y: 7.8 },
{ x: 8.0 , y: 9 }]
}]);
}
main.page.view.ts
import { ChartValues } from "./charts/chart.type";
import { ChartViewer } from "./charts/chartViewer";
export class MainPageView {
private chartRoot: HTMLElement;
private charts: ChartViewer[] = [];
public constructor() {
this.chartRoot = document.getElementById("chart_root") as HTMLElement;
}
public updateValues(values: readonly ChartValues[]): void {
const sampleChart = new ChartViewer(this.chartRoot);
sampleChart.draw(values[0]!);
this.charts.push(sampleChart);
}
}
chartViewer.ts
import c3 from "c3";
import { ChartValues } from "./chart.type";
export class ChartViewer {
private chartElement: HTMLElement;
public constructor(root: HTMLElement) {
this.chartElement = document.createElement("div");
root.appendChild(this.chartElement);
}
public draw(value: ChartValues): void {
const chart = c3.generate({
bindto: this.chartElement,
data: {
columns: [
["data1", ...value.values.map(v => v.y)]
]},
});
}
}
Result
Change ticks for x direction
The x ticks of the sample are index numbers.
So I want to change them as x values of "SampleValue".
chartViewer.ts
...
export class ChartViewer {
...
public draw(value: ChartValues): void {
const chart = c3.generate({
bindto: this.chartElement,
data: {
x: "x",
columns: [
["data1", ...value.values.map(v => v.y)],
["x", ...value.values.map(v => v.x)],
]},
axis: {
x: {
tick: {
values: [...this.getTicksX(0, 10)],
}
}
}
});
}
private getTicksX(from: number, to: number): readonly number[] {
const results: number[] = [];
for(let i = from; i <= to; i++) {
results.push(i);
}
return results;
}
}
Result
Min, Max
The ticks are drawn only up to the values.
To draw in the specified range, I add min and max.
chartViewer.ts
...
public draw(value: ChartValues): void {
const chart = c3.generate({
bindto: this.chartElement,
data: {
x: "x",
columns: [
["data1", ...value.values.map(v => v.y)],
["x", ...value.values.map(v => v.x)],
]},
axis: {
x: {
min: 0,
max: 10,
tick: {
values: [...this.getTicksX(0, 10)],
}
}
}
});
}
...
Result
Grid
I can show grid like below.
chartViewer.ts
...
public draw(value: ChartValues): void {
const chart = c3.generate({
...
grid: {
x: {
show: true,
},
},
});
}
...
I want to add auxiliary lines and set specific designs.
chart.page.css
@import url("../../node_modules/c3/c3.min.css");
.solid_line line {
stroke: #000000;
stroke-dasharray: 1 0;
stroke-linecap: round;
}
.dashed_line line {
stroke: #9f9f9f;
stroke-dasharray: 2 5;
stroke-linecap: round;
}
chartViewer.ts
...
export class ChartViewer {
...
public draw(value: ChartValues): void {
const valueXList = this.getValueX(0, 10);
const ticksX = this.getTicksX(valueXList);
const gridLines = valueXList.map(t => this.generateGridLine(t));
const chart = c3.generate({
bindto: this.chartElement,
data: {
x: "x",
columns: [
["x", ...value.values.map(v => v.x)],
["data1", ...value.values.map(v => v.y)],
],
types: {
data1: "line"
},
},
axis: {
x: {
min: 0,
max: 10,
tick: {
values: [...ticksX],
outer: true,
}
}
},
grid: {
x: {
show: false,
lines: [...gridLines]
},
y: {
show: true,
}
},
interaction: {
enabled: false,
}
});
console.log(`${chart}`);
}
private getValueX(from: number, to: number): readonly number[] {
const results: number[] = [];
for(let i = from; i <= to; i++) {
if(i < to) {
for(let j = 0.0; j < 1.0; j += 0.1) {
results.push(i + j);
}
}
}
return results;
}
private getTicksX(values: readonly number[]): readonly string[] {
const results: string[] = [];
for(const v of values) {
if(v === (Math.trunc(v))) {
results.push(v.toString());
} else {
results.push("");
}
}
return results;
}
private generateGridLine(value: number): { value: string, class: string } {
let lineClass = "";
if(value === (Math.trunc(value))) {
lineClass = "solid_line";
} else {
lineClass = "dashed_line";
}
return {
value: value.toString(),
class: lineClass,
};
}
}
Result
Posted on November 14, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.