できるだけ難しいことを考えずに shadergradient を使う

## この記事の概要

shadergradient は、美しくアニメーションするグラデーションを生成できる React のライブラリです。[^figma-plugin-and-framer-project]

[^figma-plugin-and-framer-project]: Figma のプラグインや Framer 上で動かせるコードコンポーネントも存在しますが、大概は自前のコードで動かすことになると思うので、この記事では React での話を中心に扱います。

言葉で説明するよりも公式のサイトを見てもらった方が通じると思うので、以下のリンクからご覧ください。

https://www.shadergradient.co/

このようにとても綺麗なグラデーションを実装できるのですが、ドキュメントが若干取っ付きづらいです。

細かくカスタムし倒すというよりは、まずは難しいことを抜きにして導入できるように、手順を記事にしてみました。

## 環境

簡単にプロジェクトをセットアップするために Vite を使います。

```sh
npm create vite@latest
```

その前提で、主要なものだけを記載します。

| 依存関係 | バージョン |
| --- | --- |
| vite | 5.3.1 |
| react | 18.3.1 |
| react-dom | 18.3.1 |
| shadergradient | 1.2.14 |

## shadergradient のインストール

```sh
npm i shadergradient
```

一旦、最低限動かすにはこれだけで足ります。

公式ドキュメントのインストールコマンドには色々なライブラリが列挙されている上、example では更に未紹介のライブラリが使われていますが、ひとまず`shadergradient`だけあれば動きます。

## 最低限の使用

`create vite`で自動生成された`src/App.jsx`にコードを追加します。

```diff_jsx:src/App.jsx
  import { useState } from "react";
  import reactLogo from "./assets/react.svg";
  import viteLogo from "/vite.svg";
  import "./App.css";
+ import { ShaderGradientCanvas, ShaderGradient } from "shadergradient";
  
  function App() {
    const [count, setCount] = useState(0);
  
    return (
      <>
        <div>
          <a href="https://vitejs.dev" target="_blank">
            <img src={viteLogo} className="logo" alt="Vite logo" />
          </a>
          <a href="https://react.dev" target="_blank">
            <img src={reactLogo} className="logo react" alt="React logo" />
          </a>
        </div>
        <h1>Vite + React</h1>
        <div className="card">
          <button onClick={() => setCount((count) => count + 1)}>
            count is {count}
          </button>
          <p>
            Edit <code>src/App.tsx</code> and save to test HMR
          </p>
        </div>
        <p className="read-the-docs">
          Click on the Vite and React logos to learn more
        </p>
+       <ShaderGradientCanvas
+         style={{
+           position: "absolute",
+           inset: 0,
+           zIndex: -1,
+         }}
+       >
+         <ShaderGradient />
+       </ShaderGradientCanvas>
      </>
    );
  }
  
  export default App;
```

この時点ではこのような違いが生まれます。

| Before | After |
| --- | --- |
| ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/5377de06-5dd1-02ab-0109-f61734c5d8ae.png) | ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/e26516df-4b3e-b3b9-67a2-5332048e600a.png) |

静止画でも綺麗ですし、実際はアニメーションしているので、なお見栄えがします。

## 簡単にカスタマイズする

### ビジュアル編集

まずはこちらの URL にアクセスします。

https://www.shadergradient.co/customize

すると画面下部にカスタムできるエリアが出てきます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/04074659-df47-8dd0-3161-1b2feedaf5cc.png)

ランダムに弄っているだけで良い感じになるので、一旦細かいことは考えなくて良いと思います。[^available-gradient-properties]
私は今回こんな見た目で一旦キリをつけました。

[^available-gradient-properties]: そうは言っても、ちゃんと理解したいという場合はこちらの一覧をご覧ください。 https://github.com/ruucm/shadergradient?tab=readme-ov-file#available-gradient-properties-types

見た目に納得が行ったら、操作エリア右下の`Copy URL`をクリックします。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/e6e38fca-a924-f8a9-8a09-a82f230c4044.png)

### コード編集

コードに戻り、先ほどの`ShaderGradient`を次のように編集します。
`urlString`で指定している内容は`Copy URL`によってクリップボードにコピーされた内容です。

```diff_jsx:src/App.jsx
  <ShaderGradientCanvas
    style={{
      position: "absolute",
      inset: 0,
      zIndex: -1,
    }}
  >
-   <ShaderGradient />
+   <ShaderGradient
+     control="query"
+     urlString="https://www.shadergradient.co/customize?animate=on&axesHelper=on&bgColor1=%23000000&bgColor2=%23000000&brightness=1.5&cAzimuthAngle=0&cDistance=0&cPolarAngle=90&cameraZoom=8&color1=%23ff7a33&color2=%2333a0ff&color3=%23ffc53d&destination=onCanvas&embedMode=off&enableTransition=false&envPreset=dawn&format=gif&fov=30&frameRate=10&grain=off&http%3A%2F%2Flocalhost%3A3002%2Fcustomize%3Fanimate=on&lightType=3d&pixelDensity=1&positionX=0&positionY=0&positionZ=0&range=disabled&rangeEnd=40&rangeStart=0&reflection=0&rotationX=0&rotationY=0&rotationZ=0&shader=defaults&type=sphere&uAmplitude=1&uDensity=2&uFrequency=5.5&uSpeed=0.1&uStrength=1&uTime=0&wireframe=false&zoomOut=false"
+   />
  </ShaderGradientCanvas>
```

これにより、見た目が変わります。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/e1820661-e843-2392-e93b-375a6a70a38b.png)

URL を貼り付ける形式だと、更新のしやすさや`https://www.shadergradient.co`が落ちたときのことが不安になるかもしれません。
ただ、実はそれぞれのパラメーターがたくさん記載されていて、それを読んでいるだけなのです。

ですから仮にインターネットに繋がっていなかろうとも、それぞれの値が妥当であればこのコードは問題なく動きます。[^format-url-string]

[^format-url-string]: なんと`'https://shadergradient.co/customize'` といった文字列を空文字列に変換する処理があります。https://github.com/ruucm/shadergradient/blob/main/packages/shadergradient/src/utils.ts#L9

### おまけ：より複雑な編集

`control="query"`だと、実装が簡単な分自由度は少し低いです。

用意されているプロパティのうち、ビジュアル編集パネルからでは触れないものがあったり、[^visual-uneditable]何かをトリガーに位置や色を変えたいときに取り回しがしづらかったり[^ease-of-operation]……。

[^visual-uneditable]: 例えば`enableTransition`は編集パネルからは触れないため、描画されるたびにアニメーションしながら画面に入ってきます。ただ、urlString を直接書き換えれば対応はできます。できるんですが、やはり多少面倒くさいです。

[^ease-of-operation]: `urlString`をテンプレートリテラルで書けば編集できますが、複数のパラメーターを条件にあわせて触るような場合、どうしても文字列の長さがやたら長くなって視認性は悪いと思います。

ですから`control="props"`に変えて、urlString の中身をチマチマと props に置き換える方が、最終的には自由度が高くなると思います。[^property-name]

[^property-name]: 編集パネルでは`Noise Deinsity`という名前だけど`urlString`では`uDensity`になっているなど、名前が一致していないプロパティもあるので、多少の気合は必要です。`u`ってなんだよ？と思った方は WebGL の uniform 変数について調べてみてください。簡単に言えば、慣習的な命名規則です。

もし今回の記事を読んで興味を持ってくださいった方がいれば`control="props"`も試してみてください。