iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 24
1
AI & Data

D3.js資料視覺化的浪漫突進系列 第 24

Day24 D3js Gantt Chart 來張甘特圖吧!

D3js Gantt Chart 來張甘特圖吧!

用途

甘特圖在眾多套件中也有不少已經有提供了,所以除非必要特殊客製化功能,不然應該不需要自己刻一個。

基本分析

圖上元件

根據圖上需要的部分,分成以下幾個部分。

Axis比例尺
甘特圖的方塊以及顏色識別

資料識別

甘特圖需要具備的資料是別

時間
Task名稱

簡易範例

準備基本部分範例:

console.clear();
const width = 800;
const height = 600;
const padding = 50;
const innerWidth = width - padding * 2;
const innerHeight = height - padding * 2;

const svg = d3.select('svg').attr('width', width).attr('height', height);
const rootLayer = svg.append('g').attr('transform', `translate(${padding}, ${padding})`);
const axisLayer = rootLayer.append('g');
const xAxisLayer = axisLayer.append('g');
const yAxisLayer = axisLayer.append('g');
const tasksLayer = rootLayer.append('g');

let series = [];
let xExtent;
let yExtent;
let xScale;
let yScale;
let xAxis;
let yAxis;

const datas = [
  {
    "name": "Task1",
    "start": "2020/10/09 01:00:00",
    "end": "2020/10/09 02:00:00",
    "fill": "#b3e2cd"
  },
  {
    "name": "Task2",
    "start": "2020/10/09 02:00:00",
    "end": "2020/10/09 03:00:00",
    "fill": "#fdcdac"
  },
  {
    "name": "Task3",
    "start": "2020/10/09 02:00:00",
    "end": "2020/10/09 04:00:00",
    "fill": "#cbd5e8"
  },
  {
    "name": "Task4",
    "start": "2020/10/09 03:00:00",
    "end": "2020/10/09 04:00:00",
    "fill": "#f4cae4"
  },
  {
    "name": "Task5",
    "start": "2020/10/09 03:00:00",
    "end": "2020/10/09 05:00:00",
    "fill": "#e6f5c9"
  }
];

以上皆為基本圖層設定以及基本資料配置。
series為內部使用資料,datas為原始資料,差異為會將datas的資料時間格式轉換為時間字串。

計算以及渲染範例


const preProcess = () => {
  series = datas.map(data => ({
    name: data.name,
    start: new Date(data.start),
    end: new Date(data.end),
    fill: data.fill
  }))
}

const calcExtent = () => {
  xExtent = [d3.min(series, serie => serie.start), d3.max(series, serie => serie.end)];
}

const calcScale = () => {
  xScale = d3.scaleTime().range([0, innerWidth]).domain(xExtent);
  // 此處使用`ScaleBand`因為是以資料的個別名稱為主,以名稱為類別。
  yScale = d3.scaleBand().range([0, innerHeight]).domain(series.map(serie => serie.name));
}

const paintAxis = () => {
  xAxis = d3.axisBottom().scale(xScale);
  yAxis = d3.axisLeft().scale(yScale);
  
  yAxisLayer.call(yAxis);
  xAxisLayer.call(xAxis);
}

const paintTasks = () => {
  tasksLayer.selectAll('rect')
            .data(series)
            .enter()
            .append('rect')
            .attr('x', serie => xScale(serie.start))
            // yScale.bandwidth可以得知,全部種類分別的y軸長度為多少。
            .attr('y', serie => yScale(serie.name) + yScale.bandwidth() * 0.25)
            // 透過算出頭尾位置,計算出寬度
            .attr('width', serie => xScale(serie.end) - xScale(serie.start))
            // 為了讓各個Task中間有空格,因此高度只填滿0.5
            .attr('height', yScale.bandwidth() * 0.5)
            .attr('fill', serie => serie.fill)
}

// 前置資料處理
preProcess();

// 計算Extent
calcExtent();

// 計算Scale
calcScale();

// 繪製比例尺
paintAxis();

// 繪製Task
paintTasks();

結論

甘特圖其實自己實作非常容易,而且擴充方便,可以針對自身特別業務進行擴充,讓甘特圖有趣又好用!

Codepen範例


上一篇
Day23 D3js Candlestick Chart來張K線圖吧!
下一篇
Day25 D3js d3-tip 好用的小工具
系列文
D3.js資料視覺化的浪漫突進30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言