Vue3 + Chart.js 基本使用

去年參與的幾個專案有使用的 EChart 這個套件,結果被業主掃出有資安問題。綜合考量之下,決定改用 Chart.js 來取代 EChart。在這邊整理一下 Chart.js 在 Vue3 的使用方法,考量多使用一個套件就要多考量一分資安風險,所以就不合併使用 vue-chart.js 。

安裝

npm install chart.js

引入

<script setup>
	// 引入
  import Chart from "chart.js/auto";
  import { onMounted } from "vue";
	// 以下為程式碼
</script>

Chart.js 資料結構

以下先以柱狀圖進行說明,同樣的方法也可應用在折線圖上。

資料為一個物件,裡面分別有 labes 及 datasets 屬性,兩者皆為陣列:

  • labels: 為圖表 X 軸上的分類區間,為一個陣列(array)。
  • datasets:為資料集,為一個陣列(array),裡面的結構為一個一個物件,每個物件都有 label 及 data 屬性。
    • label 為資料分類名稱
    • data 為要畫成柱狀的高度數值。
const dataObj = {
    // labels 為橫軸上的分類,為一個陣列
    labels: [
      "Mon",
      "Tue",
      "Wed",
      "Thur",
      "Fri",
      "Sat",
      "Sun",
    ],
    // datasets資料集為一個陣列,裡面的資料類型為一個一個物件
    datasets: [
      // 物件的屬性有 label 與 data
      {
        // label 為資料分類的名稱
        label: "鋼鐵人",
        //  data 為資料內容,是一個陣列,相同 index 的數值會放在同一個橫軸的分類區間
        data: [10,   20,   30,   50,   40,   30,   10],
      },
      {
        label: "美國隊長",
        data: [20,   30,   40,   40,   30,   20,   20],
      },
      {
        label: "蜘蛛人",
        data: [30,   40,   50,   30,   20,   10,   30],
      },
    ],
  };

資料陣列與圖表的比對:data 陣列裡同樣 index 的資料會放在相同的橫軸區間。

初始化(畫出圖表)

Chart.js 是以 canvas 的方式算繪,在這裡,我先設定填滿外容器(在建立圖表時,maintainAspectRatio要設為 false),所以外容器的寬高也要在 CSS 中設定好。

<canvas>也要設好 id 名稱,在這裡 id 為 heroChart。

<template>
  <div class="card-content-image">
    <canvas id="heroChart" ></canvas>
  </div>
</template>

接著在 onMounted 時,取得 canvas 的 DOM。

const ctx = document.getElementById("heroChart");

使用 Chart.js 給的 new Chart() 方法畫出圖表,第一個參數 ctx 為上面取得的 canvas DOM 元素,第二個參數則是圖表類型等相關設定,為一個物件結構。

  • type:為圖表類型,可以是 bar(長條圖)、line(折線圖)
  • data:放入上面定義的資料集 dataObj
  • options:視覺及版面配置選項
    • responsive:布林值,配合 CSS 設定自適應。
    • maintainAspectRatio:布林值,設為 false,才會跟隨外容器的長寬比例呈現。
    • plugins:設定圖例位置及標題。
onMounted(() => {
    const ctx = document.getElementById("heroChart");
    let chart = new Chart(ctx, {
      // type 可以是 bar,也可以是 line
      type: "bar",
      data: dataObj,
      options: {
        responsive: true,
        // 如果不設 maintainAspectRatio,寬度會固定
        maintainAspectRatio: false,
        plugins: {
          legend: {
            // datasets裡每一個物件會變成圖例,出現在給定的位置
            position: "top",
          },
          title: {
            display: true,
            text: "超級英雄出勤次數",
          },
        },
      },
    });
  });

完整程式碼

<script setup>
import Chart from 'chart.js/auto';
import { onMounted } from 'vue';

const dataObj = {
  // labels 為橫軸上的分類,為一個陣列
  labels: [
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
    'Sunday',
  ],
  // datasets資料集為一個陣列,裡面的資料類型為一個一個物件
  datasets: [
    // 物件的屬性有 label 與 data
    {
      // label 為資料分類的名稱
      label: '鋼鐵人',
      //  data 為資料內容,是一個陣列,相同 index 的數值會放在同一個橫軸的分類區間
      data: [10, 20, 30, 50, 40, 30, 10],
    },
    {
      label: '美國隊長',
      data: [20, 30, 40, 40, 30, 20, 20],
    },
    {
      label: '蜘蛛人',
      data: [30, 40, 50, 30, 20, 10, 30],
    },
  ],
};
onMounted(() => {
  const ctx = document.getElementById('heroChart');
  let chart = new Chart(ctx, {
    // type 可以是 bar,也可以是 line
    type: 'bar',
    data: dataObj,
    options: {
      responsive: true,
      // 如果不設 maintainAspectRatio,寬度會固定
      maintainAspectRatio: false,
      plugins: {
        legend: {
          // datasets裡每一個物件會變成圖例,出現在給定的位置
          position: 'top',
        },
        // 圖表標題
        title: {
          display: true,
          text: '超級英雄出勤次數',
        },
      },
    },
  });
});
</script>

<template>
  <div class="card-content-image">
    <canvas id="heroChart" width="100%" height="380"></canvas>
  </div>
</template>

<style scoped>
.card-content-image {
  width: 500px;
  height: 400px;
  border: 1px solid blue;
}

@media (max-width: 992px) {
  .card-content-image {
    width: 300px;
    height: 200px;
    border: 1px solid red;
  }
}
</style>

程式碼範例

參考資料

Leave a Reply

Your email address will not be published. Required fields are marked *