type SwimlaneDatum = {
swimlane: number
time: number
duration: number
color: string
}
type SwimlaneDatum = {
swimlane: number
time: number
duration: number
color: string
}
function generate_segment_data(data: SwimlaneDatum[]) {
data.length = 0;
let color_scale = d3.scale.category10();
let color = color_scale('seed');
for (let swimlane = 0; swimlane < 4; swimlane++)
for (let time = Math.random()*5; time < 100; time += Math.random() * 15) {
let duration = Math.random() * 15;
if (Math.random() > 0.75)
color = color_scale(Math.random().toString());
data.push({
swimlane: swimlane,
time: time,
duration: time + duration > 100 ? 100 - time : duration,
color: color,
});
time += duration;
}
c3.array.sort_up(data, (d) => d.time);
}
Create a c3.Plot
chart.
var segment_layer: c3.Plot.Layer.Swimlane.Segment<SwimlaneDatum>;
var swimlane_timeline = new c3.Plot.Zoomable<SwimlaneDatum>({
anchor: '#swimlanes_segment_plot',
width: '95%',
height: 300,
zoomable: 'h',
data: [],
Setup the scales to go from 0-100 horizontally and 4 swimlanes vertically.
h: d3.scale.linear().domain([0, 100]),
v: d3.scale.linear().domain([0, 4]),
Add an x axis with grid lines.
axes: [
new c3.Axis.X({
grid: true,
ticks: false,
}),
],
layers: [
Add a segment swimlane layer
segment_layer = new c3.Plot.Layer.Swimlane.Segment<SwimlaneDatum>({
Accessor functions which describe how to get x, dx and y values from the data elements.
x: (d) => d.time,
dx: (d) => d.duration,
y: (d) => d.swimlane,
rect_options: {
Static styles are more efficiently handled in a CSS file, this is just an example.
styles: {
'fill': (d) => d.color,
'stroke': (d) => d3.rgb(d.color).darker().toString(),
'stroke-width': 2,
'rx': 5, // rounded corners (Doesn't work in IE11)
'shape-rendering': 'geometricPrecision',
},
},
Create lables for each segment
label_options: {
text: (d) => Math.round(d.duration),
styles: {
'font-weight': 'bold',
'text-shadow': (d)=> '1px 1px 1px '+d3.rgb(d.color).brighter().toString(),
},
},
An HTML tooltip
hover: (d) => d ? Math.round(d.duration) : null,
Add a border between swimlanes
lane_options: {
styles: {
'fill': 'none',
'stroke': 'gray',
},
},
}),
],
});
Create a c3.Plot
chart.
var sampled_svg_timeline = new c3.Plot.Zoomable<SwimlaneDatum>({
anchor: '#swimlanes_sampled_svg_plot',
width: '95%',
height: 300,
zoomable: 'h',
data: [],
Setup the scales to go from 0-100 horizontally and 4 swimlanes vertically.
h: d3.scale.linear().domain([0, 100]),
v: d3.scale.linear().domain([0, 4]),
Add an x axis with grid lines.
axes: [
new c3.Axis.X({
grid: true,
ticks: false,
}),
],
margins: { bottom: 20 },
layers: [
Add a sampled swimlane layer
new c3.Plot.Layer.Swimlane.Sampled.SVG<SwimlaneDatum>({
Accessor functions which describe how to get x, dx and y values from the data elements.
x: (d) => d.time,
dx: (d) => d.duration,
y: (d) => d.swimlane,
line_options: {
title: (d) => Math.round(d.duration),
styles: {
'stroke': (d) => d.color,
},
},
Add a border between swimlanes
lane_options: {
styles: {
'fill': 'none',
'stroke': 'gray',
},
},
}),
],
});
var sampled_canvas_timeline = new c3.Plot.Zoomable<SwimlaneDatum>({
anchor: '#swimlanes_sampled_canvas_plot',
width: '95%',
height: 300,
zoomable: 'h',
data: [],
Setup the scales to go from 0-100 horizontally and 4 swimlanes vertically.
h: d3.scale.linear().domain([0, 100]),
v: d3.scale.linear().domain([0, 4]),
Add an x axis with grid lines.
axes: [
new c3.Axis.X({
grid: true,
ticks: false,
}),
],
layers: [
Add a sampled swimlane layer
new c3.Plot.Layer.Swimlane.Sampled.Canvas<SwimlaneDatum>({
Accessor functions which describe how to get x, dx and y values from the data elements.
x: (d) => d.time,
dx: (d) => d.duration,
y: (d) => d.swimlane,
safe: false,
line_options: {
styles: {
'stroke': (d) => d.color,
},
},
Add a border between swimlanes
lane_options: {
styles: {
'fill': 'none',
'stroke': 'gray',
},
},
HTML tooltip
hover: (d) => d ? Math.round(d.duration) : null,
}),
],
});
Generate initial data
generate_segment_data(swimlane_timeline.data);
sampled_svg_timeline.data = swimlane_timeline.data;
sampled_canvas_timeline.data = swimlane_timeline.data;
Call render()
on the charts to initially render them.
swimlane_timeline.render();
sampled_svg_timeline.render();
sampled_canvas_timeline.render();
Allow the user generate new data by clicking on a button. We need to redraw()
the charts
to reflect the updated data. Note: if we had cleared random_data via: random_data = []
in the generate_data()
function, then this wouldn’t work. It would just have updated the
variable and not the array that the charts are working with. That’s why random_data.length=0
is used.
$('#sync_generate_button').on('click', () => {
generate_segment_data(swimlane_timeline.data);
swimlane_timeline.redraw();
sampled_svg_timeline.redraw();
sampled_canvas_timeline.redraw();
});
Set the chart to limit the number of segments rendered. Note that it will prioritize drawing the longest segments first.
$('#limit_segments').on('change', function () {
if (this.checked) {
$('#limit_segment_count').prop('disabled', false);
segment_layer.limit_elements = +$('#limit_segment_count').val();
} else {
$('#limit_segment_count').prop('disabled', true);
segment_layer.limit_elements = null;
}
swimlane_timeline.redraw();
});
Adjust the number of elements to limit as the slider is changed.
The input
event not supported in IE
for (var event_name of ['input', 'change']) {
$('#limit_segment_count').on(event_name, function () {
segment_layer.limit_elements = +$(this).val();
swimlane_timeline.redraw();
});
}
Resize the chart to fit the window
window.onresize = () => {
swimlane_timeline.resize();
sampled_svg_timeline.resize();
sampled_canvas_timeline.resize();
}