Chart.js アノテーション完全ガイド
Chart.jsのアノテーション機能を利用して、データの可視化を効果的に行うための包括的なガイドです。このドキュメントでは、基本概念から高度なテクニックまでを網羅し、実際のビジネスシーンでの活用例も紹介します。すべてのアノテーション例には、動作するサンプルグラフを掲載しています。
1. 基本概念
アノテーションとは、グラフ上に特定の情報を視覚的に示すための追加要素です。これにより、データの重要なポイントや傾向を強調できます。
必要なライブラリ
import Chart from 'chart.js/auto';
import annotationPlugin from 'chartjs-plugin-annotation';
Chart.register(annotationPlugin);
注:このガイドではCDNを使用しているため、HTMLの<script>タグでライブラリを読み込んでいます。
基本構造
const config = {
type: 'line',
data: { /* データ設定 */ },
options: {
plugins: {
annotation: {
annotations: {
myAnnotation: {
// アノテーションの設定
}
}
}
}
}
};
2. 縦線アノテーション
特定の日付や時点を示すのに最適です。以下の例では、基本的な縦線、スタイル付き縦線、高度な設定の縦線を示します。
const verticalLineConfig = {
type: 'line',
data: {
labels: ['2020-01', '2020-03', '2020-06', '2020-09', '2020-12'],
datasets: [{
label: 'データ',
data: [50, 75, 100, 125, 150],
borderColor: '#007BFF',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
tension: 0.4
}]
},
options: {
plugins: {
annotation: {
annotations: {
basicVerticalLine: {
type: 'line',
scaleID: 'x',
value: '2020-03',
borderColor: '#FF0000',
borderWidth: 2
},
styledVerticalLine: {
type: 'line',
scaleID: 'x',
value: '2020-06',
borderColor: '#3366CC',
borderWidth: 3,
borderDash: [10, 5],
borderDashOffset: 0,
shadowColor: 'rgba(51, 102, 204, 0.3)',
shadowBlur: 8,
shadowOffsetX: 2,
shadowOffsetY: 2,
label: {
enabled: true,
content: 'イベント発生日',
position: 'start',
backgroundColor: '#3366CC',
color: 'white',
padding: 8,
borderRadius: 4,
font: {
size: 14,
weight: 'bold',
family: 'Arial'
}
}
},
advancedVerticalLine: {
type: 'line',
scaleID: 'x',
value: '2020-09',
display: (ctx) => ctx.chart.data.datasets.length > 0,
borderColor: (ctx) => {
const chart = ctx.chart;
const {ctx: canvasCtx, chartArea} = chart;
if (!chartArea) return '#FF0000';
const gradient = canvasCtx.createLinearGradient(0, chartArea.top, 0, chartArea.bottom);
gradient.addColorStop(0, '#FF0000');
gradient.addColorStop(1, '#FF6666');
return gradient;
},
borderWidth: 4,
enter: ({element, chart}) => {
element.options.borderWidth = 6;
chart.update('none');
},
leave: ({element, chart}) => {
element.options.borderWidth = 4;
chart.update('none');
}
}
}
}
}
}
};
3. 横線アノテーション
目標値や閾値を示すのに便利です。以下の例では、基本的な横線、目標値ライン、警告ラインを示します。
const horizontalLineConfig = {
type: 'line',
data: {
labels: ['2020-01', '2020-02', '2020-03', '2020-04', '2020-05'],
datasets: [{
label: 'データ',
data: [50, 100, 75, 150, 120],
borderColor: '#007BFF',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
tension: 0.4
}]
},
options: {
plugins: {
annotation: {
annotations: {
basicHorizontalLine: {
type: 'line',
scaleID: 'y',
value: 100,
borderColor: '#00AA00',
borderWidth: 2
},
targetLine: {
type: 'line',
scaleID: 'y',
value: 150,
borderColor: '#FF9900',
borderWidth: 3,
borderDash: [5, 5],
xMin: '2020-01',
xMax: '2020-05',
label: {
enabled: true,
content: '目標値: 150',
position: 'end',
xAdjust: -10,
backgroundColor: '#FF9900',
color: 'white',
padding: 6,
font: {
size: 12,
weight: 'bold'
}
}
},
warningLow: {
type: 'line',
scaleID: 'y',
value: 30,
borderColor: '#FF3333',
borderWidth: 2,
borderDash: [3, 3],
label: {
enabled: true,
content: '⚠️ 警告レベル(低)',
position: 'start',
backgroundColor: 'rgba(255, 51, 51, 0.9)',
color: 'white'
}
},
warningHigh: {
type: 'line',
scaleID: 'y',
value: 200,
borderColor: '#FF3333',
borderWidth: 2,
borderDash: [3, 3],
label: {
enabled: true,
content: '⚠️ 警告レベル(高)',
position: 'start',
backgroundColor: 'rgba(255, 51, 51, 0.9)',
color: 'white'
}
}
}
}
}
}
};
4. 矩形エリア
期間や範囲を強調するのに最適です。以下の例では、基本的な矩形、スタイル豊富な矩形、インタラクティブな矩形を示します。
const boxAreaConfig = {
type: 'line',
data: {
labels: ['2020-01', '2020-03', '2020-06', '2020-09', '2020-12'],
datasets: [{
label: 'データ',
data: [50, 75, 100, 125, 150],
borderColor: '#007BFF',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
tension: 0.4
}]
},
options: {
plugins: {
annotation: {
annotations: {
basicBox: {
type: 'box',
xMin: '2020-03',
xMax: '2020-06',
yMin: 50,
yMax: 150,
backgroundColor: 'rgba(255, 0, 0, 0.1)',
borderColor: '#FF0000',
borderWidth: 2
},
styledBox: {
type: 'box',
xMin: '2020-06',
xMax: '2020-09',
yMin: 0,
yMax: 200,
backgroundColor: (ctx) => {
const chart = ctx.chart;
const {ctx: canvasCtx, chartArea} = chart;
if (!chartArea) return 'rgba(0, 123, 255, 0.1)';
const gradient = canvasCtx.createLinearGradient(
chartArea.left, 0, chartArea.right, 0
);
gradient.addColorStop(0, 'rgba(0, 123, 255, 0.05)');
gradient.addColorStop(1, 'rgba(0, 123, 255, 0.2)');
return gradient;
},
borderColor: '#007BFF',
borderWidth: 3,
borderDash: [8, 4],
borderRadius: 8,
borderSkipped: false,
shadowColor: 'rgba(0, 123, 255, 0.3)',
shadowBlur: 10,
shadowOffsetX: 3,
shadowOffsetY: 3,
label: {
enabled: true,
content: ['夏季キャンペーン', '期間限定'],
position: { x: 'center', y: 'center' },
backgroundColor: '#007BFF',
color: 'white',
padding: 12,
borderRadius: 6,
font: { size: 14, weight: 'bold' },
rotation: 0
}
},
interactiveBox: {
type: 'box',
xMin: '2020-09',
xMax: '2020-12',
yMin: 75,
yMax: 125,
backgroundColor: 'rgba(40, 167, 69, 0.1)',
borderColor: '#28A745',
borderWidth: 2,
click: ({element, chart}) => {
alert('秋季プロモーション期間がクリックされました!');
},
enter: ({element, chart}) => {
element.options.backgroundColor = 'rgba(40, 167, 69, 0.3)';
element.options.borderWidth = 4;
chart.update('none');
},
leave: ({element, chart}) => {
element.options.backgroundColor = 'rgba(40, 167, 69, 0.1)';
element.options.borderWidth = 2;
chart.update('none');
}
}
}
}
}
}
};
5. 楕円・円
特定のポイントを目立たせるのに効果的です。以下の例では、基本的な楕円、完全な円、アニメーション付き楕円を示します。
const ellipseCircleConfig = {
type: 'line',
data: {
labels: ['2020-01', '2020-04', '2020-07', '2020-10', '2020-12'],
datasets: [{
label: 'データ',
data: [50, 80, 100, 120, 150],
borderColor: '#007BFF',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
tension: 0.4
}]
},
options: {
plugins: {
annotation: {
annotations: {
basicEllipse: {
type: 'ellipse',
xMin: '2020-04',
xMax: '2020-05',
yMin: 80,
yMax: 120,
backgroundColor: 'rgba(255, 193, 7, 0.2)',
borderColor: '#FFC107',
borderWidth: 3
},
perfectCircle: {
type: 'ellipse',
xMin: '2020-07',
xMax: '2020-08',
yMin: 90,
yMax: 120,
backgroundColor: 'rgba(220, 53, 69, 0.15)',
borderColor: '#DC3545',
borderWidth: 4,
borderDash: [6, 3],
rotation: Math.PI / 4,
label: {
enabled: true,
content: '注目!',
color: '#DC3545',
font: { size: 16, weight: 'bold' }
}
},
animatedEllipse: {
type: 'ellipse',
xMin: '2020-10',
xMax: '2020-11',
yMin: 60,
yMax: 140,
backgroundColor: 'rgba(111, 66, 193, 0.1)',
borderColor: '#6F42C1',
borderWidth: 3
}
}
}
}
}
};
6. ポイントマーカー
特定のデータポイントを強調します。以下の例では、基本ポイント、装飾付きポイント、カスタム形状ポイントを示します。
const pointMarkerConfig = {
type: 'line',
data: {
labels: ['2020-01', '2020-03', '2020-06', '2020-09', '2020-12'],
datasets: [{
label: 'データ',
data: [50, 95, 130, 75, 150],
borderColor: '#007BFF',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
tension: 0.4
}]
},
options: {
plugins: {
annotation: {
annotations: {
basicPoint: {
type: 'point',
xValue: '2020-03',
yValue: 95,
backgroundColor: '#FF0000',
borderColor: '#FFFFFF',
borderWidth: 3,
radius: 8
},
decoratedPoint: {
type: 'point',
xValue: '2020-06',
yValue: 130,
backgroundColor: '#28A745',
borderColor: '#FFFFFF',
borderWidth: 4,
radius: 12,
shadowColor: 'rgba(40, 167, 69, 0.5)',
shadowBlur: 12,
shadowOffsetX: 2,
shadowOffsetY: 2,
label: {
enabled: true,
content: '最高値達成!',
position: 'top',
yAdjust: -15,
backgroundColor: '#28A745',
color: 'white',
padding: 8,
borderRadius: 20,
font: { size: 12, weight: 'bold' }
}
},
customPoint: {
type: 'point',
xValue: '2020-09',
yValue: 75,
pointStyle: 'star',
backgroundColor: '#FFC107',
borderColor: '#212529',
borderWidth: 2,
radius: 15,
rotation: Math.PI / 6
}
}
}
}
}
};
7. ラベル設定詳細
ラベルは情報を伝える重要な要素です。以下の例では、ラベルの詳細な設定を示します。サンプルグラフを追加しました。
label: {
enabled: true,
content: 'ラベルテキスト',
content: [
'重要なお知らせ',
'詳細は担当者まで',
'期限: 2020/12/31'
],
content: (ctx) => {
const value = ctx.parsed?.y || 0;
return `現在値: ${value.toFixed(1)}`;
},
position: 'center',
xAdjust: 0,
yAdjust: -20,
position: { x: 'center', y: 'top' },
color: '#FFFFFF',
backgroundColor: '#007BFF',
backgroundColor: (ctx) => {
const chart = ctx.chart;
const {ctx: canvasCtx} = chart;
const gradient = canvasCtx.createLinearGradient(0, 0, 100, 0);
gradient.addColorStop(0, '#007BFF');
gradient.addColorStop(1, '#6610F2');
return gradient;
},
borderColor: '#FFFFFF',
borderWidth: 2,
borderRadius: 8,
borderDash: [5, 3],
padding: 12,
padding: { top: 8, right: 12, bottom: 8, left: 12 },
font: {
family: 'Arial, sans-serif',
size: 14,
weight: 'bold',
style: 'normal',
lineHeight: 1.2
},
rotation: Math.PI / 4,
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 6,
shadowOffsetX: 2,
shadowOffsetY: 2,
textAlign: 'center',
enabled: (ctx) => ctx.element.options.borderWidth > 2
}
8. アニメーション設定
動きのある効果でユーザーの注意を引きます。以下の例では、アニメーション設定を示します。サンプルグラフを追加しました。
options: {
animation: {
duration: 2000,
easing: 'easeInOutQuart',
onProgress: (animation) => {
const progress = animation.currentStep / animation.numSteps;
console.log(`アニメーション進行度: ${(progress * 100).toFixed(1)}%`);
},
onComplete: () => {
console.log('アニメーション完了');
}
},
plugins: {
annotation: {
animations: {
borderWidth: { duration: 1000, easing: 'easeInOutBounce' },
backgroundColor: { duration: 1500, easing: 'easeInOutSine' }
},
annotations: {
fadeInLine: {
type: 'line',
scaleID: 'x',
value: '2020-01-01',
borderColor: '#FF0000',
borderWidth: 3,
enabled: false,
animation: {
enabled: { duration: 0, delay: 1000 },
borderWidth: { from: 0, to: 3, duration: 1000 }
}
},
wavyLine: {
type: 'line',
scaleID: 'y',
value: (ctx) => {
const time = Date.now();
return 100 + Math.sin(time / 1000) * 20;
},
borderColor: '#00AA00',
borderWidth: 2
},
pulsingPoint: {
type: 'point',
xValue: '2020-06-01',
yValue: 100,
backgroundColor: '#FF6B00',
radius: (ctx) => {
const time = Date.now();
return 8 + Math.sin(time / 500) * 4;
}
}
}
}
}
}
9. イベント処理
ユーザーのインタラクションに応答します。以下の例では、インタラクティブなアノテーションを示します。
const eventHandlingConfig = {
type: 'line',
data: {
labels: ['2020-01', '2020-03', '2020-06', '2020-09', '2020-12'],
datasets: [{
label: 'データ',
data: [50, 75, 100, 125, 150],
borderColor: '#007BFF',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
tension: 0.4
}]
},
options: {
plugins: {
annotation: {
annotations: {
interactiveAnnotation: {
type: 'box',
xMin: '2020-01',
xMax: '2020-03',
yMin: 50,
yMax: 150,
backgroundColor: 'rgba(0, 123, 255, 0.1)',
borderColor: '#007BFF',
borderWidth: 2,
click: ({element, chart}) => {
console.log('クリックされたアノテーション:', element.options);
console.log('チャートデータ:', chart.data);
alert('Q1期間がクリックされました!');
element.options.backgroundColor = 'rgba(40, 167, 69, 0.3)';
chart.update('none');
setTimeout(() => {
element.options.backgroundColor = 'rgba(0, 123, 255, 0.1)';
chart.update('none');
}, 1000);
},
enter: ({element, chart}) => {
element.options.backgroundColor = 'rgba(0, 123, 255, 0.3)';
element.options.borderWidth = 4;
chart.canvas.style.cursor = 'pointer';
chart.update('none');
},
leave: ({element, chart}) => {
element.options.backgroundColor = 'rgba(0, 123, 255, 0.1)';
element.options.borderWidth = 2;
chart.canvas.style.cursor = 'default';
chart.update('none');
}
}
}
}
}
}
};
10. 実用例
実際のビジネスシーンで使える具体的な例を以下に示します。各例には動作するサンプルグラフを掲載しています。
売上分析ダッシュボード
const salesAnalysisConfig = {
type: 'line',
data: {
labels: ['2020-01', '2020-02', '2020-03', '2020-04', '2020-05', '2020-06',
'2020-07', '2020-08', '2020-09', '2020-10', '2020-11', '2020-12'],
datasets: [{
label: '月次売上',
data: [120, 135, 95, 85, 140, 160, 180, 155, 170, 145, 190, 210],
borderColor: '#007BFF',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
tension: 0.4
}]
},
options: {
plugins: {
annotation: {
annotations: {
covidStart: {
type: 'line',
scaleID: 'x',
value: '2020-03',
borderColor: '#DC3545',
borderWidth: 3,
borderDash: [8, 4],
label: {
enabled: true,
content: 'COVID-19影響開始',
position: 'start',
backgroundColor: '#DC3545',
color: 'white',
padding: 8,
font: { size: 12, weight: 'bold' }
}
},
lowPerformance: {
type: 'box',
xMin: '2020-03',
xMax: '2020-05',
yMin: 0,
yMax: 250,
backgroundColor: 'rgba(220, 53, 69, 0.05)',
borderColor: 'rgba(220, 53, 69, 0.3)',
borderWidth: 1,
label: {
enabled: true,
content: '売上低迷期',
position: { x: 'center', y: 'top' },
yAdjust: 10,
backgroundColor: 'rgba(220, 53, 69, 0.8)',
color: 'white'
}
},
recoveryStart: {
type: 'line',
scaleID: 'x',
value: '2020-06',
borderColor: '#28A745',
borderWidth: 2,
label: {
enabled: true,
content: '回復基調',
position: 'start',
backgroundColor: '#28A745',
color: 'white'
}
},
targetLine: {
type: 'line',
scaleID: 'y',
value: 150,
borderColor: '#FFC107',
borderWidth: 2,
borderDash: [5, 5],
label: {
enabled: true,
content: '月次目標: 150',
position: 'end',
backgroundColor: '#FFC107',
color: '#212529'
}
},
bestRecord: {
type: 'point',
xValue: '2020-12',
yValue: 210,
backgroundColor: '#FFD700',
borderColor: '#FF8C00',
borderWidth: 3,
radius: 12,
label: {
enabled: true,
content: '過去最高!',
position: 'top',
yAdjust: -15,
backgroundColor: '#FFD700',
color: '#8B4513',
font: { weight: 'bold' }
}
}
}
}
}
}
};
プロジェクト進捗管理
const projectConfig = {
type: 'line',
data: {
labels: ['Week1', 'Week2', 'Week3', 'Week4', 'Week5', 'Week6', 'Week7', 'Week8'],
datasets: [{
label: '実績',
data: [10, 25, 40, 35, 55, 70, 80, 95],
borderColor: '#007BFF'
}, {
label: '計画',
data: [12.5, 25, 37.5, 50, 62.5, 75, 87.5, 100],
borderColor: '#6C757D',
borderDash: [5, 5]
}]
},
options: {
plugins: {
annotation: {
annotations: {
milestone1: {
type: 'line',
scaleID: 'x',
value: 'Week3',
borderColor: '#17A2B8',
borderWidth: 3,
label: {
enabled: true,
content: 'MS1: 基本設計完了',
position: 'start',
rotation: Math.PI / 2,
backgroundColor: '#17A2B8',
color: 'white'
}
},
milestone2: {
type: 'line',
scaleID: 'x',
value: 'Week6',
borderColor: '#17A2B8',
borderWidth: 3,
label: {
enabled: true,
content: 'MS2: 開発完了',
position: 'start',
rotation: Math.PI / 2,
backgroundColor: '#17A2B8',
color: 'white'
}
},
delayWarning: {
type: 'box',
xMin: 'Week3',
xMax: 'Week5',
yMin: 0,
yMax: 100,
backgroundColor: 'rgba(255, 193, 7, 0.1)',
borderColor: 'rgba(255, 193, 7, 0.5)',
borderWidth: 1,
label: {
enabled: true,
content: '⚠️ 進捗遅延期間',
backgroundColor: '#FFC107',
color: '#212529'
},
click: () => {
alert('遅延要因:\n- リソース不足\n- 要件変更\n- 技術的課題');
}
},
completionTarget: {
type: 'point',
xValue: 'Week8',
yValue: 100,
backgroundColor: '#28A745',
borderColor: '#FFFFFF',
borderWidth: 3,
radius: 10,
label: {
enabled: true,
content: '完了予定',
position: 'top',
backgroundColor: '#28A745',
color: 'white'
}
}
}
}
}
}
};
株価チャート分析
const stockConfig = {
type: 'line',
data: {
labels: ['2020-01', '2020-02', '2020-03', '2020-04', '2020-05', '2020-06'],
datasets: [{
label: '株価',
data: [1000, 1150, 850, 750, 950, 1100],
borderColor: '#007BFF'
}]
},
options: {
plugins: {
annotation: {
annotations: {
upTrend: {
type: 'line',
scaleID: 'x',
xMin: '2020-04',
xMax: '2020-06',
yMin: 750,
yMax: 1100,
borderColor: '#28A745',
borderWidth: 2,
label: {
enabled: true,
content: '上昇トレンド',
position: 'end',
backgroundColor: '#28A745',
color: 'white'
}
},
supportLine: {
type: 'line',
scaleID: 'y',
value: 800,
borderColor: '#007BFF',
borderWidth: 2,
borderDash: [10, 5],
label: {
enabled: true,
content: 'サポート: ¥800',
position: 'start',
backgroundColor: '#007BFF',
color: 'white'
}
},
resistanceLine: {
type: 'line',
scaleID: 'y',
value: 1200,
borderColor: '#DC3545',
borderWidth: 2,
borderDash: [10, 5],
label: {
enabled: true,
content: 'レジスタンス: ¥1,200',
position: 'start',
backgroundColor: '#DC3545',
color: 'white'
}
},
announcement: {
type: 'line',
scaleID: 'x',
value: '2020-03',
borderColor: '#6F42C1',
borderWidth: 3,
label: {
enabled: true,
content: ['重要発表', '決算説明会'],
position: 'start',
backgroundColor: '#6F42C1',
color: 'white',
padding: 10
}
},
buySignal: {
type: 'point',
xValue: '2020-04',
yValue: 750,
backgroundColor: '#28A745',
borderColor: '#FFFFFF',
borderWidth: 3,
radius: 15,
label: {
enabled: true,
content: '💰 買いシグナル',
position: 'bottom',
yAdjust: 15,
backgroundColor: '#28A745',
color: 'white',
font: { size: 14, weight: 'bold' }
}
}
}
}
}
}
};
医療データ可視化
const medicalConfig = {
type: 'line',
data: {
labels: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'],
datasets: [{
label: '血圧(最高)',
data: [120, 115, 130, 140, 135, 125, 118],
borderColor: '#DC3545'
}, {
label: '血圧(最低)',
data: [80, 75, 85, 90, 88, 82, 78],
borderColor: '#007BFF'
}]
},
options: {
plugins: {
annotation: {
annotations: {
highBPWarning: {
type: 'line',
scaleID: 'y',
value: 140,
borderColor: '#DC3545',
borderWidth: 2,
borderDash: [8, 4],
label: {
enabled: true,
content: '⚠️ 高血圧レベル',
position: 'end',
backgroundColor: '#DC3545',
color: 'white'
}
},
medicationTime: {
type: 'line',
scaleID: 'x',
value: '08:00',
borderColor: '#28A745',
borderWidth: 2,
label: {
enabled: true,
content: '💊 服薬時間',
position: 'top',
backgroundColor: '#28A745',
color: 'white'
}
},
exerciseTime: {
type: 'box',
xMin: '16:00',
xMax: '20:00',
yMin: 0,
yMax: 180,
backgroundColor: 'rgba(255, 193, 7, 0.1)',
borderColor: '#FFC107',
borderWidth: 1,
label: {
enabled: true,
content: '🏃 運動時間帯',
backgroundColor: '#FFC107',
color: '#212529'
}
},
normalRange: {
type: 'box',
xMin: '00:00',
xMax: '24:00',
yMin: 90,
yMax: 130,
backgroundColor: 'rgba(40, 167, 69, 0.05)',
borderColor: 'rgba(40, 167, 69, 0.3)',
borderWidth: 1,
label: {
enabled: true,
content: '正常範囲',
position: { x: 'start', y: 'center' },
backgroundColor: 'rgba(40, 167, 69, 0.8)',
color: 'white'
}
}
}
}
}
}
};
🔧 高度なテクニック
動的アノテーション更新
以下のコードは、データの更新に応じてアノテーションを動的に変更する例です。サンプルグラフを追加しました。
function updateAnnotations(chart, newData) {
const annotations = chart.options.plugins.annotation.annotations;
const maxValue = Math.max(...newData);
annotations.warningLine = {
type: 'line',
scaleID: 'y',
value: maxValue * 0.9,
borderColor: '#FF0000',
borderWidth: 2
};
const latestIndex = newData.length - 1;
annotations.latestPoint = {
type: 'point',
xValue: chart.data.labels[latestIndex],
yValue: newData[latestIndex],
backgroundColor: '#FF6B00',
radius: 10
};
chart.update();
}
カスタムプラグイン作成
以下のコードは、カスタム矢印やアイコンを描画するプラグインの例です。サンプルグラフを追加しました。
const customAnnotationPlugin = {
id: 'customAnnotation',
afterDraw(chart, args, options) {
const ctx = chart.ctx;
const chartArea = chart.chartArea;
if (options.arrows) {
options.arrows.forEach(arrow => {
drawArrow(ctx, arrow, chart.scales);
});
}
if (options.icons) {
options.icons.forEach(icon => {
drawIcon(ctx, icon, chart.scales);
});
}
}
};
function drawArrow(ctx, arrow, scales) {
const x = scales.x.getPixelForValue(arrow.x);
const y = scales.y.getPixelForValue(arrow.y);
ctx.save();
ctx.fillStyle = arrow.color || '#FF0000';
ctx.translate(x, y);
ctx.rotate(arrow.rotation || 0);
ctx.beginPath();
ctx.moveTo(-10, -5);
ctx.lineTo(0, 0);
ctx.lineTo(-10, 5);
ctx.lineTo(-7, 0);
ctx.closePath();
ctx.fill();
ctx.restore();
}
function drawIcon(ctx, icon, scales) {
const x = scales.x.getPixelForValue(icon.x);
const y = scales.y.getPixelForValue(icon.y);
ctx.save();
ctx.font = `${icon.size || 20}px Arial`;
ctx.fillStyle = icon.color || '#000000';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(icon.symbol || '★', x, y);
ctx.restore();
}
パフォーマンス最適化
以下のコードは、大量のアノテーションを効率的に処理するための最適化例です。サンプルグラフを追加しました。
const optimizedAnnotations = {
animation: false,
clip: true,
annotations: {
conditionalLine: {
type: 'line',
scaleID: 'x',
value: '2020-06-01',
borderColor: '#FF0000',
display: (ctx) => {
const chart = ctx.chart;
const xScale = chart.scales.x;
const range = xScale.max - xScale.min;
return range < 365 * 24 * 60 * 60 * 1000;
}
}
}
};
const memoizedAnnotations = {};
function getMemoizedAnnotation(key, generator) {
if (!memoizedAnnotations[key]) {
memoizedAnnotations[key] = generator();
}
return memoizedAnnotations[key];
}
🎨 デザインのベストプラクティス
カラーパレット
統一感のあるカラーパレットを使用することで、視覚的な一貫性を保ちます。サンプルグラフを追加しました。
const colorPalette = {
primary: '#007BFF',
secondary: '#6C757D',
success: '#28A745',
warning: '#FFC107',
danger: '#DC3545',
info: '#17A2B8',
primaryAlpha: 'rgba(0, 123, 255, 0.2)',
successAlpha: 'rgba(40, 167, 69, 0.2)',
warningAlpha: 'rgba(255, 193, 7, 0.2)',
dangerAlpha: 'rgba(220, 53, 69, 0.2)'
};
一貫性のあるスタイル
共通のスタイルを定義して、すべてのアノテーションに適用することで一貫性を確保します。サンプルグラフを追加しました。
const commonStyles = {
lineWidth: 2,
pointRadius: 8,
labelPadding: 8,
labelBorderRadius: 4,
fontFamily: 'system-ui, -apple-system, sans-serif',
fontSize: 12
};
function applyCommonStyles(annotation) {
return {
...annotation,
borderWidth: commonStyles.lineWidth,
label: {
...annotation.label,
padding: commonStyles.labelPadding,
borderRadius: commonStyles.labelBorderRadius,
font: {
family: commonStyles.fontFamily,
size: commonStyles.fontSize,
...annotation.label?.font
}
}
};
}
🚀 まとめ
Chart.jsのアノテーション機能を使うことで、データの可視化をより効果的に行えます。以下のポイントを押さえてください:
- 目的に応じた適切なアノテーションタイプの選択
- ユーザビリティを考慮したインタラクション設計
- パフォーマンスを意識した実装
- 一貫性のあるデザイン
- アクセシビリティへの配慮
学習の進め方
- 基本的な縦線・横線から始める
- ラベル設定をマスターする
- インタラクション機能を追加する
- 実際のプロジェクトで活用する
- カスタムプラグインで機能拡張する
これらの知識を活用して、魅力的で実用的なデータ可視化を作成してください!
コメント