JavaScript

Apache ECharts 折れ線グラフ入門|最小サンプルから実務レベルに仕上げる手順

みなと
UI実装担当
UI実装担当

グラフ作りたいけど、結局どのライブラリが良いんだろう…

Web制作やフロントエンド開発で、数値の推移や比較をグラフで見せたい場面はよくあります。ただ、そのときに迷うのが「どのグラフライブラリを使うか」ではないでしょうか。

手軽さ重視なら軽量ライブラリ、表現力重視なら自由度の高いもの…と選択肢は多いですが、実務では次のようなポイントが大事になってきます。

  • やりたいグラフや表現にちゃんと対応できるか
  • デザインが洗練されていて、いい感じに見えるか
  • ホバーや切り替えなどの操作が直感的で使いやすいか
  • PC / スマホで崩れないレスポンシブ対応があるか

そこで候補に上がるのがApache EChartsです。無料で使えるオープンソースソフトウェアでありながら、折れ線・棒・円といった基本グラフに加えて、ツールチップや凡例、インタラクション、PC/SP対応まで“実務で必要になる機能”が一通り揃っています。

さらに、標準の時点で色味や余白、UIの雰囲気が整っていて、少し触るだけでも「それっぽい」「見やすい」グラフに仕上がりやすいのも大きな魅力です。

ただし高機能なぶん、最初はオプションが多くて把握しづらいのも事実。この記事では折れ線グラフにテーマを絞り、公式の最小デモからステップ形式で少しずつ育てながら理解していく流れにします。

この記事のゴール

最終的に、次のデモを完成させます。

  • 2つのデータの推移を1つの折れ線グラフで比較できる
  • ボタン操作で、表示するデータ(グラフ)を切り替えられる
  • PC / スマホでも崩れずに見やすいレイアウトで表示できる

まずは完成形を触ってゴールを確認し、そこから一歩ずつ作っていきましょう!

Apache EChartsとは

Apache ECharts(アパッチ イーチャーツ)は、Apache Software Foundationが管理するオープンソースのグラフ描画ライブラリです。Webページ上で、折れ線・棒・円などさまざまなグラフを柔軟に表示できます。

EChartsの良いところ

ここでは、EChartsの魅力を3つ紹介します。

  • 公式サンプルがとにかく豊富
    やりたい表現に近いデモが見つかりやすく、完成形を手がかりに理解を進められます。
  • 調整できるオプションが幅広い
    色・余白・凡例・ツールチップ・レスポンシブなど、見た目や動きを細かく整えられるので、実務向けの仕上げがしやすいです。
  • 標準の状態でもデザインが洗練されている
    余白や色使い、ツールチップの見え方などが最初から整っていて、少し設定するだけで“いい感じのグラフ”に仕上がりやすいのが特徴です。
Apache ECharts公式サイトのサンプル一覧画面。左にチャートカテゴリのメニュー、右に多数のデモサムネイルが並んでいる。
公式サンプルには、基本のグラフだけでなく、実際のWebサービスで見かけるような完成度の高い例が大量に用意されています。

どんな場面で強いライブラリなの?

EChartsは「ただグラフを置くだけ」よりも、見やすさや完成度を高めたいケースで力を発揮します。

目的EChartsが得意な理由
データの変化や違いを分かりやすく見せたい凡例・ツールチップ・強調表示などを細かく整えられる
見た目をきれいに仕上げたい標準のデザイン品質が高く、調整もしやすい
PC/スマホで崩れないグラフにしたい幅に応じたレイアウト調整も設定で対応できる

この記事では何をやる?

EChartsは高機能なぶん設定項目が多いので、いきなり全部を理解しようとすると逆に混乱しがちです。

そこで今回は、まず入口として最適な折れ線グラフ(ラインチャート)にテーマを絞ります。折れ線は、軸・データ・凡例・ツールチップ・見た目調整・レスポンシブ対応といったEChartsの基本構造が、一番シンプルに体験できるからです。

このあと完成形デモでゴールを確認し、公式の基本サンプルを出発点にしながら、段階的に少しずつ作り込んでいきましょう。

完成デモを触ってみよう

まずは完成デモを見て、最終的にどんな形を目指すか確認してみましょう。ボタンを押すと、ページ別のPV推移グラフ(※数値はサンプル)を表示・切り替えできます。

ボタンを押すと
グラフが表示されます

デモを触ってみると、1週間の曜日軸の上で「今週 / 先週」のPV推移が重なって表示され、変化の違いがひと目で分かるはずです。

また、PCとスマホで見比べると、凡例(今週 / 先週)の位置やグラフ上部の余白が自動で調整され、どちらの画面でも見やすくなるようにレイアウトが切り替わります。

事前準備

ここから先のステップでは、実際にEChartsを動かしながら折れ線グラフを作っていきます。まずは下準備として、EChartsを読み込んでグラフを描ける状態にしておきましょう。

1)EChartsを読み込む

一番手軽なのは、公式CDN(配信ファイル)を<script>で読み込む方法です。記事のデモもこの方法で進めます。

<!-- ECharts 本体を読み込む(CDN) -->
<script src="https://cdn.jsdelivr.net/npm/echarts@6.0.0/dist/echarts.min.js"></script>

これを読み込んだら、あとはJavaScriptからechartsが使えるようになります。

2)グラフを表示するためのHTMLを用意する

EChartsは描画先になる要素(コンテナ)が必須です。最小構成だとこれだけでOK。

<div id="echartsContainer" class="echarts-container"></div>

idはJavaScriptで取得するために使用、classはCSSで高さなどを決めるために使います。

3)CSSで高さを決める

EChartsは指定したコンテナのサイズの中にグラフを描く仕組みです。そのため、高さはCSSで決めておく必要があります。

/* PCのチャートの高さ */
@media (min-width: 768px) {
  .echarts-container {
    height: 400px;
  }
}

/* SPのチャートの高さ */
@media (max-width: 767px) {
  .echarts-container {
    height: 300px;
  }
}

PC / スマホで見やすい高さが変わるよう、ここで軽くレスポンシブ対応もしておきます。

4)JavaScriptでチャートを初期化する

EChartsは、「描画先の取得 → 初期化 → 設定を流し込む」の流れが基本形です。

(() => {
  // 描画先(コンテナ)を取得
  const chartDom = document.getElementById('echartsContainer');

  // コンテナが存在しない場合は処理を止める(エラー防止)
  if (!chartDom) return;

  // チャートを初期化(ここでEChartsが動き始める)
  const myChart = echarts.init(chartDom);

  // グラフの設計図(option)を作る
  const option = {
    // ← ここにグラフの設定を書いていく
  };

  // optionを反映して描画
  myChart.setOption(option);

  // 画面サイズ変更に追従(レスポンシブ対応)
  window.addEventListener('resize', () => {
    myChart.resize();
  });
})();

コードのポイント(各行の役割)

  • const chartDom = document.getElementById('echartsContainer');
    → グラフを描くためのコンテナ要素(HTMLの箱)を取得します。
  • const myChart = echarts.init(chartDom);
    → その箱を使って、EChartsのチャートを初期化します。myChartがグラフ操作の本体になります。
  • const option = { ... };
    グラフの設計図です。タイトル、軸、データ、見た目など、全部ここに書いていきます。
  • myChart.setOption(option);
    → optionを反映して、実際にグラフを描画します。
  • window.addEventListener('resize', () => { myChart.resize(); });
    → 画面サイズが変わったとき、グラフのサイズも再計算して表示を整えます。レスポンシブ対応のための定番セットです。

この形が、ステップ1〜8でもずっとベースになります。

5)公式サンプル集の使い方

EChartsの公式サイトには、膨大なグラフのサンプル集が用意されています。困ったらまずはここを見るとよいでしょう。

このサンプル集の良いところは、

  • 似た見た目のサンプルを探して、設定の書き方を確認できる
  • その場でオプションを書き換えて、動きや見た目の変化を試せる

という点です。

スワン
スワン

この記事でも、まず公式の最小サンプルからスタートして、必要な設定を段階的に足していく流れにします。

ステップ1:公式の最小折れ線グラフを動かす

まずは公式の最小サンプルをそのまま動かして、EChartsの「基本の形」を掴みます。ここが理解できれば、あとはオプションを足していくだけで完成形に近づけられます。

横軸に曜日、縦軸に数値が並んだ、シンプルな折れ線グラフが表示されています。

コード全体(HTML / CSS / JavaScript)

まずは全体コードをそのまま掲載します。細かい意味やポイントは、次のパートで順番に解説していきます。

HTML

<div id="echartsContainer" class="echarts-container"></div>

CSS

@media (min-width: 768px) {
  .echarts-container {
    height: 400px;
  }
}

@media (max-width: 767px) {
  .echarts-container {
    height: 300px;
  }
}

JavaScript

(() => {
  const chartDom = document.getElementById('echartsContainer');

  if (!chartDom) return;

  const myChart = echarts.init(chartDom);

  const option = {
    // 横の軸:曜日などのラベルを左から順に並べる
    xAxis: {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    // 縦の軸:数値の目盛りを表示する
    yAxis: {
      type: 'value'
    },
    // グラフとして描くデータ(今回は折れ線1本ぶん)
    series: [
      {
        type: 'line',
        data: [150, 230, 224, 218, 135, 147, 260]
      }
    ]
  };

  myChart.setOption(option);

  window.addEventListener('resize', () => {
    myChart.resize();
  });
})();

コードのポイント解説

ステップ1で押さえるべきポイントは、optionの中の3つのパーツだけです。ここがEChartsの折れ線グラフの“最小セット”になります。

1)横の軸(xAxis):ラベルを左から順に並べる

xAxis: {
  type: 'category',
  data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
}
  • type: 'category'
    → 横軸を「曜日や項目名などのラベルを順番に並べる軸」として使う設定です。
  • data
    → 横に並べたいラベルを配列で指定します。

今回はMon〜Sunが左から並ぶイメージですね。

ちなみに、横軸のtypeには他にも種類があります。目的に合わせて使い分けます。

category曜日・商品名など、ラベルを並べたいとき(今回
value横軸も数値として扱いたいとき(散布図など)
time日付・時刻を連続的に扱いたいとき(時系列向き)
log値の幅が極端に大きいデータを見やすくしたいとき(対数軸)

2)縦の軸(yAxis):数値の目盛りを作る

yAxis: {
  type: 'value'
}
  • type: 'value'
    → 縦軸を「数値を目盛りで見せる軸」にする設定です。データに合わせて、0〜最大値までの目盛りが自動で作られます。

yAxisのtypeも、基本はxAxisと同じ4種類(category / value / time / log)を指定できます。

value数値の大きさをそのまま目盛りで見せる基本形(今回
category縦軸に段階ラベルを並べたいとき(例:S/M/L、評価ランクなど)
time縦方向が時間の目盛りになるようなケースで使う
log値の差が大きすぎて見づらいときに対数目盛りにする

通常の折れ線グラフは、縦軸はvalueでまず問題ありません。

3)グラフとして描くデータ(series)

series: [
  {
    type: 'line',
    data: [150, 230, 224, 218, 135, 147, 260]
  }
]
  • type: 'line'
    → 「折れ線グラフとして描く」指定です。
  • data
    → 横軸のラベル順に対応する数値の配列です。

つまり、Monの値150 / Tueの値230 … という1本の折れ線が描かれる仕組みです。

series.typeには折れ線以外にもいろいろなグラフタイプを指定でき、用途に応じて切り替えられます。ここでは、特によく使われる代表例を挙げておきます。

line折れ線(推移・変化を見る)
bar棒グラフ(量を比較する)
pie円グラフ(割合を見せる)
scatter散布図(ばらつき・相関を見る)

ステップ2:タイトルを付けて整える

次はグラフの内容がひと目で伝わるようにタイトルを追加します。あわせて、折れ線の始まりと終わりが横軸の端に揃うように設定してみましょう。

また、デモとして分かりやすいように曜日ラベルを英語→日本語にし、数値データも差し替えています。

グラフ上部に「サイトアクセス数」というタイトルが表示され、折れ線が左右の端まで自然に伸びています。

コード(ステップ1からの差分)

ここではステップ1から追加・変更した部分だけを載せます。全体の形はステップ1のコードがベースです。

const option = {
  // ① タイトルを追加
  title: {
    text: 'サイトアクセス数'
  },

  xAxis: {
    type: 'category',
    // ② 折れ線の始点・終点を端に揃える
    boundaryGap: false,
    // 曜日ラベルを日本語に変更
    data: ['月', '火', '水', '木', '金', '土', '日']
  },

  // データはデモ用に差し替え
  series: [
    {
      type: 'line',
      data: [420, 980, 640, 1100, 1050, 1280, 1180]
    }
  ]
};

コードのポイント解説

ステップ2で追加・変更したポイントは2つです。

1)title:グラフのタイトルを表示する

title: {
  text: 'サイトアクセス数'
}

titleはグラフの見出しを設定する場所です。textに文字列を入れると、グラフ上部にそのまま表示されます。

2)boundaryGap:左右の余白をなくして、線の端を揃える

xAxis: {
  boundaryGap: false
}

boundaryGapは、横軸の左右に余白を作るかどうかを決める設定です。折れ線グラフでfalseを指定すると、線の始まりと終わりが横軸の端に揃い、推移の範囲を端まできっちり見せられるようになります。

ステップ3:折れ線を2本にして比較する

次は、折れ線を2本に増やして「今週 vs 先週」を比較できるグラフにしていきます。EChartsでは、折れ線を増やすのはとてもシンプルで、seriesにデータセットを追加するだけでOKです。

さらに、複数の線を扱うときに便利な凡例(legend)ツールチップ(tooltip)も合わせて入れていきます。

「今週」「先週」の2本の折れ線が表示され、ホバーするとその日の値がまとめて見られるようになっています。

コード(ステップ2からの差分)

ここではステップ2から追加・変更した部分だけを載せます。全体の形はステップ2までのコードがベースです。

const option = {
  title: {
    text: 'サイトアクセス数(今週 vs 先週)'
  },

  // ① 凡例を追加(2本の線の名前を表示)
  legend: {
    selectedMode: false
  },

  // ② ツールチップを追加(ホバーで値を表示)
  tooltip: {
    trigger: 'axis'
  },

  // ③ series を2本に増やす
  series: [
    {
      name: '今週',
      type: 'line',
      data: [420, 980, 640, 1100, 1050, 1280, 1180]
    },
    {
      name: '先週',
      type: 'line',
      data: [610, 820, 760, 960, 780, 980, 890]
    }
  ]
};

コードのポイント解説

ステップ3での追加ポイントはこの3つです。

1)seriesを2本に増やす

series: [
  {
    name: '今週',
    type: 'line',
    data: [...]
  },
  {
    name: '先週',
    type: 'line',
    data: [...]
  }
]

seriesは「グラフとして描くデータのまとまり」です。ここにもう1つデータを追加するだけで折れ線が増えるのがEChartsの分かりやすいところ。

また、nameを付けておくと、凡例やツールチップでその名前が使われます。

2)legend:折れ線の名前(凡例)を表示する

legend: {
  selectedMode: false
}

legendは、「この色の線が今週、こっちの色が先週」というように、線の色とデータ名の対応を示す目印(凡例)を出すための設定です。

また、凡例は通常クリックで線の表示/非表示を切り替えられますが、selectedMode: falseはその切り替えを無効にする設定です。比較のため今回は常に2本表示にしています。

3)tooltip:ホバーで値をまとめて表示する

tooltip: {
  trigger: 'axis'
}

tooltipは、グラフにマウスを乗せたときの情報表示です。trigger: 'axis'にすると、その日の位置に合わせて2本分の値がまとめて表示されます。比較グラフでは定番の設定です。

なおtriggerには他にも種類があります。

axis同じ横軸上の値をまとめて表示したいとき(今回
itemカーソルが乗った1点だけを表示したいとき(棒グラフや円グラフでよく使う)
noneツールチップ自体を表示しない設定

今回のように「今週 / 先週を同じ曜日で比較したい」ケースでは、axisが一番見やすくなります。

ステップ4:線のスタイルを整える

ステップ3で「今週 / 先週」の2本の折れ線を表示できるようになりました。ステップ4では、線の色や実線/点線の違い、点(マーカー)の見た目を調整して、サイトのトンマナに馴染むグラフデザインに整えていきます。

今週は赤系の実線、先週は青系の点線といった感じで、2本の線の見た目に差が付いています。

コード(ステップ3からの差分)

ここではステップ3から追加・変更した部分だけを載せます。

series: [
  {
    name: '今週',
    type: 'line',
    data: [420, 980, 640, 1100, 1050, 1280, 1180],

    // ① 線の色・太さ
    lineStyle: { width: 2, color: '#c11a51' },
    // ② 点(マーカー)の色
    itemStyle: { color: '#c11a51' },

    // ③ 点の形とサイズ
    symbol: 'emptyCircle',
    symbolSize: 4,
    // ④ 通常は点を表示しない(ホバー時のみ出す)
    showSymbol: false,

    // ⑤ ホバーした線を強調(他の線は薄くなる)
    emphasis: { focus: 'series' }
  },
  {
    name: '先週',
    type: 'line',
    data: [610, 820, 760, 960, 780, 980, 890],

    lineStyle: { width: 2, type: 'dotted', color: '#4466ce' },
    itemStyle: { color: '#4466ce' },

    symbol: 'emptyCircle',
    symbolSize: 4,
    showSymbol: false,

    emphasis: { focus: 'series' }
  }
]

コードのポイント解説

今回触るのは、series(線1本ごとの設定)にある見た目のオプションです。

1)lineStyle:線の色・太さ・種類を決める

lineStyle: { width: 2, color: '#c11a51' }
lineStyle: { width: 2, type: 'dotted', color: '#4466ce' }

lineStyleは折れ線そのものの見た目を調整する場所です。

width線の太さを決めます。
color線の色を決めます。
type線の種類を決めます。指定できるのは solid(実線) / dashed(破線) / dotted(点線)の3つです。

今回は今週=実線、先週=点線にして、推移の違いが視覚的に分かりやすくなるようにしています。

2)itemStyle:点(マーカー)の色を決める

itemStyle: { color: '#c11a51' }

itemStyleは折れ線上の点(マーカー)の見た目を調整します。

3)symbol / symbolSize:点の形とサイズを指定

symbol: 'emptyCircle',
symbolSize: 4

symbolは点(マーカー)の形、symbolSizeは大きさを決める設定です。symbolにはemptyCircle / circle / rect / roundRect / triangle / diamond / pin / arrow / noneなどを指定できます。

4)showSymbol: false:点を普段は隠してスッキリさせる

showSymbol: false

showSymbol: falseにすると、折れ線上の点(マーカー)は普段は非表示になり、ホバーしたときだけ現れます。常に点が出ないので、グラフ全体がゴチャつかず落ち着いた印象になります。

5)emphasis:ホバーした線だけ目立たせる

emphasis: { focus: 'series' }

emphasisはホバー時の強調表示の設定です。focus: 'series'にするとホバーした線が強調され、他の線は薄くなるので、どの線を見ているか分かりやすくなります。

focusは次の3つを切り替えられます。

none(未指定)他は薄くならない
selfその点だけ強調
seriesその線全体を強調(今回

ステップ5:線以外のUIを整える

ステップ4では線の見た目をトンマナに合わせました。ステップ5では、文字・凡例・余白・ツールチップといった線以外のUIを調整して完成度を上げていきます。

グラフ内の文字スタイルが統一され、凡例の位置とサイズ、グラフの余白(grid)、ツールチップの表示形式がコードどおりに反映されています。

コード(ステップ4からの差分)

ここではステップ4から追加・変更した部分だけを載せます。全体の形はステップ4までのコードがベースです。

const option = {
  // ① グラフ全体の文字スタイルをまとめて指定
  textStyle: {
    color: '#333',
    fontFamily: '"Noto Sans JP", sans-serif',
    fontSize: 12,
    fontWeight: 300
  },

  title: {
    text: 'サイトアクセス数(今週 vs 先週)',
    // ② タイトル位置を左上に固定
    top: 0,
    left: 0
  },

  // ③ 凡例の位置・サイズを調整
  legend: {
    top: 5,
    right: 0,
    itemGap: 20,
    itemWidth: 30,
    itemHeight: 6,
    selectedMode: false
  },

  // ④ グラフの余白(レイアウト)を調整
  grid: {
    top: 55,
    right: 0,
    bottom: 0,
    left: 0
  },

  // ⑤ ツールチップの見た目と表示内容を調整
  tooltip: {
    trigger: 'axis',
    valueFormatter: (value) => `${value} PV`,
    textStyle: {
      color: '#333',
      fontFamily: '"Noto Sans JP", sans-serif',
      fontSize: 12,
      fontWeight: 300
    },
    padding: 15
  },

  // xAxis / yAxis / series はステップ4と同じ
};

コードのポイント解説

ここから、ステップ5で追加したUI系オプションの役割を順に確認します。

1)textStyle:グラフ全体の文字をまとめて整える

textStyle: {
  color: '#333',
  fontFamily: '"Noto Sans JP", sans-serif',
  fontSize: 12,
  fontWeight: 300
}

textStyleは、グラフ内の文字(タイトル・軸ラベル・凡例など)に共通で効く文字設定です。ここでサイトのフォントや色味に寄せておくと、グラフ全体のトーンを揃えやすくなります。

ツールチップは別レイヤーで描画されるため、全体のtextStyleがそのまま反映されないことがあります。ツールチップ側の文字を揃えたい場合は、必要に応じてtooltip.textStyle側でも指定してください。

2)title:タイトルの内容と位置を指定する

title: {
  text: 'サイトアクセス数(今週 vs 先週)',
  top: 0,
  left: 0
}

ステップ5では、タイトルの表示位置を調整しています。top / right / bottom / left は、タイトルをどこに寄せて置くかを決めるプロパティで、値はそれぞれ上/右/下/左からの距離(余白)を表します。組み合わせて位置を指定できます。

3)legend:凡例の位置とサイズを整える

legend: {
  top: 5,
  right: 0,
  itemGap: 20,
  itemWidth: 30,
  itemHeight: 6,
  selectedMode: false
}

ステップ3で追加したlegendについて、ここでは凡例の位置やサイズなど、レイアウトと見た目を整えていきます。

top / right / bottom / left凡例をどの位置に置くかを指定します。値はそれぞれ「上/右/下/左からの距離(余白)」を表し、組み合わせて配置を決めます。
itemGap凡例どうしの間隔を指定します。
itemWidth凡例アイコンの横幅を指定します。
itemHeight凡例アイコンの高さを指定します。

今回は、凡例をグラフ上部の右側に寄せ、線の長さや間隔も合わせて整えています。

4)grid:グラフの余白を調整する

grid: {
  top: 55,
  right: 0,
  bottom: 0,
  left: 0
}

gridは、グラフ本体(軸と折れ線が描かれるエリア)の位置と余白を決める設定です。top / right / bottom / leftにそれぞれ「上・右・下・左からの距離(余白)」を入れて配置を調整します。

今回は、タイトルと凡例を上に置いた分だけ余白を確保するためtop: 55を指定し、他は0にして左右と下は余計な余白を作らずコンテナいっぱいに広げています。

5)tooltip:表示内容と見た目を整える

tooltip: {
  trigger: 'axis',
  valueFormatter: (value) => `${value} PV`,
  textStyle: {
    color: '#333',
    fontFamily: '"Noto Sans JP", sans-serif',
    fontSize: 12,
    fontWeight: 300
  },
  padding: 15
}

ステップ3で触れたtooltipについて、ここでは表示内容や見た目の調整のために追加した項目を見ていきます。

valueFormatterツールチップに出る数値の表示形式を整える関数です。今回は値にPVを付けて420 PVのように表示しています。
textStyle先ほど説明した文字設定を、ツールチップ側にも同じように指定しています(全体のtextStyleが反映されない場合があるため)。
paddingツールチップの内側の余白です。文字の周りにゆとりを作って読みやすくします。

ステップ6:PC / スマホでレイアウトを切り替える

ステップ5では、文字・凡例・余白・ツールチップを整えて、グラフ全体のUIを仕上げました。ただ、ここまでは主にPCで見たときの形を整えてきた段階なので、スマホ向けの配置調整はまだこれからです。

そこでステップ6では、EChartsのmediaを使って、チャートの幅に応じてタイトルサイズ・凡例位置・上部余白を切り替えるレスポンシブ対応を入れていきます。

スマホ幅では凡例がタイトル下に左寄せで入り、その分上部の余白やタイトルサイズも幅に応じて切り替わっています。

コード(ステップ5からの差分)

このステップで追加するのはmediaです。PC / スマホで変えたい配置はベース側から外し、チャート幅ごとの設定としてmedia側に分けて書きます。

const option = {
  // ・・・(textStyle / title / tooltip / xAxis / yAxis / series はベースのまま)

  // ① media側で位置を切り替えるので、ベース側の「位置指定」を削除する
  //    - legend から top / right を外す
  //    - grid から top を外す
  legend: {
    itemGap: 20,
    itemWidth: 30,
    itemHeight: 6,
    selectedMode: false
  },
  grid: {
    right: 0,
    bottom: 0,
    left: 0
  },

  // ② チャートの幅ごとにレイアウトを切り替える
  media: [
    {
      // PC幅
      query: { minWidth: 740 },
      option: {
        // タイトルを少し大きめに
        title: {
          textStyle: { fontSize: 18 }
        },
        // 凡例は右上へ
        legend: { top: 5, right: 0 },
        // 上部余白を確保
        grid: { top: 55 }
      }
    },
    {
      // スマホ幅
      query: { maxWidth: 739 },
      option: {
        // タイトルを少し小さめに
        title: {
          textStyle: { fontSize: 16 }
        },
        // 凡例はタイトル下へ
        legend: { top: 40, left: 0 },
        // 凡例分、上部余白を増やす
        grid: { top: 85 }
      }
    }
  ]
};

コードのポイント解説

1)media:幅に応じてoptionを切り替える仕組み

mediaは、条件(query)に合ったときだけ、指定したoptionをその幅用の設定として適用するための仕組みです。

media: [
  { query: {...}, option: {...} },
  { query: {...}, option: {...} }
]
query切り替え条件
optionその条件のときに反映したい設定

書き方の感覚はCSSのメディアクエリに近いですが、判定に使われるのは「画面幅」ではなく「チャート(コンテナ)の幅」です。なので、チャートが置かれている領域の幅に合わせて、PC/スマホ用レイアウトが切り替わります。

2)ベース側から外して、media側で切り替える

mediaでPC/SPの配置を切り替えるため、ベース側の位置指定は外しておきます。ステップ5までベースにあった

  • legend.top / legend.right
  • grid.top

は削除して、PC/SPそれぞれのmedia.optionに移しています。

3)PC幅(minWidth: 740)のときの設定

{
  query: { minWidth: 740 },
  option: {
    title: { textStyle: { fontSize: 18 } },
    legend: { top: 5, right: 0 },
    grid: { top: 55 }
  }
}

PC側では横幅に余裕があるので、

  • タイトルは少し大きめ
  • 凡例は右上へ配置
  • 凡例とタイトルのために上部余白を確保

という“横に広い画面向けの配置”にしています。

4)スマホ幅(maxWidth: 739)のときの設定

{
  query: { maxWidth: 739 },
  option: {
    title: { textStyle: { fontSize: 16 } },
    legend: { top: 40, left: 0 },
    grid: { top: 85 }
  }
}

スマホ側では横幅が狭いので、

  • タイトルは少し小さめ
  • 凡例をタイトル下に左寄せで配置
  • 凡例が入る分、上部余白を広めに確保

という“縦に積む配置”に切り替えています。

ステップ7:ボタンでグラフを表示する

ステップ6までで折れ線グラフは完成し、PC / スマホでも見やすく切り替わるようになりました。とはいえ実務では、最初から表示せず「詳細を見る」ボタンの後やモーダルを開いたタイミング、スクロールで該当箇所に来たときなど、必要な場面でだけグラフを出したいことがあります。

そこでステップ7では、ボタンを押したときにグラフを表示する遅延描画パターンを作っていきます。

ボタンを押すと
グラフが表示されます

最初はプレースホルダーが表示され、ボタンを押したタイミングでグラフが描画されるようになっています。

コード全体(HTML / CSS / JavaScript)

このステップではボタンとプレースホルダー用のHTML/CSSが増え、JavaScriptでは「初期化をクリック時に行う」ように変更しています。

HTML

<!-- ボタン -->
<button id="showChartBtn" class="chart-btn" type="button">グラフを表示</button>

<!-- グラフ表示エリア -->
<div id="echartsContainer" class="echarts-container">
  <div class="chart-placeholder">
    <p class="chart-placeholder-text">ボタンを押すと<br>グラフが表示されます</p>
  </div>
</div>

CSS

.chart-btn {
  display: block;
  margin: 0 auto;
  border: none;
  border-radius: 5px;
  background: #c11a51;
  color: #fff;
  font: inherit;
  font-weight: 700;
  line-height: 1.4;
  text-align: center;
  cursor: pointer;
  appearance: none;
  transition: background-color 300ms;
}

@media (min-width: 768px) {
  .chart-btn {
    padding: 15px 30px;
    font-size: 16px;
  }
}

@media (max-width: 767px) {
  .chart-btn {
    padding: 15px 25px;
    font-size: 15px;
  }
}

@media (min-width: 768px) {
  .chart-btn:hover {
    background: #890631;
  }
}

@media (min-width: 768px) {
  .echarts-container {
    height: 400px;
    margin-top: 40px;
  }
}

@media (max-width: 767px) {
  .echarts-container {
    height: 300px;
    margin-top: 30px;
  }
}

.chart-placeholder {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  border-radius: 5px;
  background-color: rgba(68, 102, 206, 0.09);
  text-align: center;
}

.chart-placeholder-text {
  color: #31345e;
  font-weight: 700;
  line-height: 1.6;
  opacity: 0.5;
}

@media (min-width: 768px) {
  .chart-placeholder-text {
    font-size: 26px;
  }
}

@media (max-width: 767px) {
  .chart-placeholder-text {
    font-size: 20px;
  }
}

JavaScript

(() => {
  const showBtn = document.getElementById('showChartBtn');
  const chartDom = document.getElementById('echartsContainer');

  if (!chartDom) return;

  let myChart = null;

  // optionはステップ6までと同じなので省略
  const option = { /* ステップ6のoption */ };

  function showChart() {
    // まだ描画していないときだけ初期化&描画
    if (!myChart) {
      myChart = echarts.init(chartDom);
      myChart.setOption(option);
    }
  }

  showBtn.addEventListener('click', showChart);

  window.addEventListener('resize', () => {
    if (myChart) myChart.resize();
  });
})();

コードのポイント解説

1)最初はチャートを作らず、クリック時に初期化する

let myChart = null;

function showChart() {
  if (!myChart) {
    myChart = echarts.init(chartDom);
    myChart.setOption(option);
  }
}

ポイントは、初期表示ではecharts.init()を呼ばず、ボタンを押したタイミングで初めてチャートを作ることです。これにより「必要なときだけグラフを出す」UIが実現できます。

2)二重初期化を防ぐためにifでガードする

if (!myChart) {
  myChart = echarts.init(chartDom);
  myChart.setOption(option);
}

echarts.init()を何度も呼ぶと、同じDOMにチャートが重なったり、エラーの原因になります。myChartがまだ無いときだけ初期化するようにして、最初の1回だけ描画する作りにしています。

3)リサイズ処理は「チャートが存在するときだけ」

window.addEventListener('resize', () => {
  if (myChart) myChart.resize();
});

まだ描画していない状態でresize()を呼ぶ必要はないので、myChartが作られているときだけresize()を実行します。

ステップ8:ボタンでグラフを切り替える

ステップ7では、ボタンを押したタイミングでグラフを描画する「遅延表示」を作りました。最後のステップ8では、それを発展させて、ボタン操作で表示するデータを切り替えられるグラフにしていきます。

みなと
みなと

実務でも「同じ場所で内容だけを切り替えて見せたい」場面はよくあるので、このパターンはそのまま応用できます。

ボタンを押すと
グラフが表示されます

ボタンを押すとグラフが表示され、もう片方のボタンを押すと表示内容が切り替わります。あわせて、選択中のボタンだけ見た目が変わり(is-active)、押せない状態になっています。

コード全体(HTML / CSS / JavaScript)

HTML

<div class="chart-switcher">
  <button id="serviceChartBtn" class="chart-tab" type="button">サービス紹介ページ</button>
  <button id="contactChartBtn" class="chart-tab" type="button">お問い合わせページ</button>
</div>

<div id="echartsContainer" class="echarts-container">
  <div class="chart-placeholder">
    <p class="chart-placeholder-text">ボタンを押すと<br>グラフが表示されます</p>
  </div>
</div>

CSS

.chart-switcher {
  display: flex;
  justify-content: center;
}

@media (min-width: 768px) {
  .chart-switcher {
    gap: 30px;
  }
}

@media (max-width: 767px) {
  .chart-switcher {
    gap: 15px;
  }
}

.chart-tab {
  display: block;
  position: relative;
  border: none;
  border-radius: 5px;
  padding: 15px 0;
  background: #c11a51;
  color: #fff;
  font: inherit;
  font-weight: 700;
  line-height: 1.4;
  text-align: center;
  cursor: pointer;
  appearance: none;
  transition: background-color 300ms, color 300ms;
}

@media (min-width: 768px) {
  .chart-tab {
    width: 200px;
    font-size: 16px;
  }
}

@media (max-width: 767px) {
  .chart-tab {
    flex: 1;
    width: 100%;
    font-size: 15px;
  }
}

.chart-tab::before {
  content: "";
  display: block;
  position: absolute;
  left: 50%;
  bottom: -12px;
  width: 15px;
  height: 12px;
  background: #f9e8ed;
  clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity 300ms;
}

@media (min-width: 768px) {
  .chart-tab:hover {
    background: #890631;
  }
}

.chart-tab.is-active {
  background: #f9e8ed;
  color: #c11a51;
  pointer-events: none;
}

.chart-tab.is-active::before {
  opacity: 1;
}

@media (min-width: 768px) {
  .echarts-container {
    height: 400px;
    margin-top: 40px;
  }
}

@media (max-width: 767px) {
  .echarts-container {
    height: 300px;
    margin-top: 30px;
  }
}

.chart-placeholder {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  border-radius: 5px;
  background-color: rgba(68, 102, 206, 0.09);
  text-align: center;
}

.chart-placeholder-text {
  color: #31345e;
  font-weight: 700;
  line-height: 1.6;
  opacity: 0.5;
}

@media (min-width: 768px) {
  .chart-placeholder-text {
    font-size: 26px;
  }
}

@media (max-width: 767px) {
  .chart-placeholder-text {
    font-size: 20px;
  }
}

JavaScript

(() => {
  const btnService = document.getElementById('serviceChartBtn');
  const btnContact = document.getElementById('contactChartBtn');
  const chartDom = document.getElementById('echartsContainer');

  if (!chartDom) return;

  let myChart = null;

  // ① 共通設定(UIや軸など)は baseOption にまとめる
  const baseOption = {
    textStyle: {
      color: '#333',
      fontFamily: '"Noto Sans JP", sans-serif',
      fontSize: 12,
      fontWeight: 300
    },
    legend: {
      itemGap: 20,
      itemWidth: 30,
      itemHeight: 6,
      selectedMode: false
    },
    grid: {
      right: 0,
      bottom: 0,
      left: 0
    },
    tooltip: {
      trigger: 'axis',
      valueFormatter: (value) => `${value} PV`,
      textStyle: {
        color: '#333',
        fontFamily: '"Noto Sans JP", sans-serif',
        fontSize: 12,
        fontWeight: 300
      },
      padding: 15
    },
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: ['月', '火', '水', '木', '金', '土', '日']
    },
    yAxis: {
      type: 'value'
    },
    media: [
      {
        query: { minWidth: 740 },
        option: {
          title: {
            textStyle: { fontSize: 18 }
          },
          legend: { top: 5, right: 0 },
          grid: { top: 55 }
        }
      },
      {
        query: { maxWidth: 739 },
        option: {
          title: {
            textStyle: { fontSize: 16 }
          },
          legend: { top: 40, left: 0 },
          grid: { top: 85 }
        }
      }
    ]
  };

  // ② ページ別の option を用意する(baseOption をベースに差分だけ足す)
  const optionService = {
    ...baseOption,
    title: {
      text: 'サービス紹介ページPV(今週 vs 先週)',
      top: 0,
      left: 0
    },
    series: [
      {
        name: '今週',
        type: 'line',
        data: [380, 980, 620, 1120, 1080, 1320, 1210], 
        lineStyle: { width: 2, color: '#c11a51' },
        itemStyle: { color: '#c11a51' },
        symbol: 'emptyCircle',
        symbolSize: 4,
        showSymbol: false,
        emphasis: { focus: 'series' }
      },
      {
        name: '先週',
        type: 'line',
        data: [640, 850, 760, 980, 780, 990, 880],
        lineStyle: { width: 2, type: 'dotted', color: '#4466ce' },
        itemStyle: { color: '#4466ce' },
        symbol: 'emptyCircle',
        symbolSize: 4,
        showSymbol: false,
        emphasis: { focus: 'series' }
      }
    ],
  };

  const optionContact = {
    ...baseOption,
    title: {
      text: 'お問い合わせページPV(今週 vs 先週)',
      top: 0,
      left: 0
    },
    series: [
      {
        name: '今週',
        type: 'line',
        data: [740, 880, 800, 980, 860, 1030, 1000],
        lineStyle: { width: 2, color: '#c11a51' },
        itemStyle: { color: '#c11a51' },
        symbol: 'emptyCircle',
        symbolSize: 4,
        showSymbol: false,
        emphasis: { focus: 'series' }
      },
      {
        name: '先週',
        type: 'line',
        data: [490, 750, 470, 680, 520, 860, 760],
        lineStyle: { width: 2, type: 'dotted', color: '#4466ce' },
        itemStyle: { color: '#4466ce' },
        symbol: 'emptyCircle',
        symbolSize: 4,
        showSymbol: false,
        emphasis: { focus: 'series' }
      }
    ]
  };

  // ③ option を切り替えて描画する関数
  function showChart(optionToSet, activeBtn) {
    if (!myChart) {
      myChart = echarts.init(chartDom);
    }
    myChart.setOption(optionToSet, true);

    // ボタンの見た目切り替え
    [btnService, btnContact].forEach(btn => btn.classList.remove('is-active'));
    activeBtn.classList.add('is-active');
  }

  btnService.addEventListener('click', () => showChart(optionService, btnService));
  btnContact.addEventListener('click', () => showChart(optionContact, btnContact));

  window.addEventListener('resize', () => {
    if (myChart) myChart.resize();
  });
})();

コードのポイント解説

1)共通部分をbaseOptionにまとめる

2つのグラフで共通する設定(文字・凡例・ツールチップ・軸・レスポンシブなど)は、baseOptionとしてまとめています。こうしておくと、差分(タイトルとseries)だけを切り替えれば良くなるので、管理が楽になります。

2)グラフごとのoptionを用意する

const optionService = { ...baseOption, title: {...}, series: [...] };
const optionContact = { ...baseOption, title: {...}, series: [...] };

baseOptionを土台にして、タイトルとseries(データ)だけを差し替えたoptionを2つ用意しています。切り替えたい内容が増えても、この形なら拡張しやすいです。

...baseOptionは共通の設定をまとめて引き継ぐための書き方で、そこにタイトルやデータだけを足しています。

3)setOption(option, true)で内容を入れ替える

myChart.setOption(optionToSet, true);

第2引数にtrueを渡すと、前の設定を残さずに新しいoptionへ切り替える動きになります。今回はデータ差し替えだけなのでtrueなしでもほぼ同じ表示ですが、切り替え内容が変わったときに古い設定が残るのを防ぐため、念のため付けています。

4)ボタンのactive状態も一緒に切り替える

[btnService, btnContact].forEach(btn => btn.classList.remove('is-active'));
activeBtn.classList.add('is-active');

グラフを切り替えるのと同時に、押したボタンだけactiveの見た目にする処理も入れています。これで「今どのグラフを見ているか」が明確になります。

まとめ

この記事では、Apache EChartsを使って折れ線グラフを作り、比較表示・デザイン調整・レスポンシブ対応・ボタン切り替えまで一通り実装しました。

ECharts は設定項目が多いぶん自由度が高く、基本の形を押さえれば、必要な機能を少しずつ足して「実務レベルの見やすいグラフ」に仕上げられます。今回の完成デモをベースに、表示データや見た目を自分の案件に合わせてカスタマイズしてみてください。

押していただけると励みになります!

ABOUT ME
みなと
みなと
フロントエンドエンジニア
東京のWeb制作会社で15年以上働いている現役フロントエンドエンジニアです。これまで、いろんなプロジェクトに関わりながら、フロントエンド開発やWebデザインに取り組んできました。このブログでは、今までの経験を活かして、Web制作に役立つ情報やノウハウをシェアしていきたいと思います。初心者の方から、現場で働く方まで、誰でも参考にできる内容をお届けしますので、ぜひ覗いてみてください。
記事URLをコピーしました