Hi 🤓 Cảm ơn bạn đã ghé thăm blog này, nếu những bài viết trên blog giúp ích cho bạn. Bạn có thể giúp blog hiển thị quảng cáo bằng cách tạm ngừng ad blocker 😫 và để giúp blog duy trì hoạt động nếu bạn muốn.
Cảm ơn bạn!

Hello các bạn 🤩 rất lâu rồi mình mới làm một tutorial về JavaScript. Hôm nay chúng ta sẽ custom Chart.js, đây là thư viện về biểu đồ - Chart sử dụng JavaScript, cách sử dụng cũng khá đơn giản, và docs rõ ràng.

Sử dụng Chart.js chúng ta có thể đơn giản thêm các hiệu ứng cho biểu đồ, và đặc biệt nó support rất nhiều kiểu biểu đồ như: bar, line, area, pie, bubble, radar, polar,... trong tutorial này chúng ta sẽ cùng tìm hiểu về chart.js và cách custom nó nhé 🥳!

Custom Chart.js

Chuẩn bị

Trong tutorial này, mình sẽ sử dụng sass/scss để style. Các bạn có thể cài đặt bằng lệnh ở terminal:

npm install -g sass

Sau khi cài xong, khi thêm các style, sass sẽ recompile khi file thay đổi. Để làm được điều này các bạn sử dụng thêm lệnh sau ở terminal nhé 😁:

sass -w (hoặc --watch) style.scss style.css

Chúng ta sẽ viết style ở file style.scss và khi file này có thay đổi thì sass sẽ compile lại, xử lí sau đó đưa các style vào file style.css đơn giản vậy thôi ^^.

Ở file html thì chúng ta link file style.css, main.js và thêm source chart.js từ cdn vào nhé:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Chart.js by homiedev.com</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div class="wrapper">
      <h1>📊 Custom Chart.js by homiedev.com</h1>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="./main.js"></script>
  </body>
</html>

Các bạn lưu ý, chúng ta include chart.js trước tiên nhé, như vậy thì trong main.js ta mới có thể sử dụng các hàm, object từ chart.js

Docs của chart.js bạn đọc tại đây.

Thực hành

Đầu tiên chúng ta xem hình mẫu trước, nếu thành công chúng ta sẽ được như hình bên dưới nhé ^^.

custom chart.js

Để làm được như trên thì chúng ta sẽ sử dụng 2 chart type là line (bên trái hình) và bar (bên phải hình).

Cách tạo biểu đồ đơn giản như ví dụ sau, bạn có thể lên docs của chart.js để tìm hiểu thêm nhé:

<canvas id="myChart" width="400" height="400"></canvas>
<script>
const ctx = document.getElementById('myChart').getContext('2d');
const myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: ['Red', 'Blue', 'Yellow'],
        datasets: [{
            label: '# of Votes',
            data: [12, 19, 3],
            backgroundColor: [
                'rgba(255, 99, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)',
            ],
            borderColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)',
            ],
            borderWidth: 1
        }]
    },
});
</script>

Mình sẽ giải thích chi tiết phần code của bài viết này ngay sau đây ^^.

HTML

Trong file html, mình sẽ tạo trước 2 thẻ canvas. Thẻ canvas hiểu đơn giản là nơi chứa nội dung xây dựng từ JavaScript. Thư viện Chart.js sẽ tạo biểu đồ và đưa vào các thẻ canvas này.

Nội dung html sẽ đơn giản như sau 😄:

<head>
  <title>Chart.js by homiedev.com</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>
  <div class="wrapper">
    <h1>📊 Custom Chart.js by homiedev.com</h1>
    <div class="charts">
      <div class="chart">
        <canvas data-type="line" data-color-datalabels="#2e2a2a"></canvas>
      </div>
      <div class="chart">
        <canvas data-type="bar" data-color-datalabels="#ffffff"></canvas>
      </div>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <script src="./main.js"></script>
</body>

Trong các thẻ canvas mình sử dụng thuộc tính data-*="giá trị cần thêm", mục đích sử dụng thuộc tính này là để JavaScript có thể lấy được những giá trị custom. Khi lấy được thì chúng ta sẽ sử dụng giá trị từ data-* và thêm vào chart.js ^^.

JAVASCRIPT

Đến với phần JavaScript, ở bài hướng dẫn này có 2 file js đó là utils.jsmain.js. File main.js dùng để viết các phần code chính của chương trình, trong file utils.js (các hàm tiện ích), mình đã tạo sẵn 2 utils đó là MONTHrandomInteger dùng để random số trong khoảng cho trước:

// file utils.js
export const MONTHS = [
  "January",
  "February",
  "March",
  "April",
  "May",
  "June",
  "July",
  "August",
  "September",
  "October",
  "November",
  "December",
];

export function randomInteger(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

Khi chúng ta import các đối tượng từ file utils.js, sẽ xảy ra lỗi Uncaught SyntaxError: Cannot use import statement outside a module. Lỗi này đơn giản chỉ cần chúng ta thêm type='module' vào thẻ script như này: <script type="module" src="./main.js"></script>.

File main.js của chúng ta lúc này sẽ như sau:

// khi import sẽ xảy ra lỗi: Cannot use import statement outside a module
// fix bằng cách thêm type='module' vào thẻ script 😁
import { MONTHS, randomInteger } from "./utils.js";

const labels = MONTHS.slice(0, 6);

Đầu tiên chúng ta sẽ tạo labels, labels trong chart.js là tiêu đề của dữ liệu, ví dụ labels: tháng 1, tháng 2, tháng 3,... tương ứng với nó là dữ liệu người dùng, chẳng hạn tháng 1: 200, tháng 2: 400, tháng 3: 600,...

Mình muốn lấy 6 tháng đầu tiên của MONTHS thì sử dụng slice(start, end - không bao gồm). Như vậy là chúng ta có labels từ tháng 1 đến tháng 6 ^^.

Tiếp theo, chúng ta sẽ lấy các phần tử canvas đã tạo ở file html, sau đó ta chỉ việc sử dụng chart.js vào tạo biểu đồ cho 2 phần tử này ^^.

Để làm được thì chúng ta phải lấy được 2 phần tử này:

// các phần tử canvas sẽ nằm trong một mảng
const allCanvas = document.querySelectorAll("canvas");

Vì các elements nằm trong một mảng nên chúng ta sẽ sử dụng forEach để lặp qua các elements này. Cách sử dụng chart.js cơ bản là ta sẽ lấy phần tử sẽ chứa biểu đồ, truyền vào phần khởi tạo Chart như này:

<canvas id="myChart" width="400" height="400"></canvas>
<script>
  const element = document.getElementById('myChart');
  const myChart = new Chart(element, config);
</script>

Trong đó đối số đầu tiên là phần tử sẽ chứa biểu đồ, đối số thứ 2 là config sẽ chứa các phần custom như màu sắc của labels, type chart (bar, line,...), fonts, animations,... ví dụ:

const config = {
  type: 'line',
  data: data, 
  // data chứa labels và giá trị tương ứng, các custom khác,...
  options: {
    animations: {
      tension: {
        duration: 1000,
        easing: 'linear',
        from: 1,
        to: 0,
        loop: true
      }
    },
  }
};

Phần tạo biểu đồ bằng chart.js khá nhanh gọn, bây giờ chúng ta sẽ tạo biểu đồ cho 2 canvas nhé:

import { MONTHS, randomInteger } from "./utils.js";

const labels = MONTHS.slice(0, 6);

const allCanvas = document.querySelectorAll("canvas");

allCanvas.forEach((el) => {
  const { type, colorDatalabels } = el.dataset;

  const data = {
    labels: labels, // từ tháng 1 -> tháng 6
    datasets: [
      {
        label: "Analyze site traffic",
        backgroundColor: "rgb(255, 99, 132)",
        borderColor: "rgb(255, 99, 132)",
        // random data trong khoảng 100 -> 500 tương ứng tháng 1 -> tháng 6
        data: Array.from({ length: labels.length }).map(() =>
          randomInteger(100, 500)
        ),
        tension: 0.2,
      },
    ],
  };

  const config = {
    type: type,
    data: data,
  };

  // tạo biểu đồ
  new Chart(el, config);
});

Kết quả của chúng ta sẽ như sau:

homiedev-custom-chartjs-1

Như bạn thấy, chúng ta hover vô cột của biểu đồ (bar type) thì lúc này giá trị tương ứng của cột đó sẽ hiện ra. Để tiện hơn, chúng ta sẽ tìm cách custom để giá trị này hiện ra mà không cần hover vào.

Để làm được yêu cầu trên thì ta sẽ sử dụng thêm plugin của chart.js tên là: chartjs-plugin-datalabels. Các bạn import vào file html source của plugin này:

<body>
    ...

    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>
    <script type="module" src="./main.js"></script>
</body>

Một lưu ý cho các bạn là chartjs-plugin-datalabels phải được load sau thư viện Chart.js. Khi import qua thẻ <script>, chúng ta có thể sử dụng plugin này thông qua global property là ChartDataLabels.

Các bạn có thể tìm hiểu về plugin tại: chartjs-plugin-datalabels.

Để sử dụng plugin này cho tất cả các biểu đồ chúng ta sẽ thêm nó vào Chart:

Chart.register(ChartDataLabels);

Toàn bộ phần code JS sẽ như sau:

import { MONTHS, randomInteger } from "./utils.js";

Chart.register(ChartDataLabels);

const labels = MONTHS.slice(0, 6);

const allCanvas = document.querySelectorAll("canvas");

allCanvas.forEach((el) => {
  // lấy giá trị type và color-datalabels để thêm vào config chart.js
  // lấy từ data-type, data-color-datalabels trong file html
  // color-datalabels sẽ chuyển thành colorDatalabels
  const { type, colorDatalabels } = el.dataset;

  const data = {
    labels: labels,
    datasets: [
      {
        label: "Analyze site traffic",
        backgroundColor: "rgb(255, 99, 132)",
        borderColor: "rgb(255, 99, 132)",
        data: Array.from({ length: labels.length }).map(() =>
          randomInteger(100, 500)
        ),
        tension: 0.2,
      },
    ],
  };

  const config = {
    type: type,
    data: data,
    options: {
      aspectRatio: 16 / 9, // hiện biểu đồ theo tỉ lệ 16/9
      layout: {
        padding: 20,
      },
      plugins: {
        tooltip: {
          titleFont: {
            size: 22,
          },
          bodyFont: {
            size: 22,
          },
          callbacks: {
            // custom label khi hover
            label: (context) => `Total: ${context.formattedValue} users`,
          },
        },
        datalabels: {
          color: colorDatalabels,
          font: { size: 18 },
          formatter: function (value, context) {
            return value; // custom hiển thị label tại đây
          },
          display: function (context) {
            // ẩn label nếu cần thiết 
            // vì biểu đồ line có line vị trí đầu, cuối label bị che khuất
            if (type === "line")
              return (
                context.dataIndex > 0 && context.dataIndex < labels.length - 1
              );

            return true;
          },
        },
      },
    },
  };

  new Chart(el, config);
});

Đây là toàn bộ phần code custom chart.js 😄

Khi có nhiều labels, việc hiện thị trước các giá trị tương ứng có thể sẽ gây rối mắt, chúng ta có thể ẩn thuộc tính của chartjs-plugin-datalabels đi bằng cách:

const config = {
   options: {
     plugins: {
       datalabels: {
         display: false, // ẩn label
       },
     },
   },
 };

Như vậy là mình đã hướng dẫn cho các bạn cách custom chart.js và cách sử dụng plugin của nó 🥳. Hi vọng tutorial này giúp ích cho các bạn.

Nếu có thắc mắc gì các bạn có thể comment bên dưới phần bình luận nhé 👇.

Một số projects hay khác:

  1. Hướng dẫn làm reaction buttons sử dụng JavaScript
  2. Hướng dẫn làm Skeleton Loader sử dụng HTML/CSS/JAVASCRIPT
  3. Project Star Rating Systems sử dụng HTML/SASS/JAVASCRIPT
  4. Javascript OTP sử dụng HTML SCSS JAVASCRIPT
  5. Làm Dropdown Menu dễ dàng bằng Javascript
Có thể bạn thích ⚡
homiedev
About Me

Hi, I'm @devnav. Một người thích chia sẻ kiến thức, đặc biệt là về Frontend 🚀. Trang web này được tạo ra nhằm giúp các bạn học Frontend hiệu quả hơn 🎉😄.

Chúc các bạn tìm được kiến thức hữu ích trong blog này 😁😁.