Gantt

Gantt charts illustrate project schedules. They show the start and finish dates of each task and typically add information such as the completion state of each task and dependencies between tasks. In this sample, we assign the gantItemFormatter() method to the itemFormatter property and the ganttChartRendered() method to the rendered event to create a Gantt chart using FlexChart.

Learn about FlexChart | FlexChart API Reference

import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import './styles.css'; // import { clamp, format, isArray, isNumber, Rect } from '@mescius/wijmo'; import { FlexChart, Palettes } from '@mescius/wijmo.chart'; import { getData } from './data'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // create Gantt chart let theChart = new FlexChart('#theChart', { itemsSource: getData(), chartType: 'Bar', bindingX: 'name', tooltip: { content: getTooltipContent }, axisY: { majorGrid: false, minorGrid: true, reversed: true }, itemFormatter: ganttItemFormatter, rendered: ganttChartRendered, series: [ { binding: 'start,end' } ], palette: getRandomPalette() }); // // utilities function getTooltipContent(ht) { let str = format('<b>{name}</b>:<br/>{start:d} - {end:d}', { name: ht.x, start: ht.item.start, end: ht.item.end }); // if (ht.item && ht.item.percent != null) { str += format('<br/><i>percent complete: {percent}%</i>', ht.item); } // return str; } // // show the percentage complete for each task function ganttItemFormatter(engine, hti, defaultFormatter) { // draw the item as usual defaultFormatter(); // // show percentage done let task = hti.item; // if (isNumber(task.percent) && task.percent > 0) { let pct = clamp(task.percent, 0, 100) / 100, rc = getTaskRect(hti.series.chart, task).inflate(-8, -8); // engine.fill = pct == 1 ? 'green' : 'gold'; //engine.stroke; engine.drawRect(rc.left, rc.top, rc.width * pct, rc.height); } } // // show the task dependencies function ganttChartRendered(chart, e) { let tasks = chart.collectionView.items; // tasks.forEach(task => { let parents = getTaskParents(task, tasks); // get the parent tasks // parents.forEach(function (parent) { drawConnectingLine(e.engine, chart, task, parent); // draw connector }); }); } // function drawConnectingLine(engine, chart, task, parent) { let rc1 = getTaskRect(chart, parent), // parent rect rc2 = getTaskRect(chart, task), // task rect x1 = rc1.left + rc1.width / 2, // parent x center x2 = rc2.left, // task left y1 = rc1.bottom, // parent bottom y2 = rc2.top + rc2.height / 2; // task y center // // draw connecting line let xs = [x1, x1, x2], ys = [y1, y2, y2]; // engine.drawLines(xs, ys, 'connector', { stroke: 'black' }); // // draw arrow at the end let sz = 5; // xs = [x2 - 2 * sz, x2, x2 - 2 * sz]; ys = [y2 - sz, y2, y2 + sz]; // engine.drawPolygon(xs, ys, 'arrow', { fill: 'black' }); } // function getTaskRect(chart, task) { let x1 = chart.axisX.convert(task.start.valueOf()), x2 = chart.axisX.convert(task.end.valueOf()), index = chart.collectionView.items.indexOf(task), y1 = chart.axisY.convert(index - .35), y2 = chart.axisY.convert(index + .35); // return new Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); } // function getTaskParents(task, tasks) { let parents = []; // if (task.parent) { task.parent.split(',').forEach(name => { for (let i = 0; i < tasks.length; i++) { if (tasks[i].name === name) { parents.push(tasks[i]); break; } } }); } // return parents; } } // function getRandomPalette() { let palettes = Object.keys(Palettes).filter(prop => isArray(Palettes[prop])); let rand = Math.floor(Math.random() * palettes.length); // return Palettes[palettes[rand]]; }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo FlexChart Gantt</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.21.5/system.src.js" integrity="sha512-skZbMyvYdNoZfLmiGn5ii6KmklM82rYX2uWctBhzaXPxJgiv4XBwJnFGr5k8s+6tE1pcR1nuTKghozJHyzMcoA==" crossorigin="anonymous"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <div id="theChart"></div> </div> </body> </html>
// export function getData() { let year = new Date().getFullYear(); // return [ { name: 'Task1', start: new Date(year, 0, 1), end: new Date(year, 2, 31), parent: null, percent: 100 }, { name: 'Task2', start: new Date(year, 3, 1), end: new Date(year, 3, 30), parent: 'Task1', percent: 100 }, { name: 'Task3', start: new Date(year, 4, 1), end: new Date(year, 6, 31), parent: 'Task2', percent: 75 }, { name: 'Task4', start: new Date(year, 3, 1), end: new Date(year, 6, 31), parent: 'Task1', percent: 33 }, { name: 'Task5', start: new Date(year, 7, 1), end: new Date(year, 8, 30), parent: 'Task3,Task4', percent: 0 }, { name: 'Task6', start: new Date(year, 9, 1), end: new Date(year, 11, 31), parent: 'Task1,Task5', percent: 0 }, { name: 'Task7', start: new Date(year, 0, 1), end: new Date(year, 11, 31), parent: null, percent: 50 } ]; }
body { margin-bottom: 24pt; }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@mescius/wijmo': 'npm:@mescius/wijmo/index.js', '@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js', '@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures', '@mescius/wijmo.chart': 'npm:@mescius/wijmo.chart/index.js', '@mescius/wijmo.chart.analytics': 'npm:@mescius/wijmo.chart.analytics/index.js', '@mescius/wijmo.chart.animation': 'npm:@mescius/wijmo.chart.animation/index.js', '@mescius/wijmo.chart.annotation': 'npm:@mescius/wijmo.chart.annotation/index.js', '@mescius/wijmo.chart.finance': 'npm:@mescius/wijmo.chart.finance/index.js', '@mescius/wijmo.chart.finance.analytics': 'npm:@mescius/wijmo.chart.finance.analytics/index.js', '@mescius/wijmo.chart.hierarchical': 'npm:@mescius/wijmo.chart.hierarchical/index.js', '@mescius/wijmo.chart.interaction': 'npm:@mescius/wijmo.chart.interaction/index.js', '@mescius/wijmo.chart.radar': 'npm:@mescius/wijmo.chart.radar/index.js', '@mescius/wijmo.chart.render': 'npm:@mescius/wijmo.chart.render/index.js', '@mescius/wijmo.chart.webgl': 'npm:@mescius/wijmo.chart.webgl/index.js', '@mescius/wijmo.chart.map': 'npm:@mescius/wijmo.chart.map/index.js', '@mescius/wijmo.gauge': 'npm:@mescius/wijmo.gauge/index.js', '@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js', '@mescius/wijmo.grid.detail': 'npm:@mescius/wijmo.grid.detail/index.js', '@mescius/wijmo.grid.filter': 'npm:@mescius/wijmo.grid.filter/index.js', '@mescius/wijmo.grid.search': 'npm:@mescius/wijmo.grid.search/index.js', '@mescius/wijmo.grid.style': 'npm:@mescius/wijmo.grid.style/index.js', '@mescius/wijmo.grid.grouppanel': 'npm:@mescius/wijmo.grid.grouppanel/index.js', '@mescius/wijmo.grid.multirow': 'npm:@mescius/wijmo.grid.multirow/index.js', '@mescius/wijmo.grid.transposed': 'npm:@mescius/wijmo.grid.transposed/index.js', '@mescius/wijmo.grid.transposedmultirow': 'npm:@mescius/wijmo.grid.transposedmultirow/index.js', '@mescius/wijmo.grid.pdf': 'npm:@mescius/wijmo.grid.pdf/index.js', '@mescius/wijmo.grid.sheet': 'npm:@mescius/wijmo.grid.sheet/index.js', '@mescius/wijmo.grid.xlsx': 'npm:@mescius/wijmo.grid.xlsx/index.js', '@mescius/wijmo.grid.selector': 'npm:@mescius/wijmo.grid.selector/index.js', '@mescius/wijmo.grid.cellmaker': 'npm:@mescius/wijmo.grid.cellmaker/index.js', '@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js', '@mescius/wijmo.odata': 'npm:@mescius/wijmo.odata/index.js', '@mescius/wijmo.olap': 'npm:@mescius/wijmo.olap/index.js', '@mescius/wijmo.rest': 'npm:@mescius/wijmo.rest/index.js', '@mescius/wijmo.pdf': 'npm:@mescius/wijmo.pdf/index.js', '@mescius/wijmo.pdf.security': 'npm:@mescius/wijmo.pdf.security/index.js', '@mescius/wijmo.viewer': 'npm:@mescius/wijmo.viewer/index.js', '@mescius/wijmo.xlsx': 'npm:@mescius/wijmo.xlsx/index.js', '@mescius/wijmo.undo': 'npm:@mescius/wijmo.undo/index.js', '@mescius/wijmo.interop.grid': 'npm:@mescius/wijmo.interop.grid/index.js', '@mescius/wijmo.touch': 'npm:@mescius/wijmo.touch/index.js', '@mescius/wijmo.cloud': 'npm:@mescius/wijmo.cloud/index.js', '@mescius/wijmo.barcode': 'npm:@mescius/wijmo.barcode/index.js', '@mescius/wijmo.barcode.common': 'npm:@mescius/wijmo.barcode.common/index.js', '@mescius/wijmo.barcode.composite': 'npm:@mescius/wijmo.barcode.composite/index.js', '@mescius/wijmo.barcode.specialized': 'npm:@mescius/wijmo.barcode.specialized/index.js', 'jszip': 'npm:jszip/dist/jszip.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);