EChartsは、多様な種類のグラフを柔軟に作成できる強力なJavaScriptライブラリです。ここでは、基本的なグラフ作成を超えて、より高度でインタラクティブなデータ可視化を実現するためのテクニックについて解説します。
積み上げ面グラフによる時系列データの可視化
複数のカテゴリにわたる時系列データのトレンドとその構成比を同時に把握するには、積み上げ面グラフが非常に効果的です。以下に、週ごとの運用タスクの統計を示す積み上げ面グラフの実装例を示します。
const echarts = window.echarts; // EChartsライブラリのインスタンスを取得
const chartElement1 = document.getElementById('chartContainer1'); // チャート描画用DOM要素
if (!chartElement1) return;
const chartInstance1 = echarts.init(chartElement1, null, {
renderer: 'canvas', // レンダリングモードをCanvasに設定
useDirtyRect: false // ダーティ矩形レンダリングを無効化
});
const chartOptions1 = {
tooltip: {
trigger: 'axis', // ツールチップのトリガーを軸に設定
axisPointer: {
type: 'cross', // 十字型のポインタを表示
label: {
backgroundColor: '#6a7985'
}
}
},
title: {
text: '週次運用タスク統計', // チャートのメインタイトル
left: 'center'
},
legend: {
data: ['機器修理', '定期保守', '部品交換', '検査業務', '緊急対応'], // 凡例の項目
bottom: 0 // 凡例をチャートの下部に配置
},
grid: {
left: '3%',
right: '4%',
bottom: '10%', // 凡例とコンテンツが重ならないように調整
containLabel: true // ラベルがチャート領域内に収まるように調整
},
xAxis: {
type: 'category',
data: ['月曜', '火曜', '水曜', '木曜', '金曜', '土曜', '日曜'], // X軸のカテゴリデータ
boundaryGap: false // 軸の端に余白を作らない
},
yAxis: {
type: 'value' // Y軸を数値軸に設定
},
series: [
{
name: '機器修理',
type: 'line',
stack: '合計', // 同じスタックグループでグラフを積み上げる
areaStyle: {}, // 面スタイルを有効化し、面グラフとして表示
emphasis: { focus: 'series' }, // ホバー時に該当シリーズを強調表示
label: { show: true, position: 'top' }, // データポイントの値をラベルで表示
data: [120, 140, 110, 100, 130, 150, 140] // シリーズデータ
},
{
name: '定期保守',
type: 'line',
stack: '合計',
areaStyle: {},
emphasis: { focus: 'series' },
label: { show: true, position: 'top' },
data: [100, 110, 130, 150, 140, 120, 100]
},
{
name: '部品交換',
type: 'line',
stack: '合計',
areaStyle: {},
emphasis: { focus: 'series' },
label: { show: true, position: 'top' },
data: [90, 100, 120, 110, 160, 110, 80]
},
{
name: '検査業務',
type: 'line',
stack: '合計',
areaStyle: {},
emphasis: { focus: 'series' },
label: { show: true, position: 'top' },
data: [110, 160, 100, 120, 160, 120, 110]
},
{
name: '緊急対応',
type: 'line',
stack: '合計',
areaStyle: {},
emphasis: { focus: 'series' },
label: { show: true, position: 'top' },
data: [110, 110, 160, 130, 170, 190, 150]
}
]
};
chartInstance1.setOption(chartOptions1); // 設定オプションをチャートに適用
window.addEventListener('resize', chartInstance1.resize); // ウィンドウリサイズ時にチャートを再描画
円グラフ・ドーナツグラフによる比率分析
カテゴリ間の構成比を視覚的に表現するには、円グラフやドーナツグラフが最適です。以下は、タスクの進捗状況を美しいドーナツグラフで示す例です。padAngleやborderRadiusを利用して、セグメント間に視覚的な区切りを持たせています。
const chartElement2 = document.getElementById('chartContainer2'); // 2つ目のチャート用DOM要素
if (!chartElement2) return;
const chartInstance2 = echarts.init(chartElement2, null, {
renderer: 'canvas',
useDirtyRect: false
});
const chartOptions2 = {
title: {
text: 'タスク進捗状況', // チャートのメインタイトル
subtext: 'カテゴリ別', // サブタイトル
left: 'center'
},
tooltip: {
trigger: 'item', // ツールチップのトリガーをデータアイテムに設定
formatter: '{b}: {c} ({d}%)' // ツールチップの表示形式をカスタマイズ (名前: 値 (割合%))
},
legend: {
top: '80%', // 凡例をチャートの下部に配置
left: 'center'
},
series: [
{
name: '進捗詳細', // シリーズ名
type: 'pie', // 円グラフタイプ
center: ['50%', '45%'], // チャートの中心位置
radius: ['40%', '70%'], // ドーナツグラフの内径と外径
avoidLabelOverlap: false, // ラベルの重なりを避ける設定
padAngle: 5, // 各セグメント間の隙間(角度)
itemStyle: {
borderRadius: 10 // 各セグメントの角を丸くする
},
label: {
show: false, // 通常時はラベルを非表示
position: 'center'
},
emphasis: { // ホバー時の強調スタイル
label: {
show: true, // ホバー時にラベルを表示
fontSize: 30,
fontWeight: 'bold'
}
},
labelLine: {
show: false // ラベルとセグメントを結ぶ線を表示しない
},
data: [
{ value: 45, name: '完了(期日内)' },
{ value: 20, name: '完了(遅延)' },
{ value: 15, name: '進行中' },
{ value: 10, name: '未着手' },
{ value: 5, name: '中断中' }
]
}
]
};
chartInstance2.setOption(chartOptions2);
window.addEventListener('resize', chartInstance2.resize);
グラフの自動ツールチップ表示とインタラクティブ機能
ユーザーがチャートに触れていない間に、データポイントを自動的に巡回し、対応するツールチップを表示させることで、チャートの情報をより効果的に伝えることができます。また、マウスオーバー時には自動再生を一時停止し、マウスアウトで再開するといったインタラクティブな挙動も実装可能です。
// 自動ツールチップ表示を制御するタイマー関数
function createTooltipAnimator(chart, totalDataPoints, totalSeries) {
let currentDataIndex = 0; // 現在表示中のデータポイントのインデックス
let currentSeriesIndex = 0; // 現在表示中のシリーズのインデックス
return setInterval(() => {
chart.dispatchAction({
type: 'showTip', // ツールチップ表示アクション
seriesIndex: currentSeriesIndex,
dataIndex: currentDataIndex
});
// 次のデータポイントに移動。週末になったらシリーズを切り替える
currentDataIndex = (currentDataIndex + 1) % totalDataPoints;
if (currentDataIndex === 0) {
currentSeriesIndex = (currentSeriesIndex + 1) % totalSeries;
}
}, 1800); // 1.8秒ごとにツールチップを切り替える
}
const dataPointCount = 7; // X軸のカテゴリ数(例: 週の曜日数)
const seriesCount = 5; // 折れ線グラフのシリーズ数
let animationTimerId = null; // タイマーIDを保持する変数
let isMouseOverChart = false; // マウスがチャート上にあるかどうかのフラグ
// 初期状態として最初のデータポイントのツールチップを表示
chartInstance1.dispatchAction({ type: 'showTip', seriesIndex: 0, dataIndex: 0 });
// 自動アニメーションの開始
animationTimerId = createTooltipAnimator(chartInstance1, dataPointCount, seriesCount);
// チャート領域へのマウス移動イベントハンドリング
chartInstance1.getZr().on('mousemove', () => {
if (!isMouseOverChart) {
isMouseOverChart = true; // マウスがチャート上にある状態に設定
if (animationTimerId) {
clearInterval(animationTimerId); // 自動アニメーションを停止
animationTimerId = null;
}
}
});
// チャート領域からのマウスアウトイベントハンドリング
chartInstance1.getZr().on('mouseout', () => {
isMouseOverChart = false; // マウスがチャート上にない状態に設定
setTimeout(() => {
// 短い遅延後、マウスが再度チャート上に戻っていない場合のみアニメーションを再開
if (!isMouseOverChart && !animationTimerId) {
animationTimerId = createTooltipAnimator(chartInstance1, dataPointCount, seriesCount);
}
}, 400); // 400ミリ秒の遅延
});
// ウィンドウのフォーカス/ブラーイベントによるアニメーション制御
// ウィンドウが非アクティブになったらアニメーションを停止
window.addEventListener('blur', () => {
if (animationTimerId) {
clearInterval(animationTimerId);
animationTimerId = null;
}
});
// ウィンドウがアクティブに戻ったらアニメーションを再開
window.addEventListener('focus', () => {
// マウスがチャート上になく、タイマーが停止している場合のみ再開
if (!isMouseOverChart && !animationTimerId) {
animationTimerId = createTooltipAnimator(chartInstance1, dataPointCount, seriesCount);
}
});