Hiroaki Satou profile image Hiroaki Satou

ASTROでGSAPを使ったコンポーネントを作るときに気をつけること

GSAPをASTROのコンポーネントに組み込んだので気づいたことをつらつらと書きました。ASTROの柔軟性が伝わるでしょうか。Next.jsに浮気しそうだったけれど、改めてASTROの良さを再確認しました。

ASTROでGSAPを使ったコンポーネントを作るときに気をつけること
ASTRO PIXEL TRANSITION Before Screen
0:00
/0:08

GSAP Image Transision

上のムービーのようなトランジションをASTROで書いて分かった注意するべき原則をメモしておきます。OSMOのサンプルコードを使用しているのでコード全体は載せません

では、原則をまとめてみましょう

1. ASTROはコンポーネント(ASTROコンポーネント)内に</script>タグでスクリプトを書くと、DOMを読み込んだ後に実行される

ASTROは何もしなくてもコンポーネント内のTypeScriptをcomposeにして分けて、defer評価します。なので
```document.addEventListener('DOMContentLoaded', () => {```
のような評価は不要です。

<script is:inline>

と書くと通常のscriptタグと同じようにDOMと順番が入れ替わったりするようです。

2. ASTROはTailwindCSSや</style>タグをDOMの生成時に適用したら、コンポーネントをJavaScriptで適用してもスタイルシートの再適用はしない

Astroコンポーネントでスタイルを生成した要素に適用するときは、全てのスタイルを適用しないといけません。スタイルが勝手に適用されるなどと思ってはいけません。つまりアニメーション部分だけが再レンダリングされるのです。

          const pixel = document.createElement('div');
          pixel.classList.add('pixelated-image-card__pixel');
          pixel.style.width = `${pixelSize}px`;
          pixel.style.height = `${pixelSize}px`;
          pixel.style.top = `${row * pixelSize}px`;
          pixel.style.left = `${col * pixelSize}px`;
          pixel.style.position = 'absolute';
          pixel.style.backgroundColor = 'orange';
          pixel.style.display = 'none';

例えばこのように細かく書かないとバグの原因になります。

3 コンポーネントの<style>タグの名前空間はコンポーネント内でのみ有効

ここがNext.jsと違うところです。私は可読性の面からTailwindCSSを使い、GSAPで上書きするスタイルシートだけstyleタグ内に書きました。なぜかというと、TailwindCSSが評価された後でStyleタグの内容で上書きされるからです。本来、全て順番に動作するのでコンポーネント内で名前の衝突は起きませんが、GSAP側でもCSSを使ってDOMのプロパティを設定するので、Tailwindと名前が違うと、プロパティ名などの誤記の原因になります。私はTailwindにdisplay-noneというクラスを書いて、危うくバグを作り込むところでした。

4. ASTROは&ltdiv data-pixelated-image-reveal-grid=""のようなJavaScriptのタグも問題なく使える

クラス名はTailwindCSSと混在すると紛らわしいので、クラス名は避けてdataプロパティをつけて、dataプロパティ名でTypeScript側からQuerySelectしたほうが混乱が避けられて奇麗なコードになります。

    <div data-pixelated-image-reveal-grid="" 
    class="w-full h-full absolute top-0 left-0 z-20">
      <div class="pixelated-image-card__pixel"></div>      
    </div>

このようにclassを使用していいところ、styleセクションでスタイルを指定するというところという風に使い分けると良いと思います。

 const pixel = document.createElement('div');
          pixel.classList.add('pixelated-image-card__pixel');

このようにJavaScriptからクラスを追加した要素を作るのでクラスを用いています。また空の要素を入れることで、スタイルシートの間違いの早期発見や不要なnullエラーを防止出来ます。

5. ASTROのscriptタグ内のコードはデフォルトでTypeScript

といっても、ほとんど推測で型指定してくれるので、あまり意識しなくても大丈夫です。ただし、メソッドを呼び出す時はDOM要素はnullになる場合があることを理由にエラーを出してきます。小さなコンポーネントでDOMがあることが分かり切っているのなら

      const activeCard = card.querySelector('[data-pixelated-image-reveal-active]') as HTMLDivElement;

このようにasでキャストしても、HTMLを見れば明らかにあるので問題ないと私は思っています。

            activeCard!.style.display = isActive ? 'block' : 'none';

こういう場合の!マークの使用も同様です。

以上、簡単に気をつけたいことで今日続いたことをまとめました。また書いていくと出てくると思うので続きがあるかもしれません。ところで、こういった使い方をGSAPで出来るところは、ASTROの柔軟性で良きところかもしれません。Next.jsも多少分かるので、サンプルコードがNext.jsを使っていたら、参考に書きますが、ことGSAPのようにAPIに大きな変化がないライブラリでは、名前空間がコンポーネントに閉じられていることやDOMが直接操作できる利便性のほうが勝ると思います。