About 200 tasks in 40 groups, generated from today’s date, to show the Gantt chart keeps up with a big plan. Scroll through the list, fold groups away, or press the fit button to zoom the timeline to the tasks in view.
A large plan needs two things from a Gantt chart: it must stay quick, and the reader must not get lost. This one draws only the rows in view, and folding groups or fitting the timeline brings hundreds of tasks back to a size you can read.
Built with amCharts 5, a JavaScript library. The code includes your theme and renderer (canvas or SVG), but not the changes you make with the controls.
npm install @amcharts/amcharts5. See Getting started.
The same chart as a JSON config. Gantt reads and writes this format, the library's JSON parser turns it into a chart, and the <am5-chart> element puts it on a page with no code."type" is the class to create and "settings" what you would pass to its new() in code; "properties" fill in its parts, lists such as series and axes or templates such as columns.template; "refs" holds objects used in more than one place ("#series-0" stands for the one named series-0); a string starting with "@" takes a value from another object, such as "@series.get('fill')".
A complete page. Save it as an .html file and open it in a browser.
Paste this into an AI coding assistant to build the chart from scratch. More in amCharts & AI.
// Create root element
// https://www.amcharts.com/docs/v5/getting-started/#Root_element
var root = am5.Root.new("chartdiv");
// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([
am5themes_Animated.new(root)
]);
// Create Gantt chart
// https://www.amcharts.com/docs/v5/charts/gantt/
var gantt = root.container.children.push(am5gantt.Gantt.new(root, {}));
// Hide a date label that would be cut off at the left edge of the timeline
gantt.xAxis.get("renderer").labels.template.set("minPosition", 0.08);
// random plan: each category gets 2 to 6 subtasks, each linked to the next and starting after it ends
function generateGanttData(categoriesCount) {
var categories = [];
var series = [];
var date = new Date();
date.setHours(0, 0, 0, 0);
var timestamp = date.getTime();
for (let i = 0; i < categoriesCount; i++) {
const categoryId = `gantt_${i}`;
categories.push({
name: `Category ${i + 1}`,
id: categoryId
});
// no dates: a parent row's bar spans its subtasks
series.push({
id: categoryId
});
const subCount = Math.floor(Math.random() * 5) + 2;
for (let j = 0; j < subCount; j++) {
const subId = `${categoryId}_sub_${j}`;
categories.push({
name: `Subcategory ${i + 1}.${j + 1}`,
id: subId,
parentId: categoryId
});
const duration = Math.floor(Math.random() * 5) + 1;
const progress = Math.round(Math.random() * 100) / 100; // 0 to 1, with two decimals
series.push({
start: timestamp,
duration,
progress,
id: subId,
// an arrow to the next subtask; the last one links to nothing
linkTo: j < subCount - 1 ? [`${categoryId}_sub_${j + 1}`] : []
});
timestamp += duration * 86400000; // add days
}
}
return { categories, series };
}
var ganttData = generateGanttData(40);
// When the data is in, zoom the timeline to the tasks in view, plus a little room on both sides
gantt.series.events.on("datavalidated", function () {
setTimeout(function () { // half a second later
var min = gantt.series.getPrivate("selectionMinX", 0);
var max = gantt.series.getPrivate("selectionMaxX", 0);
var extraTime = am5.time.getDuration("day", 1); // one day
gantt.xAxis.zoomToValues(min - extraTime, max + extraTime * 8); // a day before, eight days after
}, 500);
});
gantt.yAxis.data.setAll(ganttData.categories);
gantt.series.data.setAll(ganttData.series);