HTML5
Animate
flash
CreateJS
AnimateCC
FLASHerDay 18

実践!Animate CCを使ったWebサイト演出

More than 1 year has passed since last update.

はじめに

こんにちは。フロントエンドエンジニアの @butchi_y です。

高校生時代にFlashでアニメーションを作り始め、
大学生時代にActionScriptで音声の研究をし、
社会人はFLASHerとして入社しました。

Webでアニメーション演出する際、CSSやJavaScriptのコードを書く場合もしばしばですが、
AnimateでCanvas書き出しができるようになった今、
複雑なアニメーションを行うにあたってこの技術を使わない手はありません。

今回のAdvent Calendarの趣旨からすると、Flash技術を懐かしむ感じかもしれませんが、まだまだ現役で使えるツールとして利用していきましょう!

今回やること

Flashアプリケーションのようなものをサイト上で実現するのではなく、あくまでも部品として
Animate CCで書き出したHTMLを基にWebサイトにアニメーションを組み込みます。
Canvas要素にイベントを送るとアニメーションがスタートし、
アニメーション終了時にCanvas要素からイベントが発火する、
という流れを実装します。

成果物

このようなアニメーションを動かします。

kanpai.gif

デモサイトはこちらになります。

https://butchi.github.io/animate-sample/

リポジトリに全部入ってるので、 コミットの差分 を見ながらだと手順が理解しやすいかと思います。

https://github.com/butchi/animate-sample

前提条件

HTMLはPug、JavaScriptはES2015で組みます。今回はCSSは使いません。
組みやすさのため、こちらの starter kit を利用します。

手順

先ほどのデモに示したアニメーションを実装します。

Flaファイルは こちら

アニメーションの作成

アニメーション自体の制作手順は省きますが、
構造としては、

  • メインのタイムラインは1フレームのみで、シンボル名「mc」と名付けたムービークリップ(名前はなんでもいい)を配置
  • mcの中にアニメーションを実装
  • mcの最終フレームに stop(); のスクリプトを記述

今回メインのタイムラインにベタでアニメーションを配置しなかったのは、イベントを送ってアニメーションを開始する処理を書きやすくするためです。

image

メインのタイムラインでベタにアニメーションを作ってしまった場合は、フレームを全選択して「フレームをコピー」後にmc内のタイムラインに「フレームをペースト」をするとよいでしょう。

パブリッシュ

パブリッシュは何も難しいことがありません。
用途によって都度詳細設定することになるかもしれませんが、
書き出し先さえわかっていれば大丈夫です。

差分

クラスの定義

1アニメにつき1クラスを作ります。

土台としてはこんな感じです。

module/AnimKanpai.js
export default class AnimKanpai {
  constructor (opts = {}) {
  }
};

差分

HTMLからの移植

スクリプトはHTMLにベタ書きされたものと、JSファイルに記述されたものに分かれています。
まずはHTMLにベタ書きされたスクリプトを移植します。

    var canvas, stage, exportRoot;
    function init() {
      canvas = document.getElementById("canvas");
      images = images||{};
      ss = ss||{};
      var loader = new createjs.LoadQueue(false);
      loader.addEventListener("fileload", handleFileLoad);
      loader.addEventListener("complete", handleComplete);
      loader.loadManifest(lib.properties.manifest);
    }
    function handleFileLoad(evt) {  
      if (evt.item.type == "image") { images[evt.item.id] = evt.result; } 
    }
    function handleComplete(evt) {
      //This function is always called, irrespective of the content. You can use the variable "stage" after it is created in token create_stage.
      var queue = evt.target;
      var ssMetadata = lib.ssMetadata;
      for(i=0; i<ssMetadata.length; i++) {
        ss[ssMetadata[i].name] = new createjs.SpriteSheet( {"images": [queue.getResult(ssMetadata[i].name)], "frames": ssMetadata[i].frames} )
      }
      exportRoot = new lib.kanpai();
      stage = new createjs.Stage(canvas);
      stage.addChild(exportRoot); 
      //Registers the "tick" event listener.
      createjs.Ticker.setFPS(lib.properties.fps);
      createjs.Ticker.addEventListener("tick", stage);      
      //Code to support hidpi screens and responsive scaling.
      (function(isResp, respDim, isScale, scaleType) {    
        var lastW, lastH, lastS=1;    
        window.addEventListener('resize', resizeCanvas);    
        resizeCanvas();   
        function resizeCanvas() {     
          var w = lib.properties.width, h = lib.properties.height;      
          var iw = window.innerWidth, ih=window.innerHeight;      
          var pRatio = window.devicePixelRatio, xRatio=iw/w, yRatio=ih/h, sRatio=1;     
          if(isResp) {                
            if((respDim=='width'&&lastW==iw) || (respDim=='height'&&lastH==ih)) {                    
              sRatio = lastS;                
            }       
            else if(!isScale) {         
              if(iw<w || ih<h)            
                sRatio = Math.min(xRatio, yRatio);        
            }       
            else if(scaleType==1) {         
              sRatio = Math.min(xRatio, yRatio);        
            }       
            else if(scaleType==2) {         
              sRatio = Math.max(xRatio, yRatio);        
            }     
          }     
          canvas.width = w*pRatio*sRatio;     
          canvas.height = h*pRatio*sRatio;
          canvas.style.width = w*sRatio+'px';     
          canvas.style.height = h*sRatio+'px';
          stage.scaleX = pRatio*sRatio;     
          stage.scaleY = pRatio*sRatio;     
          lastW = iw; lastH = ih; lastS = sRatio;   
        }
      })(false,'both',false,1); 
    }

差分

HTMLから移植したコードの調整

大まかな変更点は以下です。

  • canvasstage はconstとして定義
  • init, handleFileLoad, handleComplete をメソッド化
  • jQueryを使う前提で、 canvas 要素のjQueryオブジェクトを $elm として定義
  • コンストラクタから init を呼ぶ (個人的な趣向でいったん initialize メソッドを介してます)
  • fileload, complete のイベントハンドラを先ほどメソッド化したものに変更
  • exportRootthis の下に置く

差分

JSファイルからの移植

kanpai.js から、 AnimKanpai.js の末尾へコードを移植します。コードの中身は省略します。

差分

JSファイルから移植したコードの調整

  • グローバルの createjs 変数を window.createjs に変更
  • for文で直接定義されている ilet で宣言

こちらの修正点は少ないです。
ただ、何故かfor文でいきなり i が出てくるので、そこを直さないといけません。

for(let i=0; i<ssMetadata.length; i++) {
  ...
}

差分1
差分2

スプライト画像を移植

スプライトはデフォルトでは kanpai_atlas_.png のようなファイル名になってます。
ファイル名は変えても変えなくてもいいので、好きな場所に配置しましょう。

差分

スプライト画像の指定先修正

先ほどのスプライト画像の移植でファイルのパスが変わった場合、 manifest の画像指定を直します。

   manifest: [
    {src:"img/sprite-kanpai.png", id:"kanpai_atlas_"}

id はそのままでいいです。

メインのスクリプト準備

CreateJS を読み込むと同時に、Canvas要素にクラスを指定し、HTML上に配置します。
複数のアニメーションを配置した際にID被りしないように、 id="canvas" の部分には注意しましょう。

index.pug
  .kanpai.
    <canvas class="anim-kanpai" width="375" height="375" style="display: block; background-color:rgba(255, 255, 255, 1.00)"></canvas>

prepend JavascriptBlock
  script(src="https://code.createjs.com/createjs-2015.11.26.min.js")

これで一旦アニメーションが動くはずです。

script.js
import $ from 'jquery';
import AnimKanpai from './module/AnimKanpai';

let animKanpai = new AnimKanpai();

差分

アニメーション開始イベントの設定

アニメーション側で、Canvas要素に start-anim というイベントが発火した時にアニメーションを開始するようにします。

AnimKanpai.js
let _this = this;
$(canvas).on('start-anim', (evt) => {
  _this.timeline.addTween(cjs.Tween.get(_this.instance).wait(1));
});

メインのスクリプトでイベントを発火します。

script.js
setTimeout(() => {
  animKanpai.$elm.trigger('start-anim');
}, 2000);

差分

アニメーション終了イベントの設定

アニメーション側で、アニメーションが終了した時にCanvas要素に end-anim というイベントが発火します。 stop() を仕込んだところに発火の命令を記述します。

AnimKanpai.js
// timeline functions:
this.frame_19 = function() {
  $(canvas).trigger('end-anim');
  stop();
}

アニメーション終了のイベントハンドラで任意のコードを実行します。

script.js
animKanpai.$elm.on('end-anim', (evt) => {
  console.log('end anim.');
});

差分

呼び出し元スクリプト

呼び出し側のスクリプトがするのはこれだけです。
ここでは setTimeout で2000ミリ秒待った後にイベントを発火していますが、ここも任意です。

script.js
import $ from 'jquery';
import AnimKanpai from './module/AnimKanpai';

let animKanpai = new AnimKanpai();

animKanpai.$elm.on('end-anim', (evt) => {
  console.log('end anim.');
});

setTimeout(() => {
  animKanpai.$elm.trigger('start-anim');
}, 2000);

これでひと通りのアニメーションが完成しました!

最後に

今回は割と単純なアニメーションだったので、もっと複雑なことをしようとすると手を加えるところは増えてくると思います。
特にスクリプトと組み合わせたりすると可能性が広がるんじゃないかと思うので、個人的にも幅広い表現を模索していこうと思います。