2
0

wheel イベント を見やすくしてみる - css custom variable を利用した 動的な色の変更アイディア

Last updated at Posted at 2024-08-06

作成サンプル

css custom variable で数値を渡すことができます。

css には calc() という 数値を計算する関数があります。

css には min() max() clamp() という値の範囲を制限する為の関数があります。

つまり、こういうことができます。

See the Pen wheel event example by juner clarinet (@juner) on CodePen.

※ code pen 領域でマウスのホイールや タッチパッドの スクロールジェスチャーを試してみてください。

ソース
<input id=scale type="number" min=1 value=200 placeholder="--base-scale" />
<ul id="target">
  <template id="template">
    <li>
      <span class="property"><span class="title">deltaMode:</span><span class="delta-mode"></span></span>,
      <span class="property"><span class="title">deltaX:</span><span class="delta-x"></span></span>,
      <span class="property"><span class="title">deltaY:</span><span class="delta-y"></span></span>,
      <span class="property"><span class="title">deltaZ:</span><span class="delta-z"></span></span>,
      <span class="property"><span class="title">wheelDelta:</span><span class="wheel-delta"></span></span>,
      <span class="property"><span class="title">wheelDeltaX:</span><span class="wheel-delta-x"></span></span>,
      <span class="property"><span class="title">wheelDeltaY:</span><span class="wheel-delta-y"></span></span>
    </li>
  </template>
</ul>
<script type="module">
{
  // #region base scale
  scale.valueAsNumber = Number(
    getComputedStyle(document.body).getPropertyValue("--base-scale"),
  );
  scale.addEventListener("input", updateBaseScale);
  updateBaseScale();
  function updateBaseScale() {
    const scaleNum = scale.valueAsNumber;
    const body = document.body;
    if (isNaN(scaleNum)) body.style.removeProperty("--base-scale");
    else body.style.setProperty("--base-scale", scaleNum);
  }
  // #endregion

  // #region wheel event
  document
    .querySelector(":root")
    .addEventListener("wheel", wheel, { passive: false });

  function wheel(e) {
    e.preventDefault();
    e.stopPropagation();

    (async ({
      deltaMode,
      deltaX,
      deltaY,
      deltaZ,
      wheelDelta,
      wheelDeltaX,
      wheelDeltaY,
    }) => {
      await timeout();
      const $temp = template.content.cloneNode(true);
      const $li = $temp.querySelector("li");

      // #region deltaMode
      (($e, deltaMode) => {
        $e.innerText = ((deltaMode) => {
          if (WheelEvent.DOM_DELTA_PIXEL === deltaMode)
            return `DOM_DELTA_PIXEL(${deltaMode})`;
          if (WheelEvent.DOM_DELTA_LINE === deltaMode)
            return `DOM_DELTA_LINE(${deltaMode})`;
          if (WheelEvent.DOM_DELTA_PAGE === deltaMode)
            return `DOM_DELTA_PAGE(${deltaMode})`;
          return `${deltaMode}`;
        })(deltaMode);
        $e.classList.add(`delta-mode-${deltaMode}`);
      })($li.querySelector(".delta-mode"), deltaMode);
      // #endregion

      // #region other property
      [
        [$li.querySelector(".delta-x"), deltaX],
        [$li.querySelector(".delta-y"), deltaY],
        [$li.querySelector(".delta-z"), deltaZ],
        [$li.querySelector(".wheel-delta"), wheelDelta],
        [$li.querySelector(".wheel-delta-x"), wheelDeltaX],
        [$li.querySelector(".wheel-delta-y"), wheelDeltaY],
      ].forEach((v) => {
        const [$e, value] = v;
        $e.innerText = value;
        $e.style.setProperty("--value", value);
      });
      // #endregion

      target.insertAdjacentElement("afterbegin", $li);
      const removeTarget = target.querySelectorAll(":nth-child(1n+100 of li)");
      for (const rt of [...removeTarget]) rt.remove();
    })(e);
  }
  // #endregion
  function timeout(milliseconds) {
    return new Promise((resolve) => setTimeout(resolve, milliseconds));
  }
}
</script>
<style>
:root {
  /** delta の 色変換用スケール */
  --base-scale: 500;
  height: 100vh;
  width: 100vw;
  overflow: hidden;
  display: flex;
  flex-flow: column nowrap;
  container-type: size;

  body {
    display: contents;
    ul {
      flex: 1 1 auto;
      display: flex;
      flex-flow: column nowrap;
      padding: 0;
      margin: 0;
      overflow: hidden;
      gap: 2px;
      li {
        .delta-mode {
          color: var(--delta-mode-color, currentcolor);
          background-color: var(--delta-mode-background-color, transparent);
          &.delta-mode-0 {
            --delta-mode-color: white;
            --delta-mode-background-color: green;
          }
          &.delta-mode-1 {
            --delta-mode-background-color: yellow;
          }
          &.delta-mode-2 {
            --delta-mode-color: white;
            --delta-mode-background-color: blue;
          }
        }
        .property {
          display: inline flow-root;
        }
        .title {
          font-size: 0.5em;
        }
        .delta-x,
        .delta-y,
        .delta-z,
        .wheel-delta,
        .wheel-delta-x,
        .wheel-delta-y {
          background-color: oklab(
            100% 0 clamp(-0.5, calc(var(--value) / var(--base-scale)), 0.5)
          );
        }
      }
    }
    & > ul {
      pointer-events: none;
    }
  }
}
</style>

ポイント

色の調整方法

色変換については次の様なコードで実現しています。

.delta-x,
.delta-y,
.delta-z,
.wheel-delta,
.wheel-delta-x,
.wheel-delta-y {
  background-color: oklab(
    100% 0 clamp(-0.5, calc(var(--value) / var(--base-scale)), 0.5)
  );
}

--base-scale は :root で定義しており、 動作見た感じ 200 に設定してあります。そのうち js で変更できると面白いかもしれません。

--value は js から設定している各 delta の値です。

oklab() は 色 関数の一つで今回の L a b の b ……つまり 青と黄色の色合いの値を 算出しています。

値の範囲が外に出てしまうと不都合の場合は clamp() 関数で 最大と最小を範囲に収めるとよいです。

要素数の制限

要素数の制限は次の様に実装してあります。

const removeTarget = target.querySelectorAll(":nth-child(1n+100 of li)");
for (const rt of [...removeTarget]) rt.remove();

:nth-child(1n+100 of li) により ul#target 内にある 100 件目以降の li 要素を削除対象として逐次削除を行っております。

以上。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0