SVGに順番に色をつける方法!用意したJSONの値を使ってcssを操作するしくみ[JavaScript, SVG]

SVGに順番に色をつける方法!用意したJSONの値を使ってcssを操作するしくみ[JavaScript, SVG]

SVG内のシェイプに、JavaScriptで順番に色をつけていく

まずは次の動画をご覧ください。
世界地図のSVGを用意したのですが、そこに、JSONの値を基準にJavaScriptで次々に色を塗っていく、というサンプルです。
※このサンプルでは、例として野生のカピバラの生息地に色をつけてみました。

↑こちら、いかがでしょうか。

扱う地図の種類を変えたり、
工夫次第では、いろいろなビジュアル表現に役立つ手法だと思います。

たとえば、「もっと地域をクローズアップした地図のSVG」を用意して、その地域の色を塗ってあげるなど。
旅行サイトの案件や、観光地を紹介する案件などでも利用ができそうですね。

SVGに順番に色をつけるしくみ

↑冒頭の動画ですが、

こちらがどういう仕組みで動いているかというと、

・色をつけたい場所を定義した、JSONファイルを用意する
・そのJSONをJavaScriptで読み込み、JSONの値の数だけ順番にclass名をつけていく

と言った仕組みになります。
(※ちなみに、JSONを使わない場合は、JavaScript内に同じ意味の配列を持たせてあげる形でも動きます。)

ただ、いきなり「複雑な形の地図SVG + そこに着色」だとわかりづらいと思いますので、

次のセクションからは、
「15個の単純な正方形が並んでいるSVG」に、「順番に色をつけていくサンプル」を例に、
この仕組みについて書いていきたいと思います。

単純な正方形が並ぶSVGで見る、順番に色をつけるしくみ

こちらの動画をご覧ください。

↑こちらは「15個の単純な正方形が並んでいるSVG」に、
「順番に色をつけていくサンプル」になります。

次のセクションから、
・JSON
・JavaScript
・html
・svg
・css

の順番に、
どういったコードが必要か書いていきたいと思います。

色をつけたい場所を定義した、JSONファイル

まずは、JSONを外部ファイルとして用意します。

このサンプルではとにかく「色をつけたい場所が定義」されていれば良いので、

たとえば次の様なJSONがあれば最低限大丈夫です。
(ちなみに便宜上、”id”: という項目も値として持たせてますが、
このサンプルでは使ってないのでなくても良いです。”shape”: の値だけがあれば最低限動きます)

※コードが長いので「…中略…」としていますが、
実際は6~14番目が、同じフォーマットで
「…中略…」の部分に入り、計15個の要素を持つJSONになります。

(※ちなみに、もしJSONを使わない場合は、JavaScript内に同じ意味合いの配列を持たせてあげる形でも動きます。)

{
  "svgsample": [
    {
      "id": 1,
      "shape": "shape1"
    },
    {
      "id": 2,
      "shape": "shape2"
    },
    {
      "id": 3,
      "shape": "shape3"
    },
    {
      "id": 4,
      "shape": "shape4"
    },
    {
      "id": 5,
      "shape": "shape5"
    },
    ...(中略。実際はここに6~14番目が、同じフォーマットで入ります)...
    {
      "id": 15,
      "shape": "shape15"
    }
  ]
}

↑こちらのJSONを外部ファイルとして
/assets/json/ というディレクトリに data-fifteen.json という名前で保存、

それを次のセクションで書いていくJavaScriptで読み込んで使用します。
(※ディレクトリ名やファイル名は、分かりやすいものであれば何でも良いです)

SVGに順番に色をつけるJavaScript

JavaScript側は、たとえば次の様に書きます。

最初の関数 liveColoring() は、JSONを読み込む関数です。
後半にある関数 liveColoringOrder() は、読み込んだJSONの値を元にして
色をつけたいSVGのシェイプを取得。順番に is-active というclassを付与してあげて、
次々に色をつけていく関数になっています。
(※ちなみに、もしJSONを使わない場合は、JavaScript内に同じ意味合いの配列を持たせてあげる形でも動きます。その場合は少し書き換えが必要ですが、
それはまた 次の記事 で取り扱いたいと思います。)

// JSONを読み込む記述
async function liveColoring(){
  const jsonPath = "/assets/json/data-fifteen.json";
  const jsonRequest = new Request(jsonPath);
  const resultRequest = await fetch(jsonRequest);
  const resultJson = await resultRequest.json();

  liveColoringOrder(resultJson.svgsample);
}

// ループするたびにディレイをかける変数。初期値は必須
let delayNumber = 0;

// JSON内の配列に対してループを記述する
const liveColoringOrder = (elements)=> {
  elements.forEach((j) => {
    const attachmentShape = document.querySelector(`.${j.shape}`);

    setTimeout(() => {
      attachmentShape.classList.add("is-active");
    }, delayNumber);
    
    delayNumber += 100;
  });
}

// ページが読み込まれたらJSON読み込みの関数を実行
window.addEventListener("load", () => {
  liveColoring();
});

htmlの用意

htmlとしては、次の構成があれば最低限良いです。

重要なのは、「ここにSVGをコードとして埋め込みます」と記載のコメントアウト部分に、
この後のセクションでご紹介するSVGをコードとして配置する、という点だけです。

<section>
  <div class="shape-container">
    <div class="shape-svg-parent">
      <!--ここにSVGをコードとして埋め込みます-->
    </div>
  </div>
</section>

SVGのコード

さて、SVGはこんな感じのものを用意します。

あらかじめ書いておきますが、「インラインSVGはコードが長い」です。

それはさておき、これを上のセクションの
「ここにSVGをコードとして埋め込みます」と記載のコメントアウト部分に貼り、
ブラウザで開くと白い正方形が15個並んだ状態が描画されます。

なお実際、こう言った用途のSVGを用意する時は、
Illustratorで希望する図形を配置したドキュメントを作り、それをSVG形式で書き出します。

そしてそのファイルをVSCodeで開き、コードとして取得する形が比較的スムーズで良いかと思います。

<svg id="layer" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1366 768">
<g>
<rect class="shape1 default-fill" x="483.5" y="184.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line1 cls-1" d="M553,185c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M553,184h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape2 default-fill" x="563.5" y="184.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line2 cls-1" d="M633,185c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M633,184h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape3 default-fill" x="643.5" y="184.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line3 cls-1" d="M713,185c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M713,184h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape4 default-fill" x="723.5" y="184.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line4 cls-1" d="M793,185c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M793,184h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape5 default-fill" x="803.5" y="184.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line5 cls-1" d="M873,185c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M873,184h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape6 default-fill" x="483.5" y="264.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line6 cls-1" d="M553,265c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M553,264h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape7 default-fill" x="563.5" y="264.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line7 cls-1" d="M633,265c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M633,264h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape8 default-fill" x="643.5" y="264.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line8 cls-1" d="M713,265c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M713,264h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape9 default-fill" x="723.5" y="264.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line9 cls-1" d="M793,265c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M793,264h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape10 default-fill" x="803.5" y="264.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line10 cls-1" d="M873,265c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M873,264h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape11 default-fill" x="483.5" y="344.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line11 cls-1" d="M553,345c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M553,344h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape12 default-fill" x="563.5" y="344.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line12 cls-1" d="M633,345c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M633,344h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape13 default-fill" x="643.5" y="344.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line13 cls-1" d="M713,345c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M713,344h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape14 default-fill" x="723.5" y="344.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line14 cls-1" d="M793,345c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M793,344h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
<g>
<rect class="shape15 default-fill" x="803.5" y="344.5" width="79" height="79" rx="9.5" ry="9.5"/>
<path class="line15 cls-1" d="M873,345c4.96,0,9,4.04,9,9v60c0,4.96-4.04,9-9,9h-60c-4.96,0-9-4.04-9-9v-60c0-4.96,4.04-9,9-9h60M873,344h-60c-5.5,0-10,4.5-10,10v60c0,5.5,4.5,10,10,10h60c5.5,0,10-4.5,10-10v-60c0-5.5-4.5-10-10-10h0Z"/>
</g>
</svg>

サンプルcss

cssは、こんな感じのものを用意しておきます。

ポイントとしては、JavaScriptで付与する is-active というclassです。
これが順番に「0.数秒おき」に付与されることにより、
SVGに順番に色がついていく様に見えます。

自然な着色のアニメーションをさせるため、フェードイン効果として
transition: fill 0.1s, opacity 1s;
を与えています。

.shape-container {
  margin: 2rem auto;
  width: fit-content;
}
.shape-svg-parent {
  width: 1366px;
}

/* ここからSVG内のスタイル */
.cls-1 {
  fill: #fff;
}
/* SVG内の図形のデフォルトスタイル */
.default-fill {
  fill: #fff;
  opacity: 0;
  transition: fill 0.1s, opacity 1s;
}
/* この is-active がJSにより付与されると着色される */
.default-fill.is-active {
  fill: rgb(65, 175, 190);
  opacity: 1;
}

以上で、次の動画の様なサンプルになります

ここまでのセクションで、

・JSON
・JavaScript
・html
・svg
・css

の順番に、必要な要素を書いてきました。
これらを組み合わせることにより、
「15個の単純な正方形が並んでいるSVG」に、「順番に色をつけていく」
次の動画の様なサンプルになります。

留意点:サンプルが動かない場合

単純に、この記事のサンプルをhtmlに貼り付けて試してみようとしても

貼り付けたhtmlをそのままブラウザで開くだけでは
ブラウザが持っているセキュリティ機能がはたらくため(「CORS」エラーが出る)
動きません。

・サーバーにアップして試してみる
・localサーバーを立ち上げる(これは、gulpを使ったり色々な方法があり検索すればすぐ出るため、この記事では割愛します)
・Safariで開き、「設定 > デベロッパー」から、一時的にクロスオリジンに関する項目を止めてリロード

などの方法で試してみることができます。

地図SVGへの応用をするには

ここまでのセクションの記述で、
「15個の単純な正方形が並んでいるSVG」に、「順番に色をつけていくサンプル」
ができたことになります。

さて、本題としてはこの仕組みを使って、冒頭の動画にある様な
「地図のSVGに順番に色をつける」と言う話です。

これに関しては、「15個の単純な正方形が並んでいるSVG」に、「順番に色をつけていくサンプル」と同じ仕組みで実現が可能になります。

必要な手順は次のとおりです。

1. 色をつけたい地図SVGをコードとして用意する(Illustrator等でSVG変換するのがスムーズかと思います)
2. 色をつけたい地図SVGのコード内に、正方形の場合と同様に、あらかじめclassを付けておく
3. この記事のサンプルで書いた様なJavaScriptを用意する
4. 正方形の場合と同様に、色をつけたい 2. のclass名と対応している外部JSONファイルを用意する
5. JavaScriptを実行すると色が順番につけることができる

という形で、地図SVGにも順番に色をつけていく表現が可能になります。

この記事のまとめ

今回は、冒頭の動画でご紹介した様な、
「SVGに順番に色をつける方法」について記事を書いてみました。

最初のセクションで書いた通りですが

扱う地図の種類を変えたり、
工夫のしかた次第では、いろいろなビジュアル表現に役立つ手法だと思います。

繰り返しになりますが、
たとえば「もっと地域をクローズアップした地図のSVG」を用意して、その地域の色を塗ってあげるなど。
旅行サイトの案件や、観光地を紹介する案件などでも利用ができそうですね。

この記事が皆さんのより良いWeb制作体験につながれば、嬉しく思います。

※この記事は内容の品質向上を目的に、随時更新を行う場合があります。

この記事をシェアする: