🍥

ランダムなカラーグラデーションの生成

2024/06/29に公開

目標

  • スクロールに応じて要素のカラーグラデーションが変化するアニメーションの実装。この時、グラデーション角度も都度変更する
    アニメーション説明

背景

以前からJavaScriptのWebAPIである「IntersectionObserver」を用いたスクロールイベントの制御に触れようと思っていたので、その一環です。
インタラクティブなアニメーションをストックして実務に活かせたらと考えています。

前提条件

IntersectionObserverを利用

CODE

◼️HTML

<div class="clm">
  <div class="box">
    <p class="plain">要素のグラデーションが変化</p>
  </div>
  <div class="box">
    <div id="colorBox" class="color_box">
      <!-- この要素はスクロールに基づきグラデーションカラーが変更 -->
    </div>
  </div>
</div>

colorBoxというidを取得してカラーグラデーションを付与するアニメーションを実装します。

◼️JavaScript

document.addEventListener('DOMContentLoaded', function() {
  // BOXのグラデーションカラーをスクロールに合わせて変化させる
  // 1. IntersectionObserverのoptionに独自関数を設定
  const options = {
    threshold: buildThresholdArry()
  }

  // 2. "1"を20等分にして配列に格納する関数
  function buildThresholdArry() {
    let thresholds = [];
    let numSteps = 20;

    for (let i =1; i <= numSteps; i++) {
      let ratio = i / numSteps;
      thresholds.push(ratio);
    }
    return thresholds;
  }

  // 3. ランダムなRGBカラーを生成するコード
  function randomColorFactory(entries) {

    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        let ratio = entry.intersectionRatio;

        const randomValues = Array.from({ length: 9 }, () => Math.floor(Math.random() * ratio * 256));
        const randomDegree = Math.floor(Math.random() * 360);

        const color1 = `rgb(${randomValues[0]}, ${randomValues[1]}, ${randomValues[2]})`;
        const color2 = `rgb(${randomValues[3]}, ${randomValues[4]}, ${randomValues[5]})`;
        const color3 = `rgb(${randomValues[6]}, ${randomValues[7]}, ${randomValues[8]})`;

        const target = document.getElementById("colorBox");
        target.style.background = `linear-gradient(${randomDegree}deg, ${color1} 0%, ${color2} 100%, ${color3} 200%)`;
      }
    })

  }
  
  const observerBox = new IntersectionObserver(randomColorFactory, options);
  const targetBox = document.getElementById("colorBox");

  observerBox.observe(targetBox);

});

◼️イメージ
アニメーションイメージ

コード分割・解説

  1. IntersectionObserverについて
    交差監視API。従来のスクロールイベントはスクロールのするたびに発火するのでパフォーマンス制御が難しいことが懸念点でした。対象要素が特定の要素と交差したタイミングを検知し関数を実行することで、ビューポートのサイズに影響することなく、常に同じタイミングで発火可能、無駄なメモリ消費を抑えることができます。
const options = {
  root: document.querySelector("#observerArea"),
  rootMargin: "10px",
  threshold: 0
};

const observer = new IntersectionObserver(callback, options);

適当な定数(変数)にIntersectionObserverコンストラクターを呼び出します。第1引数は交差タイミングで実行したいコールバック関数を、第2引数は各オプションの設定したオブジェクトを返します。オプションは以下の通り

  • root : 交差監視する枠を設定できます。デフォルトではビューポートを監視します。
  • rootMargin : rootで設定した交差起点からの距離です。距離を指定すると、交差判定を数値分拡大します。
  • threshold : 第1引数で指定するコールバック関数を呼び出したい交差割合を指定できます。開始点0、交差終了1の0 ~ 1の範囲で指定可能。
  1. IntersectionObserverのoptionに独自関数を設定
const options = {
  threshold: buildThresholdArry()
}

今回はスクロールに応じてカラーを変更する関数を呼び出したいので、複数回関数を呼びだすように設定する必要があります。そこで、オプションのthresholdプロパティに複数回コールバック関数を呼び出すような関数を設定します。

  1. "1"を20等分にして配列に格納する関数
  function buildThresholdArry() {
    let thresholds = [];
    let numSteps = 20;

    for (let i =1; i <= numSteps; i++) {
      let ratio = i / numSteps;
      thresholds.push(ratio);
    }
    return thresholds;
  }

今回は20分割にして配列に格納しています。numStepsに代入する数値は適宜変更してください。

  1. ランダムなRGBカラーを生成するコード
function randomColorFactory(entries) {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      let ratio = entry.intersectionRatio;

      const randomValues = Array.from({ length: 9 }, () => Math.floor(Math.random() * ratio * 256));
      const randomDegree = Math.floor(Math.random() * 360);

      const color1 = `rgb(${randomValues[0]}, ${randomValues[1]}, ${randomValues[2]})`;
      const color2 = `rgb(${randomValues[3]}, ${randomValues[4]}, ${randomValues[5]})`;
      const color3 = `rgb(${randomValues[6]}, ${randomValues[7]}, ${randomValues[8]})`;

      const target = document.getElementById("colorBox");
      target.style.background = `linear-gradient(${randomDegree}deg, ${color1} 0%, ${color2} 100%, ${color3} 200%)`;
    }
  })
}

グラデーションを生成するRGB値の数値やグラデーションの角度は、thresholdオプションで決定したコールバック関数の発火タイミングで都度ランダムに変更されるように設定しました。
entriesはIntersection Observerのコールバック関数に渡される監視対象の要素(群)のオブジェクト配列であり、こちらを反復処理しています。

最後に

今回作成した乱数は、"0"から"1"の不可逆的な数値に依存するため、発火タイミングによって取りうる数値の範囲にばらつきが生じてしまっています。こちらの問題を解決することが今後の課題です。

参考

Discussion