紀錄工作經驗、相關知識,解決技術相關問題。

Javascript, 工作雜談, 新手 Jamp Holes, 網站相關, 資訊相關

Chart.js 教學:從 CDN 引入到 JS 網頁圖錶快速繪製

在網頁開發與前端專案中,經常需要將大量的數據以視覺化圖表呈現,幫助使用者更快地理解數據內容、趨勢與商業洞察。面對這類需求,最常用也最容易上手的工具非 Chart.js 莫屬。本文將帶領你從 CDN 引入開始,快速掌握基礎繪製流程,並進一步探討其他常見圖表類型與響應式設定的實務注意事項。

⚡ 快速導覽

Chart.js – 簡單實用的JS圖表庫

Chart.js 具有人性化的簡潔設計和強大的客製化能力,非常適合新手快速入門,同時也能滿足企業級專案的複雜需求。

Chart.js 互動式 JavaScript 圖表庫官方網站首頁

官方連結 : Chart.js 官方文件

簡易使用範例

  1. 引入Chart.js

首先透過 CDN 或 npm 將 Chart.js 引入你的網頁:

<!-- CDN -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
# 或是使用 npm
npm install chart.js
  1. 準備Canvas元素

接著在 HTML 中準備一個 <canvas> 容器,這就是之後圖表呈現的區域:

<canvas id="myChart"></canvas>
  1. 使用Chart.js API繪製圖表

最後就是在 JavaScript 撰寫程式碼,呼叫 Chart.js 提供的 API 函式即可輕鬆繪製圖表:

// 準備圖表資料
var data = {
    labels: ['一月', '二月', '三月', '四月'],
    datasets: [{
        label: '營收統計',
        data: [5000, 8000, 12000, 6500],
        backgroundColor: 'rgb(116, 185, 255)'
    }]
};

// 設定圖表類型和屬性
var options = {
    responsive: true,
    scales: {
        y: {
            beginAtZero: true
        }
    }
};

// 繪製圖表
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
    type: 'bar', // 圖表類型
    data: data,
    options: options
});

結果呈現

使用 Chart.js 繪製之月度營收統計長條圖渲染結果

以上範例將會在網頁上呈現一個簡單的營收統計長條圖。你只需傳入對應的資料和樣式設定,Chart.js 會自動幫你生成高品質的互動式圖表,內建支援多種常見圖表類型。

Chart.js 支援的其他常見圖表類型

除了上述示範的長條圖(bar)之外,Chart.js 內建支援多種核心圖表類型,只需透過調整 type 屬性與對應的 data 結構即可輕鬆切換:

  1. 折線圖(Line Chart, type: 'line')
    • 適用情境:適合呈現隨著時間推移而變化的連續性數據(例如每日流量、股價走勢)。
    • 設定要點:可在 datasets 中設定 tension 屬性(例如 0.1 到 0.4)來讓折線變得平滑。
  2. 圓餅圖與環形圖(Pie & Doughnut Chart, type: 'pie' / type: 'doughnut')
    • 適用情境:適合表現各類別佔整體比例的分配狀況(例如瀏覽器市佔率、預算分配)。
    • 設定要點:此類圖表通常不需要座標軸(scales),資料結構中的 backgroundColor 陣列需提供與 data 相同數量且不同的色彩。
  3. 雷達圖(Radar Chart, type: 'radar')
    • 適用情境:適合多維度指標的綜合評比(例如能力值評估、效能評測)。

響應式設定與版面調整注意事項

在實務網頁開發中,圖表必須能夠適應各種螢幕尺寸(如桌機、平板與手機)。以下是設定響應式時必須注意的關鍵事項:

  1. 確保 responsive: true 生效
    在 options 物件中預設即為 responsive: true,這會讓圖表自動偵測父層容器的大小並進行縮放。
  2. 父層容器的寬高控制限制
    Chart.js 的響應式機制是依據父層 DOM 元素的大小來計算。因此,請絕對不要直接在 <canvas> 標籤上寫死固定的 width 與 height 屬性。正確的做法是將 <canvas> 包裹在一個具有相對定位(position: relative)且指定了寬高或寬高比的父層 <div> 容器中,否則在手機螢幕上常會遇到圖表不斷放大或變形的問題。正確的 HTML 與 CSS 實作範例:
    <!-- HTML 結構:將 canvas 包裹於父層 div 中 -->
    <div class="chart-container">
        <canvas id="myResponsiveChart"></canvas>
    </div>
    /* CSS 設定:確保父層擁有相對定位與尺寸限制 */
    .chart-container {
        position: relative;
        width: 100%;
        max-width: 800px;
        margin: 0 auto;
    }
  3. 維持固定寬高比(Maintain Aspect Ratio)
    若希望圖表在響應式縮放時維持固定的長寬比例,可保留預設的 maintainAspectRatio: true;若想完全由 CSS 自定義容器高度,則需將其設為 false。

工程師實務開發常見 3 大踩坑與排錯解法

在實際前端專案(如 Vue、React、單頁式應用 SPA 或傳統 jQuery 專案)中實作 Chart.js 時,工程師往往會遇到幾個官方文件沒寫清楚、但在 F12 主控台(Console)或介面上直接爆炸的經典情境:


坑一:Canvas 重複渲染與「Canvas is already in use」記憶體洩漏

  • 發生情境:在單頁式應用(SPA)或透過 Ajax / 頁籤切換(Tab)動態載入圖表的頁面中,當使用者頻繁切換頁面或重複觸發函式時,F12 主控台會跳出致命錯誤:Canvas is already in use. Chart with ID '0' must be destroyed before creating a new one。
  • 根本原因:舊的 Chart 實例(Instance)還殘留在記憶體與 DOM 元素中,當程式再次執行 new Chart(ctx, ...) 時,Chart.js 發現該 Canvas 已經被綁定,拒絕覆蓋。
  • 正確排錯與解法:
    在每次建立新圖表前,必須先檢查並呼叫 .destroy() 方法將舊實例銷毀。
    // 宣告一個全域或元件級別的變數來存放 chart 實例
    let myChart = null;
    
    function renderChart(newData) {
        const ctx = document.getElementById('myChart').getContext('2d');
        
        // 關鍵防護:若實例已存在,先銷毀舊的
        if (myChart) {
            myChart.destroy();
        }
    
        // 建立新實例
        myChart = new Chart(ctx, {
            type: 'bar',
            data: newData,
            options: { responsive: true }
        });
    }

坑二:非同步資料(AJAX / Fetch API)尚未回傳就執行繪製導致的空白

  • 發生情境:從後端 API 透過 fetch() 非同步抓取資料來畫圖,結果圖表一開始呈現空白,或者 F12 噴出 Cannot read properties of undefined (reading 'datasets')。
  • 根本原因:JavaScript 的非同步特性使得 new Chart() 在資料回來之前就已經執行,此時傳入的 data 陣列為空或 undefined。
  • 正確排錯與解法:
    務必將繪製圖表的邏輯寫在 fetch().then() 的回呼函式內部,確保拿到資料後才初始化 Chart.js:
    fetch('/api/get-revenue-data')
        .then(response => response.json())
        .then(result => {
            // 確保資料確實回傳並組裝完畢再建立圖表
            const ctx = document.getElementById('myChart').getContext('2d');
            new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: result.months,
                    datasets: [{
                        label: '即時營收統計',
                        data: result.values,
                        backgroundColor: 'rgb(54, 162, 235)'
                    }]
                },
                options: { responsive: true }
            });
        })
        .catch(error => console.error('載入圖表資料失敗:', error));

坑三:父層 display: none(如 Bootstrap Modal 或 Tab 切換)導致圖表無限放大變形

  • 發生情境:將 Chart.js 放在隱藏的 Modal 彈跳視窗、折疊面板(Accordion)或是未啟動的 Tab 頁籤中。當視窗打開時,圖表尺寸變得極端巨大或完全擠壓變形。
  • 根本原因:當父層元素處於 display: none 時,DOM 元素的寬高為 0。Chart.js 在初始化時讀取到寬高為 0,等到視窗打開變成 display: block 後,響應式引擎計算錯誤。
  • 正確排錯與解法:
    必須將 Chart.js 的初始化時機延遲到「Modal 完全展開顯示(Shown 事件)」之後,或者在父層顯示時手動呼叫 myChart.resize()。以 Bootstrap Modal 為例:
    const myModalEl = document.getElementById('myModal');
    
    myModalEl.addEventListener('shown.bs.modal', event => {
        // 確保 Modal 完全展開、DOM 寬高正確計算後才畫圖
        renderChart(); 
    });

除了基本功能外,Chart.js 還提供了例如動態更新、樣式自訂、多重資料序列等進階選項,讓你的圖表能按需求客製化,與網頁設計完美無縫整合,為你的數據視覺化體驗增添獨特魅力。

總結

在現代網頁中,能夠將數據以圖表方式呈現可說是相當重要的技能。透過引入輕量且功能強大的 JS 圖表庫,就能輕鬆打造出美觀又專業的數據視覺化呈現,搭配正確的圖表類型選擇與響應式排版規劃,更能大幅提升網站的資料可讀性與專業說服力。

參考資料與延伸閱讀

發佈留言