2024.08.09[Fri]

Adobe Animate CCとPixiJSを連携するプラグインの紹介【pixi-animate-container】

    目次

    はじめまして。webデザイナーの浅野です。

    アニメーション制作ソフトAdobe Animate CC(以下Adobe Animate)と、JavaScriptのアニメーションライブラリの”PixiJS”を連携するプラグイン、”pixi-animate-container”について、実際のプロジェクトで使用する機会がありました。そのため、この記事では、その使用方法と注意点をまとめてみました。

    pixi-animate-containerを使用した経緯

    弊社の案件において、複雑なアニメーションを要するページ制作のご依頼をいただきました。通常はCSSやJavaScript(以下、JS)だけで十分実装できますが、今回の要件は実装が困難でした。そのため、弊社のデザイナーにAdobe Animateというソフトでアニメーションを作成してもらい、それをブラウザ上で再生可能な形にすることにしました。

    また、CSSとJSだけで実装するアニメーションの量も多く、ページのパフォーマンスがかなり厳しくなると予想されており、可能な限りアニメーションライブラリを通して動かすことで、パフォーマンス向上を図る必要もありました。ライブラリは要件を考慮し、二次元のアニメーションに特化したPixiJSが候補に上がります。

    しかしここで問題が発生します。Adobe Animateで作成したアニメーションはPixiJSでは動かせず、CreateJSというライブラリでしか動作しません。もちろんAdobe AnimateだけCreateJSで実装し、他のアニメーションはPixiJSで実装するという方法も取れますが、これではPixiJSとCreateJSが混在して管理が大変になります。できればPixiJSのみで管理したい。

    この問題を解決するため、tawatawa様が作成された"pixi-animate-container"というプラグインを利用することにしました。このプラグインにより、Adobe Animateで作成したアニメーションもPixiJS上で動かすことができ、アニメーションはPixiJSのみで統一することが可能になりました。

    pixi-animate-containerの使い方

    プラグインの作者であるtawatawa様が、すでにご自身のQiitaGitHubで解説を行っていますが、実際に使用してみて補足したい点もありましたので、本稿でも改めて解説いたします。tawatawa様の記事と併せて読むことで、より深い理解が得られるかと思います。

    今回は以下の順にお伝えしていきます。

    1. プラグインを使用してブラウザ上にアニメーションを表示させる
    2. ブラウザでアニメーション自体の大きさや座標を変更する
    3. ブラウザでアニメーションを一時停止、再生する

    1.プラグインを使用してブラウザ上にアニメーションを表示させる

    まず、Adobe Animateからパブリッシュしたファイルを用意し、必要なファイルをHTMLの<head>タグ内で読み込みます。 読み込み順序を誤ると、正常に動作しない可能性があるため、注意してください。

    <!-- 以下をheadタグ内に記述 -->
    <script src="https://code.createjs.com/1.0.0/createjs.min.js" defer="defer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.3.2/pixi.min.js" defer="defer"></script>
    <script src="pixi-animate-container.min.js" defer="defer"></script>
    <script src="パブリッシュしたコンテンツ一式のjsファイル" defer="defer"></script>

    Adobe Animateのパブリッシュ方法については本稿では解説しませんが、正しくパブリッシュした場合、HTMLとimageフォルダ、そしてJSファイルが同梱されたフォルダが生成されるはずです。また、Adobe Animateからアニメーションをパブリッシュした際に生成されるHTMLファイルは不要なので、削除しても問題ありません。pixi-animate-container.min.jsはtawatawa様のGitHubからダウンロードしてください。

    次にPixiアプリケーションの作成です。この部分も通常通り進めます。

    const app = new PIXI.Application({
            width: 600, // 要素の幅
            height: 600, // 要素の高さ
            antialias: true,
            resolution: window.devicePixelRatio
          });
    

    次にアニメーションの初期化です。ここまでやればアニメーションはブラウザ上で動作するはずです。

    const instances = {}; //座標や大きさ変更のために使用する
    
    PIXI.animate.loadAssetAsync([
              {
                id: "Adobe Animateコンテンツのjsファイルに記述されている 'lib.properties'のid(32文字くらいの英数字)",
                basepath: "この処理が書かれているjsファイルから見て、Animateコンテンツのjsファイルが入っているディレクトリへの相対パス",
                options: {
                  crossOrigin: false,
                },
              }
            ]).then(function (lib) {
              class Root extends PIXI.animate.Container {
                constructor() {
                  super(app.ticker);
                  instances.flask = this.addCreatejs(new lib.flask()); //lib.の後は適宜変更
                }
              }
              // ステージにRootコンテナを追加
              app.stage.addChild(new Root());
              // 指定したhtml要素にcanvasを挿入
              document.body.appendChild(app.view);
            });
    

    以下の箇所については適宜書き換えてください。

    id
    パブリッシュしたアニメーションのjs内に記述されている、32文字程度の文字列(library properties: と検索し、その下に書かれているはずです)。
    basepath
    PixjJSの処理が書かれているjsファイルから見て、パブリッシュしたアニメーションのjsファイルまでの相対パス。または絶対パス。
    this.addCreatejs(new lib.flask())
    lib.の後のメソッド名はAdobe Animateのファイル名になります。今回はflask.flaから書き出したため、lib.flaskとなっています。

    ※複数のアニメーションを読み込む場合
    複数のアニメーションを読み込むには、loadAssetAsyncメソッドの引数にオブジェクトの配列を追加します。この操作により、thenメソッドで受け取るlibが配列形式になります。libの中身をthis.addCreatejs()するとアニメーションを動作させることができます。

    const instances = {};
    PIXI.animate.loadAssetAsync([
              {
                id: "CDE27FE7B92A4DF599534F99E5BDCF60",
                basepath: `./assets/adobe/flask/`,
                options: {
                  crossOrigin: false,
                },
              },
              {
                id: "4A9BB83406D140D6B4FA15B64B16A967", 
                basepath: `./assets/adobe/huga/`, 
                options: {
                  crossOrigin: false,
                },
              }
            ]).then(function (lib) {
              class Root extends PIXI.animate.Container {
                constructor() {
                  super(app.ticker);
                  instances.flask = this.addCreatejs(new lib[0].flask());
                  instances.huga = this.addCreatejs(new lib[1].huga());
                }
              }
              app.stage.addChild(new Root());
              // 指定したhtml要素にcanvasを挿入
              document.body.appendChild(app.view);
            });
    

    2.ブラウザでアニメーション自体の大きさや座標を変更する

    この時点で、ブラウザにアニメーションが表示されているはずです。しかし、画面のサイズを変更しても、アニメーションの大きさや位置は自動で調整されません。画面の幅に合わせてアニメーションの大きさや位置を調整したい場合は、resizeイベントが発生するたびに、this.addCreatejs(new lib.flask())._pixiData.instanceオブジェクト内のx、y、width、heightプロパティを更新する必要があります。

    例えばアニメーションの大きさを常に画面幅の横幅の半分にする場合は以下のように記述することになります。

    window.addEventListener('resize', () => {
      this.addCreatejs(new lib.flask())._pixiData.instance.width = window.innerWidth * 0.5;
    })
    

    3.ブラウザでアニメーションを一時停止、再生する

    this.addCreatejs(new lib.flask())を使用してCreateJSのアニメーションを操作することができます。例えば、以下の方法を用いることで、アニメーションを任意のフレームで停止または再生することが可能です。

    this.addCreatejs(new lib.flask()).gotoAndStop(20) //20フレーム目で一時停止
    this.addCreatejs(new lib.flask()).gotoAndPlay(0) //0フレーム目から再生

    注意すべき点として、アニメーションスピードはブラウザの状態により変化することがあるということです。これは、requestAnimationFrame()を使用するのと同様で、アニメーションはブラウザが実行を許可するタイミングでのみ進行します。例えば通常、ブラウザは60fpsで動作しますが、iPhoneが低電力モードに入ると、iOS Safariはフレームレートを下げます。これにより、アニメーションは通常時に比べて遅く再生されてしまいます。

    この問題を解決する方法はいくつかありますが、PixiJSを使用している場合は、app.ticker.addメソッドの引数で受け取るdeltaを利用するのが便利です。アニメーションの総フレーム数は、

    this.addCreatejs(new lib.flask()).totalFrames

    を用いて取得できます。したがって、以下の方法でアニメーションを実行することで、再生速度を維持しながらアニメーションをループさせることが可能です。

    let currentFrame = 0;
    const animatePerTime = (delta) => {
      //現在のフレーム数が全フレーム数に到達したら0フレームから再生し直し
      if (currentFrame > this.addCreatejs(new lib.flask()).totalFrames) currentFrame = 0;
      currentFrame += 1 * delta;
      this.addCreatejs(new lib.flask()).gotoAndStop(currentFrame);
    };
    app.ticker.add(animatePerTime);
    

    プラグインの注意点

    ここまでアニメーションを最低限自由に扱える方法を説明しました。ですが実際に使用してみるとわかりますが、プラグインを使用する上で注意すべき点がいくつかあります。この章では、私がプロジェクトで遭遇した問題点と、可能な場合の解決策を一緒に紹介します。

    • adobe animateにレイヤーマスクが使用されていると、ブラウザに表示したときに上手く表示されないことが多い。
    • 画面幅に合わせてアニメーションを拡大縮小するようにしても、アニメーションが正しい大きさにならない。
    • pivotを設定しても、基準点がずれる
    • アニメーションが高負荷

    ■adobe animateにレイヤーマスクが使用されていると、ブラウザに表示したときに上手く表示されないことが多い。
    原因は不明ですが、レイヤーマスクを使用したコンテンツが表示されないという不具合がありました。
    一部の場合では正常に表示されることもあり、これに基づいて改善策が存在する可能性はあるものの、具体的な解決方法はまだ見つかっていません。
    私の場合、デザイナーにレイヤーマスクを使用しないように依頼し直したところ、問題が解決しました。

    ■画面幅に合わせてアニメーションを拡大縮小するようにしても、アニメーションが正しい大きさにならない。
    ブラウザのサイズに応じてアニメーションを拡大縮小しても、予想以上に大きくなってしまうことがあります。
    この問題の具体的な原因はまだ明らかにされていませんが、効果的な解決策があります。
    その方法は、リサイズ時にアニメーションを一時的に0フレーム目にリセットすることです。この手法を使用すると、アニメーションがリセットされて正しいサイズになります。
    ただし、このアプローチではリサイズのたびにアニメーションが最初から再生されてしまうため、リサイズ前のフレームに戻す必要があります。以前に述べた時間依存のアニメーション手法を適用することで、この問題の解決策をコードに反映させることができます。

    let currentFrame = 0;
    const animatePerTime = (delta) => {
      //現在のフレーム数が全フレーム数に到達したら0フレームから再生し直し
      if (currentFrame > this.addCreatejs(new lib.flask()).totalFrames) currentFrame = 0;
      currentFrame += 1 * delta;
      this.addCreatejs(new lib.flask()).gotoAndStop(currentFrame);
    };
    app.ticker.add(animatePerTime);
    
    //リサイズ時に0フレームに戻す。
    window.addEventListener('resize', () => {
      instance.flask.gotoAndStop(0);
    })
    
    

    ■pivotを設定しても、基準点がずれる
    pivotを設定しても基準点が設定通りの位置になりません。そのため、思い通りにアニメーションを回転させることができない状態になっています。原因と対応策はまだ判明していません。
    私が制作したサイトにも回転する要素がありましたが、そちらはCSSで実装しました。

    ■アニメーションが高負荷
    実際に実装してみると、デバイスへの負荷が大きいことが明らかになりました。
    ただし、極端に性能の低いマシンでない限り、アニメーションが完全に動作しないわけではありません。
    私の場合、ブラウザのタブがアクティブでない時やフォーカスが外れている時にはアニメーションを停止することで、デバイスへの負担を軽減しました。
    このアプローチは、アニメーションが裏で動いていると他の作業に影響を及ぼすためです。

    まとめ

    ここまでpixi-animate-containerの使い方と注意点について解説しました。このプラグインは私が知る中で唯一Adobe AnimateとPixiJSを連結させることができるもので、もし今後似たような案件があった場合、また利用させていただきたいと思います。
    また、私以外にも使用した方がいれば、ぜひとも情報を発信していただけると大変助かります。

    Share

    @googlemaps/react-wrapperでGoogle Map上に自作マーカーを描画するShopifyで商品の在庫が少ない時に管理者へ通知を送る方法