はじめに
バーグラフのラベルは、チャートがリサイズされたり、異なる画面解像度で表示されたり、amChartsのズームコントロールを使ってズームイン/アウトされたりすると、可読性の問題に直面することがよくあります。デフォルトでは、amCharts 4のラベルは固定幅を持っており、小さな画面では重なり、切り取り、または読みにくいテキストを引き起こす可能性があります。
ラベルを本当にレスポンシブにするためには、次のことを行う必要があります:
- チャートのサイズとカラム幅に基づいて、ラベルの最大幅を動的に計算します。
- 必要に応じて長いラベルを切り取ります。
- イベントフックを使用して、ラベルが適切に調整されるようにします。
- チャートがリサイズされる。
- ズームまたはパンが発生する。
- データが動的に更新される。
この記事では、amCharts 4のイベントを使用して、ラベル幅を動的に調整し、必要に応じてラベルを切り取る方法を探ります。
問題のある実装コード:
am4core.ready(function () {
// チャートインスタンスを作成
let chart = am4core.create("chartdiv", am4charts.XYChart);
chart.hiddenState.properties.opacity = 0; // 初期フェードイン
chart.logo.disabled = true;
chart.responsive.enabled = true; // レスポンシブ動作を有効にする
// チャートデータ
chart.data = [
{ company: "Apple Inc.", visits: 23725 },
{ company: "Microsoft Corporation", visits: 1882 },
{ company: "Alphabet Inc. (Google)", visits: 1809 },
{ company: "Amazon.com, Inc.", visits: 1322 },
{ company: "Tesla, Inc.", visits: 1122 },
{ company: "Meta Platforms, Inc. (Facebook)", visits: 1114 },
{ company: "Berkshire Hathaway Inc.", visits: 711 },
{ company: "PepsiCo, Inc.", visits: 443 },
{ company: "Johnson & Johnson Services, Inc.", visits: 350 },
{ company: "McDonald's Corporation", visits: 298 },
{ company: "NVIDIA Corporation", visits: 150 },
{ company: "Netflix, Inc.", visits: 100 }
];
// カテゴリー軸(X軸)を作成
let categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "company";
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.minGridDistance = 40;
categoryAxis.fontSize = 11;
categoryAxis.renderer.labels.template.wrap = true;
categoryAxis.renderer.labels.template.maxWidth = 80;
categoryAxis.renderer.labels.template.padding(10, 0, 0, 0); // 全ての側面を0に設定
// 値軸(Y軸)を作成
let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.min = 0;
valueAxis.max = 24000;
valueAxis.strictMinMax = true;
valueAxis.renderer.minGridDistance = 30;
// カラムシリーズを作成
let series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.categoryX = "company";
series.dataFields.valueY = "visits";
series.columns.template.tooltipText = "{valueY.value}";
series.columns.template.tooltipY = 0;
series.columns.template.strokeOpacity = 0;
// 各カラムに異なる色を適用
series.columns.template.adapter.add("fill", function (fill, target) {
return chart.colors.getIndex(target.dataItem.index);
});
// スクロールバーを追加
chart.scrollbarX = new am4core.Scrollbar();
chart.scrollbarX.marginTop = 30;
chart.scrollbarX.parent = chart.bottomAxesContainer;
// 大きな値のための軸ブレークを追加
let axisBreak = valueAxis.axisBreaks.create();
axisBreak.startValue = 2100;
axisBreak.endValue = 22900;
// 軸ブレークサイズを調整
let d = (axisBreak.endValue - axisBreak.startValue) / (valueAxis.max - valueAxis.min);
axisBreak.breakSize = 0.05 * (1 - d) / d; // より良い可視性のためにブレークサイズを増加
// より良いインタラクションのためにホバー効果を調整
let hoverState = axisBreak.states.create("hover");
hoverState.properties.breakSize = 1; // ホバー時に少し大きなブレーク
hoverState.properties.opacity = 0.1;
hoverState.transitionDuration = 1500;
axisBreak.defaultState.transitionDuration = 1000;
}); // end am4core.ready()
ラベルのレスポンシブ最大幅を設定
amCharts 4では、カラムチャートのxAxisのラベルには固定のmaxWidthがあります。しかし、この幅は動的に調整されないため、カラム幅とチャートサイズに基づいて計算する必要があります。
これを実現するために、rangechangeendedと sizechanged イベントにフックし、カラムの幅をチェックして、maxWidthを設定します。
実装
// 最も広いカラムに基づいてラベルの最大幅を更新する関数
const updateLabelMaxWidth = () => {
let maxColumnWidth = 0;
// 各カラムをループして最大幅を見つける
series.columns.each(function (column) {
if (column && column.pixelWidth > maxColumnWidth) {
maxColumnWidth = column.pixelWidth;
}
});
// ラベルに最大幅を適用
categoryAxis.renderer.labels.template.maxWidth = maxColumnWidth;
console.log("更新された最大ラベル幅: " + maxColumnWidth);
// ラベルを再処理するよう強制
categoryAxis.invalidateLabels();
}
// ズーム/パン時にラベル幅を更新するトリガー
categoryAxis.events.on("rangechangeended", function () {
updateLabelMaxWidth();
});
// 画面リサイズ時にラベル幅を更新するトリガー
chart.events.on("sizechanged", function () {
updateLabelMaxWidth();
});
説明
関数updateLabelMaxWidthは、チャート内の最も広いカラムに基づいてx軸ラベルの幅を動的に調整します。すべてのカラムをループし、最大幅を見つけて、ラベルに適用し、重なりを防ぎます。
イベントフック:
- rangechangeended → ズーム/パン時にラベル幅を更新します。
- sizechanged → チャートがリサイズされたときにラベルを調整します。
これにより、ラベルは動的にサイズ変更され、利用可能なスペースに基づいて調整されます。
可読性向上のための長いラベルの切り取り
動的な最大幅を持っていても、一部のラベルはまだ長すぎる場合があり、混雑を引き起こす可能性があります。これを処理するために、ラベルを切り取ります。
実装
categoryAxis.renderer.labels.template.adapter.add("textOutput", function(text, target) {
let maxLabelLength = 10; // 切り取り前の最大文字数を設定
if (text.length > maxLabelLength) {
return text.substring(0, maxLabelLength) + "..."; // 切り取りと省略記号を追加
}
return text; // 制限内であれば元のテキストを返す
});
説明
- この関数は各ラベルの長さをチェックします。
- ラベルが長すぎる場合、テキストを最初の単語に切り取り、".."を末尾に追加します。
- 最大文字数制限内のラベルは変更されません。
これにより、長いラベルが重ならず、十分な情報を提供し、リサイズやズームイン/アウト時にもレスポンシブであることが保証されます。
解決策を適用した後の完全な実装コード
am4core.ready(function () {
// チャートインスタンスを作成
let chart = am4core.create("chartdiv", am4charts.XYChart);
chart.hiddenState.properties.opacity = 0; // 初期フェードイン
chart.logo.disabled = true;
chart.responsive.enabled = true; // レスポンシブ動作を有効にする
// チャートデータ
chart.data = [
{ company: "Apple Inc.", visits: 23725 },
{ company: "Microsoft Corporation", visits: 1882 },
{ company: "Alphabet Inc. (Google)", visits: 1809 },
{ company: "Amazon.com, Inc.", visits: 1322 },
{ company: "Tesla, Inc.", visits: 1122 },
{ company: "Meta Platforms, Inc. (Facebook)", visits: 1114 },
{ company: "Berkshire Hathaway Inc.", visits: 711 },
{ company: "PepsiCo, Inc.", visits: 443 },
{ company: "Johnson & Johnson Services, Inc.", visits: 350 },
{ company: "McDonald's Corporation", visits: 298 },
{ company: "NVIDIA Corporation", visits: 150 },
{ company: "Netflix, Inc.", visits: 100 }
];
// カテゴリー軸(X軸)を作成
let categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "company";
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.minGridDistance = 40;
categoryAxis.fontSize = 11;
categoryAxis.renderer.labels.template.wrap = true;
categoryAxis.renderer.labels.template.maxWidth = 80;
categoryAxis.renderer.labels.template.padding(10, 0, 0, 0); // 全ての側面を0に設定
// 値軸(Y軸)を作成
let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.min = 0;
valueAxis.max = 24000;
valueAxis.strictMinMax = true;
valueAxis.renderer.minGridDistance = 30;
// 最も広いカラムに基づいてラベルの最大幅を更新する関数
const updateLabelMaxWidth = () => {
let maxColumnWidth = 0;
// 各カラムをループして最大幅を見つける
series.columns.each(function (column) {
if (column && column.pixelWidth > maxColumnWidth) {
maxColumnWidth = column.pixelWidth;
}
});
// ラベルに最大幅を適用
categoryAxis.renderer.labels.template.maxWidth = maxColumnWidth;
console.log("更新された最大ラベル幅: " + maxColumnWidth);
// ラベルを再処理するよう強制
categoryAxis.invalidateLabels();
}
// ズーム/パン時にラベル幅を更新するトリガー
categoryAxis.events.on("rangechangeended", function () {
updateLabelMaxWidth();
});
// 画面リサイズ時にラベル幅を更新するトリガー
chart.events.on("sizechanged", function () {
updateLabelMaxWidth();
});
// 必要に応じてラベルを最初の単語で切り取る
categoryAxis.renderer.labels.template.adapter.add("textOutput", function (text, target) {
let labelMaxWidth = target.maxWidth;
if (labelMaxWidth < 60 && text) { // ラベル幅が60ピクセル未満かチェック
let words = text.split(" "); // テキストを単語に分割
return words.length > 1 ? words[0] + ".." : text; // 最初の単語のみを保持し、".."を追加
}
return text;
});
// カラムシリーズを作成
let series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.categoryX = "company";
series.dataFields.valueY = "visits";
series.columns.template.tooltipText = "{valueY.value}";
series.columns.template.tooltipY = 0;
series.columns.template.strokeOpacity = 0;
// 各カラムに異なる色を適用
series.columns.template.adapter.add("fill", function (fill, target) {
return chart.colors.getIndex(target.dataItem.index);
});
// スクロールバーを追加
chart.scrollbarX = new am4core.Scrollbar();
chart.scrollbarX.marginTop = 30;
chart.scrollbarX.parent = chart.bottomAxesContainer;
// 大きな値のための軸ブレークを追加
let axisBreak = valueAxis.axisBreaks.create();
axisBreak.startValue = 2100;
axisBreak.endValue = 22900;
// 軸ブレークサイズを調整
let d = (axisBreak.endValue - axisBreak.startValue) / (valueAxis.max - valueAxis.min);
axisBreak.breakSize = 0.05 * (1 - d) / d; // より良い可視性のためにブレークサイズを増加
// より良いインタラクションのためにホバー効果を調整
let hoverState = axisBreak.states.create("hover");
hoverState.properties.breakSize = 1; // ホバー時に少し大きなブレーク
hoverState.properties.opacity = 0.1;
hoverState.transitionDuration = 1500;
axisBreak.defaultState.transitionDuration = 1000;
}); // end am4core.ready()
これらの調整により、私たちのチャートは完璧に適応します—ラベルは読みやすく、重なりもなく、すべてがスムーズにリサイズされます。幅を動的に調整し、長い名前を切り取り、イベントにフックすることで、どの画面でもチャートが素晴らしく見えるようにしました。さあ、試してみてください!