Chart.js 互動式圖表入門教程
Chart.js 互動式圖表入門教程
第一部分:課程介紹與環境準備
課程簡介與 Chart.js 概覽
什麼是 Chart.js? Chart.js 是一個簡單而靈活的 JavaScript 圖表庫,基於 HTML5 Canvas 元素,使網頁開發者能夠輕鬆在網頁中創建各種互動式圖表。
為什麼選擇 Chart.js?
- 輕量級:檔案小,載入快速(壓縮後約 80KB)
- 易於使用:簡潔的 API,上手容易
- 響應式:圖表會自動調整大小以適應設備螢幕
- 模組化:可以只引入需要的模組,減少載入時間
- 內建互動性:預設具備工具提示、動畫和滑鼠懸停效果等互動功能
- 可擴展性:豐富的插件生態系統
環境準備與 Chart.js 引入
開始使用 Chart.js 需要兩個基本步驟:
- 在 HTML 中引入 Chart.js 庫
- 創建一個 Canvas 元素作為圖表的容器
基本 HTML 結構
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chart.js 入門</title>
<!-- 透過 CDN 引入 Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.chart-container {
width: 80%;
max-width: 700px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="chart-container">
<!-- Canvas 元素,Chart.js 會在此處繪製圖表 -->
<canvas id="myChart"></canvas>
</div>
<script>
// 我們的 Chart.js 程式碼將在這裡編寫
</script>
</body>
</html>
第二部分:Chart.js 核心概念與繪製第一個圖表
Chart.js 基本架構
Chart.js 的基本架構由以下幾個關鍵部分組成:
- Canvas Context:指定圖表繪製的 Canvas 元素
- Type:圖表類型(如 ‘bar’、’line’、‘pie’ 等)
- Data:圖表的數據,包含標籤和數據集
- Options:圖表的各種設定選項,包括標題、軸、圖例和互動性等
繪製第一個靜態圖表 - 長條圖 (Bar Chart)
以下是一個基本長條圖的示例,使用的是日經新聞分類統計數據:
// 取得 Canvas 元素
const ctx = document.getElementById('myChart').getContext('2d');
// 準備數據
const data = {
labels: ['經濟', '政治', '科技', '健康', '娛樂', '體育', '國際'],
datasets: [{
label: '日經新聞分類統計',
data: [100, 70, 95, 50, 25, 30, 85],
backgroundColor: 'rgba(255, 99, 132, 0.6)',
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 1
}]
};
// 建立圖表
const myChart = new Chart(ctx, {
type: 'bar', // 圖表類型:長條圖
data: data, // 數據
options: {} // 暫時留空的選項
});
添加圖表基本元素 - 標題、軸標籤、圖例
現在我們來為長條圖添加一些基本元素,讓它更加信息豐富:
// 取得 Canvas 元素
const ctx = document.getElementById('myChart').getContext('2d');
// 準備數據(與前面相同)
const data = {
labels: ['經濟', '政治', '科技', '健康', '娛樂', '體育', '國際'],
datasets: [{
label: '日經新聞分類統計',
data: [100, 70, 95, 50, 25, 30, 85],
backgroundColor: 'rgba(255, 99, 132, 0.6)',
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 1
}]
};
// 建立圖表,這次添加更多選項
const myChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '日經新聞分類統計',
font: {
size: 18
}
},
legend: {
position: 'top'
}
},
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: '文章數量'
}
},
x: {
title: {
display: true,
text: '新聞類別'
}
}
}
}
});
第三部分:繪製其他基本圖表類型
繪製線圖 (Line Chart)
線圖通常用於表示隨時間變化的數據。以下是使用 S&P 500 指數數據的線圖示例:
// 取得 Canvas 元素
const ctx = document.getElementById('lineChart').getContext('2d');
// 準備數據
const data = {
labels: ['2015', '2016', '2017', '2018', '2019', '2020', '2021', '2022', '2023', '2024'],
datasets: [{
label: 'S&P 500',
data: [2054, 2239, 2673, 2507, 3231, 3756, 4766, 3840, 4180, 5500],
fill: false,
backgroundColor: 'rgba(75, 192, 192, 0.6)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 2,
tension: 0.2 // 使線條略微平滑
}]
};
// 建立圖表
const lineChart = new Chart(ctx, {
type: 'line', // 圖表類型:線圖
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'S&P 500 指數走勢',
font: {
size: 18
}
}
},
scales: {
y: {
title: {
display: true,
text: '指數值'
}
},
x: {
title: {
display: true,
text: '年份'
}
}
}
}
});
繪製散點圖 (Scatter Chart)
散點圖非常適合展示兩個變量之間的關聯。以下是一個簡單的散點圖示例:
// 取得 Canvas 元素
const ctx = document.getElementById('scatterChart').getContext('2d');
// 準備數據 - 散點圖的數據格式與其他圖表不同
const data = {
datasets: [{
label: '國家發展指標',
data: [
{ x: 60, y: 98, r: 5.0 }, // r 代表點的大小
{ x: 45, y: 88, r: 12.0 },
{ x: 30, y: 75, r: 20.0 },
{ x: 78, y: 99, r: 2.5 },
{ x: 35, y: 80, r: 8.0 },
{ x: 55, y: 95, r: 3.5 }
],
backgroundColor: 'rgba(54, 162, 235, 0.6)',
borderColor: 'rgba(54, 162, 235, 1)'
}]
};
// 建立圖表
const scatterChart = new Chart(ctx, {
type: 'bubble', // 使用 bubble 而不是 scatter,因為我們有 r 值
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '國家發展指標對比',
font: {
size: 18
}
}
},
scales: {
y: {
title: {
display: true,
text: '識字率 (%)'
}
},
x: {
title: {
display: true,
text: '人均GDP (千美元)'
}
}
}
}
});
繪製餅圖與環圈圖 (Pie and Doughnut Charts)
餅圖和環圈圖適合用於展示部分對整體的比例關係。以下是一個簡單的環圈圖示例:
// 取得 Canvas 元素
const ctx = document.getElementById('doughnutChart').getContext('2d');
// 準備數據
const data = {
labels: ['台灣', '日本', '韓國', '中國', '美國', '歐盟'],
datasets: [{
label: '半導體市場份額',
data: [25, 18, 15, 12, 20, 10],
backgroundColor: [
'rgba(255, 99, 132, 0.7)',
'rgba(54, 162, 235, 0.7)',
'rgba(255, 206, 86, 0.7)',
'rgba(75, 192, 192, 0.7)',
'rgba(153, 102, 255, 0.7)',
'rgba(255, 159, 64, 0.7)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
};
// 建立圖表
const doughnutChart = new Chart(ctx, {
type: 'doughnut', // 圖表類型:環圈圖 (改為 'pie' 即可變成餅圖)
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '全球半導體市場份額分布',
font: {
size: 18
}
},
legend: {
position: 'right'
},
tooltip: {
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.parsed || 0;
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = Math.round((value / total) * 100);
return `${label}: ${percentage}% (${value}%)`;
}
}
}
}
}
});
繪製雷達圖 (Radar Chart)
雷達圖適合比較多個群組在不同維度上的表現。以下是一個展示不同編程語言特性的雷達圖:
// 取得 Canvas 元素
const ctx = document.getElementById('radarChart').getContext('2d');
// 準備數據
const data = {
labels: ['性能', '易用性', '社群支持', '工具生態', '跨平台', '學習曲線'],
datasets: [{
label: 'Python',
data: [70, 95, 90, 85, 80, 90],
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255, 99, 132, 1)',
pointBackgroundColor: 'rgba(255, 99, 132, 1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(255, 99, 132, 1)'
}, {
label: 'JavaScript',
data: [65, 90, 95, 95, 100, 75],
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
pointBackgroundColor: 'rgba(54, 162, 235, 1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(54, 162, 235, 1)'
}, {
label: 'Go',
data: [95, 75, 70, 65, 80, 75],
backgroundColor: 'rgba(255, 206, 86, 0.2)',
borderColor: 'rgba(255, 206, 86, 1)',
pointBackgroundColor: 'rgba(255, 206, 86, 1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(255, 206, 86, 1)'
}]
};
// 建立圖表
const radarChart = new Chart(ctx, {
type: 'radar', // 圖表類型:雷達圖
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '程式語言比較',
font: {
size: 18
}
}
},
scales: {
r: {
angleLines: {
display: true
},
suggestedMin: 0,
suggestedMax: 100
}
}
}
});
第四部分:Chart.js 的互動性
理解內建互動功能
Chart.js 的一個關鍵特性是它內建的互動功能,這些功能讓您的圖表更加生動和信息豐富。
主要互動功能包括:
-
滑鼠懸停效果 (Hover):當用戶將滑鼠移到圖表元素上時,該元素會有視覺反饋(如高亮顯示)。這是 Chart.js 的預設行為。
-
工具提示 (Tooltip):當用戶懸停在數據點上時,會顯示一個包含詳細信息的小彈窗。工具提示是展示詳細數據的重要方式。
-
圖例交互:用戶可以點擊圖例來切換數據集的顯示與隱藏。
讓我們看看如何強化這些互動功能:
// 以長條圖為例,添加互動功能
const myChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '日經新聞分類統計',
font: { size: 18 }
},
// 自訂工具提示
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
titleFont: { size: 14 },
bodyFont: { size: 12 },
displayColors: false, // 不顯示小色塊
callbacks: {
label: function(context) {
return `文章數量: ${context.parsed.y}`;
}
}
}
},
// 設定懸停效果
hover: {
mode: 'index', // 懸停在特定索引上
intersect: false // 不需要直接懸停在點上
},
scales: {
y: {
beginAtZero: true,
title: { display: true, text: '文章數量' }
},
x: {
title: { display: true, text: '新聞類別' }
}
}
}
});
在範例中體驗互動
比特幣價格走勢圖表(使用蠟燭圖)
以下是一個更複雜的例子,展示了比特幣價格的蠟燭圖,這種圖表類型本身就具有很強的互動性:
// 取得 Canvas 元素
const ctx = document.getElementById('candlestickChart').getContext('2d');
// 準備數據
const data = {
labels: [
'2024-1-15', '2024-1-30', '2024-2-15', '2024-2-30', '2024-3-15',
'2024-3-30', '2024-4-15', '2024-4-30', '2024-5-15', '2024-5-30'
],
datasets: [{
label: '比特幣/美元',
data: [
{ t: '2024-01-15', o: 42000, h: 43200, l: 41500, c: 42800 },
{ t: '2024-01-30', o: 42800, h: 44500, l: 42500, c: 43900 },
{ t: '2024-02-15', o: 43900, h: 45000, l: 43200, c: 44200 },
{ t: '2024-02-29', o: 44200, h: 46700, l: 44000, c: 45800 },
{ t: '2024-03-15', o: 45800, h: 47500, l: 45300, c: 47000 },
{ t: '2024-03-30', o: 47000, h: 48800, l: 46200, c: 48500 },
{ t: '2024-04-15', o: 48500, h: 52000, l: 48200, c: 51500 },
{ t: '2024-04-30', o: 51500, h: 53500, l: 51000, c: 52800 },
{ t: '2024-05-15', o: 52800, h: 54000, l: 51500, c: 53000 },
{ t: '2024-05-30', o: 53000, h: 56000, l: 52000, c: 55000 }
]
}]
};
// 由於標準 Chart.js 沒有內建蠟燭圖,這裡我們模擬一個互動效果
const candlestickChart = new Chart(ctx, {
type: 'bar', // 用長條圖模擬蠟燭圖
data: {
labels: data.labels,
datasets: [{
label: '比特幣/美元 價格範圍',
backgroundColor: ctx => {
// 依據開盤價和收盤價決定顏色
const value = data.datasets[0].data[ctx.dataIndex];
return value.o > value.c ? 'rgba(255, 99, 132, 0.8)' : 'rgba(75, 192, 192, 0.8)';
},
data: data.datasets[0].data.map(d => d.h - d.l), // 高低差
barPercentage: 0.4
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '比特幣/美元 價格走勢',
font: { size: 18 }
},
tooltip: {
callbacks: {
label: function(context) {
const dataIndex = context.dataIndex;
const value = data.datasets[0].data[dataIndex];
return [
`開盤: $${value.o}`,
`最高: $${value.h}`,
`最低: $${value.l}`,
`收盤: $${value.c}`
];
}
}
}
},
scales: {
y: {
title: {
display: true,
text: '價格範圍 (USD)'
}
},
x: {
title: {
display: true,
text: '日期'
}
}
}
}
});
第五部分:Chart.js 動畫與效果
動畫基礎與配置
Chart.js 內建了豐富的動畫效果,讓圖表在載入時更加生動。您可以配置這些動畫的持續時間、緩動函數等:
// 以長條圖為例,添加動畫效果
const myChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
// 動畫配置
animation: {
duration: 2000, // 動畫持續時間(毫秒)
easing: 'easeOutBounce', // 緩動函數
delay: 500 // 延遲開始動畫的時間
},
// 可以為不同的動畫類型單獨配置
animations: {
colors: {
type: 'color',
duration: 2000
},
y: {
type: 'number',
from: 0 // 從 0 開始動畫
}
},
plugins: {
title: {
display: true,
text: '日經新聞分類統計',
font: { size: 18 }
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
漸變色彩效果
您可以使用漸變色彩,為長條圖或其他圖表添加更吸引人的視覺效果:
// 取得 Canvas 元素
const ctx = document.getElementById('gradientChart').getContext('2d');
// 創建漸變色
const gradient = ctx.createLinearGradient(0, 0, 0, 400);
gradient.addColorStop(0, 'rgba(255, 0, 0, 0.8)');
gradient.addColorStop(0.5, 'rgba(255, 150, 0, 0.8)');
gradient.addColorStop(1, 'rgba(255, 255, 0, 0.8)');
// 準備數據
const data = {
labels: ['一月', '二月', '三月', '四月', '五月', '六月'],
datasets: [{
label: '月度銷售額',
data: [125, 190, 160, 175, 200, 230],
backgroundColor: gradient,
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 1
}]
};
// 建立圖表
const gradientChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '月度銷售額 (漸變色效果)',
font: { size: 18 }
}
},
// 添加漸進動畫
animation: {
y: {
duration: 2000,
from: 0
}
}
}
});
第六部分:進階使用技巧
資料刷新與動態更新
在實際應用中,您可能需要定期更新圖表數據,以下是一個動態更新圖表的示例:
// 取得 Canvas 元素
const ctx = document.getElementById('dynamicChart').getContext('2d');
// 初始數據
const initialData = {
labels: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'],
datasets: [{
label: '即時數據',
data: [65, 59, 80, 81, 56, 55, 40, 45, 60, 70],
fill: false,
borderColor: 'rgba(75, 192, 192, 1)',
tension: 0.1
}]
};
// 建立圖表
const dynamicChart = new Chart(ctx, {
type: 'line',
data: initialData,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '即時數據監控',
font: { size: 18 }
}
},
scales: {
y: {
min: 0,
max: 100
}
}
}
});
// 模擬數據更新
function updateChart() {
// 移除第一個數據點
dynamicChart.data.labels.shift();
dynamicChart.data.datasets[0].data.shift();
// 添加新數據點
const newLabel = (parseInt(dynamicChart.data.labels[dynamicChart.data.labels.length - 1]) + 1).toString();
dynamicChart.data.labels.push(newLabel);
const newData = Math.floor(Math.random() * 100);
dynamicChart.data.datasets[0].data.push(newData);
// 更新圖表
dynamicChart.update();
}
// 每 2 秒更新一次圖表
setInterval(updateChart, 2000);
使用事件處理程序
Chart.js 支持多種事件,可以讓您的圖表與用戶互動:
// 取得 Canvas 元素
const ctx = document.getElementById('eventChart').getContext('2d');
// 準備數據
const data = {
labels: ['台北', '台中', '高雄', '台南', '新竹', '花蓮'],
datasets: [{
label: '城市人口 (萬人)',
data: [270, 280, 280, 190, 45, 33],
backgroundColor: 'rgba(54, 162, 235, 0.6)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
};
// 建立圖表
const eventChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '台灣主要城市人口',
font: { size: 18 }
}
}
}
});
// 添加點擊事件處理器
document.getElementById('eventChart').onclick = function(evt) {
const points = eventChart.getElementsAtEventForMode(evt, 'nearest', { intersect: true }, true);
if (points.length) {
const firstPoint = points[0];
const label = eventChart.data.labels[firstPoint.index];
const value = eventChart.data.datasets[firstPoint.datasetIndex].data[firstPoint.index];
alert(`您點擊了 ${label},人口數為 ${value} 萬人`);
}
};
複合圖表與多數據集
在同一個圖表中顯示不同類型的圖表或多個數據集,可以提供更全面的數據視角:
// 取得 Canvas 元素
const ctx = document.getElementById('mixedChart').getContext('2d');
// 準備數據
const data = {
labels: ['一月', '二月', '三月', '四月', '五月', '六月'],
datasets: [
{
type: 'bar',
label: '銷售額',
data: [110, 148, 124, 128, 138, 141],
backgroundColor: 'rgba(75, 192, 192, 0.6)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1,
yAxisID: 'y'
},
{
type: 'line',
label: '利潤率(%)',
data: [19.4, 19.5, 22.0, 19.0, 23.5, 28.1],
backgroundColor: 'transparent',
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 2,
yAxisID: 'y1'
}
]
};
// 建立圖表
const mixedChart = new Chart(ctx, {
type: 'bar', // 基本類型,但每個數據集可以有自己的類型
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '銷售額與利潤率對比',
font: { size: 18 }
},
tooltip: {
mode: 'index',
intersect: false
}
},
scales: {
y: {
position: 'left',
title: {
display: true,
text: '銷售額'
}
},
y1: {
position: 'right',
title: {
display: true,
text: '利潤率(%)'
},
grid: {
drawOnChartArea: false // 只有左軸的網格線
},
min: 0,
max: 50
}
}
}
});
第七部分:外部數據整合
從 JSON 檔案載入數據
在實際應用中,圖表數據通常來自外部 API 或檔案。以下是從 JSON 檔案載入數據的示例:
// 取得 Canvas 元素
const ctx = document.getElementById('jsonDataChart').getContext('2d');
// 從 JSON 檔案載入數據
fetch('data/sales-data.json')
.then(response => response.json())
.then(jsonData => {
// 準備圖表數據
const data = {
labels: jsonData.map(item => item.month),
datasets: [{
label: '月度銷售額',
data: jsonData.map(item => item.sales),
backgroundColor: 'rgba(54, 162, 235, 0.6)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
};
// 建立圖表
const jsonDataChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '從 JSON 檔案載入的銷售數據',
font: { size: 18 }
}
}
}
});
})
.catch(error => console.error('載入數據失敗:', error));
使用 Fetch API 從外部 API 獲取數據
以下是一個從外部 API 獲取數據並生成圖表的例子:
// 取得 Canvas 元素
const ctx = document.getElementById('apiDataChart').getContext('2d');
// 從 API 獲取數據
async function fetchStockData() {
try {
// 替換為實際的 API 網址
const response = await fetch('https://api.example.com/stocks/AAPL/history');
const data = await response.json();
// 處理數據
const labels = data.map(item => item.date);
const prices = data.map(item => item.closePrice);
// 創建圖表
const stockChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'AAPL 股票價格',
data: prices,
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1,
pointRadius: 2,
tension: 0.1
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'AAPL 股票價格走勢',
font: { size: 18 }
}
},
scales: {
x: {
title: {
display: true,
text: '日期'
}
},
y: {
title: {
display: true,
text: '價格 (USD)'
}
}
}
}
});
} catch (error) {
console.error('獲取股票數據失敗:', error);
document.getElementById('apiDataChart').parentNode.innerHTML = '<p>無法載入股票數據</p>';
}
}
// 執行函數
fetchStockData();
從 CSV 檔案載入數據
使用外部函式庫 PapaParse 來處理 CSV 數據:
<!-- 在 HTML 檔案中引入 PapaParse -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.0/papaparse.min.js"></script>
// 取得 Canvas 元素
const ctx = document.getElementById('csvDataChart').getContext('2d');
// 載入並解析 CSV 檔案
Papa.parse('data/temperature-data.csv', {
download: true,
header: true,
dynamicTyping: true,
complete: function(results) {
// 處理數據
const data = results.data;
const labels = data.map(row => row.date);
const temperatures = data.map(row => row.temperature);
// 創建圖表
const temperatureChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '每日平均溫度',
data: temperatures,
backgroundColor: 'rgba(255, 159, 64, 0.2)',
borderColor: 'rgba(255, 159, 64, 1)',
borderWidth: 1,
fill: true
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '每日平均溫度變化',
font: { size: 18 }
}
},
scales: {
y: {
title: {
display: true,
text: '溫度 (°C)'
}
}
}
}
});
}
});
第八部分:Chart.js 插件系統
常用插件介紹
Chart.js 有豐富的插件生態系統,可以擴展其功能。以下是一些常用插件:
- chartjs-plugin-datalabels:在圖表上直接顯示數據標籤
- chartjs-plugin-zoom:添加縮放和平移功能
- chartjs-plugin-annotation:添加註釋,如線條、框和點
- chartjs-plugin-colorschemes:預定義的顏色配色方案
使用 chartjs-plugin-datalabels 示例
首先在 HTML 中引入插件:
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>
然後在 JavaScript 代碼中使用:
// 註冊插件(全局)
Chart.register(ChartDataLabels);
// 取得 Canvas 元素
const ctx = document.getElementById('datalabelsChart').getContext('2d');
// 準備數據
const data = {
labels: ['一月', '二月', '三月', '四月', '五月', '六月'],
datasets: [{
label: '銷售額',
data: [80, 92, 75, 110, 150, 125],
backgroundColor: 'rgba(75, 192, 192, 0.6)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
};
// 建立圖表
const datalabelsChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '帶數據標籤的銷售圖表',
font: { size: 18 }
},
// 數據標籤配置
datalabels: {
color: '#000',
anchor: 'end',
align: 'top',
formatter: Math.round,
font: {
weight: 'bold'
}
}
}
}
});
使用 chartjs-plugin-zoom 示例
首先引入插件:
<script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@1.1.1"></script>
然後在 JavaScript 代碼中使用:
// 取得 Canvas 元素
const ctx = document.getElementById('zoomChart').getContext('2d');
// 準備數據 - 生成更多的數據點
const labels = Array.from({ length: 100 }, (_, i) => i + 1);
const dataPoints = Array.from({ length: 100 }, () => Math.random() * 100);
// 建立圖表
const zoomChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '隨機數據',
data: dataPoints,
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1,
pointRadius: 0, // 不顯示點,提高性能
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '可縮放和平移的圖表',
font: { size: 18 }
},
zoom: {
pan: {
enabled: true,
mode: 'xy'
},
zoom: {
wheel: {
enabled: true
},
pinch: {
enabled: true
},
mode: 'xy'
}
}
}
}
});
// 添加重置縮放按鈕
document.getElementById('resetZoom').onclick = function() {
zoomChart.resetZoom();
};
在 HTML 中添加重置按鈕:
<button id="resetZoom">重置縮放</button>
第九部分:響應式設計與移動設備優化
響應式基礎配置
Chart.js 默認提供響應式行為,但您可以進一步優化:
const responsiveChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
maintainAspectRatio: false, // 不維持原始寬高比
aspectRatio: 2, // 設定寬高比(如果 maintainAspectRatio 為 true)
// 根據螢幕大小調整選項
plugins: {
legend: {
position: window.innerWidth < 600 ? 'bottom' : 'top'
}
},
// 針對小螢幕優化
scales: {
x: {
ticks: {
autoSkip: true, // 自動跳過標籤,避免過度擁擠
maxRotation: 45, // 旋轉角度
minRotation: 45 // 最小旋轉角度
}
}
}
}
});
// 監聽視窗大小變化
window.addEventListener('resize', function() {
// 動態調整圖表選項
responsiveChart.options.plugins.legend.position = window.innerWidth < 600 ? 'bottom' : 'top';
responsiveChart.update();
});
觸控事件優化
對於移動設備,可以優化觸控體驗:
const touchChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
responsive: true,
// 優化工具提示顯示
plugins: {
tooltip: {
mode: 'index', // 顯示全部同一位置的數據點
intersect: false, // 不需要直接點擊數據點
// 在觸控設備上更大的工具提示
bodyFont: {
size: 16 // 更大的字體,方便觸控設備閱讀
},
padding: 12 // 更大的內邊距
},
legend: {
// 更大的點擊區域
labels: {
boxWidth: 30,
padding: 20
}
}
},
// 更大的點,方便點擊
elements: {
point: {
radius: 6, // 默認大小
hoverRadius: 12 // 懸停時更大
}
}
}
});
第十部分:資源與總結
利用資源進行進一步學習
官方資源:
- Chart.js 官方文件:https://www.chartjs.org/docs/latest/
- Chart.js 範例:https://www.chartjs.org/samples/latest/
- GitHub 倉庫:https://github.com/chartjs/Chart.js
其他實用資源:
- Stack Overflow 上的 Chart.js 標籤
- 各種教學網站上的 Chart.js 教程
- CodePen 和 JSFiddle 上的範例
常見問題與解決方案
1. 圖表不顯示?
- 檢查 Canvas 元素是否存在
- 確認數據格式是否正確
- 檢查控制台錯誤信息
2. 響應式行為不如預期?
- 確認父容器的樣式設置
- 檢查
responsive
和maintainAspectRatio
選項 - 如果使用 hidden 或 display:none,需要手動更新圖表
3. 自定義工具提示不起作用?
- 確認 callback 函數的格式是否正確
- 注意 Chart.js 版本差異,API 可能有變化
最終項目:完整的數據儀表板
整合所學內容,創建一個小型數據儀表板:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chart.js 數據儀表板</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 20px;
background-color: #f8f9fa;
}
.dashboard {
max-width: 1200px;
margin: 0 auto;
}
.dashboard-header {
text-align: center;
margin-bottom: 30px;
}
.chart-container {
background: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 20px;
margin-bottom: 20px;
}
.chart-row {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-bottom: 20px;
}
.chart-item {
flex: 1;
min-width: 300px;
height: 300px;
}
@media (max-width: 768px) {
.chart-item {
min-width: 100%;
}
}
</style>
</head>
<body>
<div class="dashboard">
<div class="dashboard-header">
<h1>銷售數據儀表板</h1>
</div>
<div class="chart-row">
<div class="chart-container chart-item">
<canvas id="salesChart"></canvas>
</div>
<div class="chart-container chart-item">
<canvas id="categoriesChart"></canvas>
</div>
</div>
<div class="chart-row">
<div class="chart-container chart-item">
<canvas id="regionChart"></canvas>
</div>
<div class="chart-container chart-item">
<canvas id="trendsChart"></canvas>
</div>
</div>
</div>
<script>
// 模擬數據
const monthlySales = {
labels: ['一月', '二月', '三月', '四月', '五月', '六月'],
datasets: [{
label: '銷售額 (萬元)',
data: [120, 190, 160, 210, 175, 225],
backgroundColor: 'rgba(75, 192, 192, 0.6)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
};
const categoryData = {
labels: ['電子產品', '服裝', '食品', '家居', '運動'],
datasets: [{
label: '類別銷售佔比',
data: [45, 25, 15, 10, 5],
backgroundColor: [
'rgba(255, 99, 132, 0.7)',
'rgba(54, 162, 235, 0.7)',
'rgba(255, 206, 86, 0.7)',
'rgba(75, 192, 192, 0.7)',
'rgba(153, 102, 255, 0.7)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)'
],
borderWidth: 1
}]
};
const regionData = {
labels: ['北', '中', '南', '東'],
datasets: [{
label: '區域銷售額',
data: [300, 240, 190, 75],
backgroundColor: [
'rgba(255, 159, 64, 0.7)',
'rgba(75, 192, 192, 0.7)',
'rgba(54, 162, 235, 0.7)',
'rgba(153, 102, 255, 0.7)'
],
borderWidth: 1
}]
};
const trendData = {
labels: ['一月', '二月', '三月', '四月', '五月', '六月'],
datasets: [{
label: '網路銷售',
data: [70, 90, 100, 120, 130, 140],
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.1)',
fill: true
}, {
label: '實體銷售',
data: [50, 100, 60, 90, 45, 85],
borderColor: 'rgba(54, 162, 235, 1)',
backgroundColor: 'rgba(54, 162, 235, 0.1)',
fill: true
}]
};
// 創建圖表
const salesChart = new Chart(
document.getElementById('salesChart').getContext('2d'),
{
type: 'bar',
data: monthlySales,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '月度銷售額',
font: { size: 16 }
}
}
}
}
);
const categoriesChart = new Chart(
document.getElementById('categoriesChart').getContext('2d'),
{
type: 'doughnut',
data: categoryData,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '類別銷售佔比',
font: { size: 16 }
},
tooltip: {
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.parsed || 0;
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = Math.round((value / total) * 100);
return `${label}: ${percentage}%`;
}
}
}
}
}
}
);
const regionChart = new Chart(
document.getElementById('regionChart').getContext('2d'),
{
type: 'polarArea',
data: regionData,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '區域銷售分布',
font: { size: 16 }
}
}
}
}
);
const trendsChart = new Chart(
document.getElementById('trendsChart').getContext('2d'),
{
type: 'line',
data: trendData,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '銷售趨勢對比',
font: { size: 16 }
}
},
interaction: {
mode: 'index',
intersect: false
}
}
}
);
</script>
</body>
</html>
課程總結
在這個擴展版的 Chart.js 課程中,我們學習了:
-
Chart.js 的基本概念和優勢
- 輕量級、易於使用、靈活性強
- 內建互動功能,增強用戶體驗
-
環境設置和基本架構
- 如何引入 Chart.js
- Canvas 元素的重要性
- Chart 物件的基本結構
-
各種圖表類型的使用
- 長條圖 (Bar Chart)
- 線圖 (Line Chart)
- 散點/氣泡圖 (Scatter/Bubble Chart)
- 餅圖/環圈圖 (Pie/Doughnut Chart)
- 雷達圖 (Radar Chart)
- 極區圖 (Polar Area Chart)
-
動態數據與互動性
- 數據即時更新
- 用戶互動事件處理
- 自定義工具提示
-
外部數據整合
- 從 JSON、CSV 和 API 載入數據
-
使用插件擴展功能
- 數據標籤
- 縮放和平移
- 其他常用插件
-
響應式設計與移動設備優化
- 適應不同螢幕大小
- 觸控體驗優化
-
進階應用場景
- 儀表板設計
- 混合圖表
這只是 Chart.js 強大功能的開始。通過持續實踐和探索,您可以創建出更加複雜、美觀、交互性強的數據可視化作品!
課後練習
- 嘗試將長條圖的顏色改為漸變色
- 為線圖添加動畫效果
- 創建一個餅圖或雷達圖
- 嘗試使用真實數據(如 CSV 檔案或 API)載入數據
- 使用 Chart.js 插件添加更多功能(如數據標籤或縮放功能)
- 搭建一個包含多個圖表的儀表板
- 實現一個能夠動態新增、修改數據的互動式圖表
- 嘗試整合第三方數據源,如氣象 API 或股票數據