Web制作・ホームページ作成なら、東京・神田神保町のビットビーンズ

BLOG

Beans ブログ

Bit Beansのデザイナーやエンジニアを中心としたスタッフによる情報発信ブログです。
Web制作、デザインや技術、ワークスタイルに関する情報を日々お届けします。

PixiJSを使ってカメラのシャッターアニメーションを作ってみた

こんにちは、フロント担当nakaiです。

さっそくですが、みなさんはJavaScriptでアニメーションを作る時、どんなライブラリを使っていますか?

最近のアニメーション作成といえば、GSAPVelocity.jsThree.jsAnime.jsCreateJSなど、様々なライブラリやツールが世に出ています。普段使っているものもあるのですが、中にはチラッとデモを見たり、ちょっとしたDOM操作のアニメーションを作っただけのものもあります。

今回、ある案件でカメラのシャッターアニメーションをWebで表現したいという提案があり、どうせならcanvasを使った方がアニメーションの幅が広がると思い、ライブラリPixiJS(GSAPも)を使って実装してみました。残念ながら今回作ったものは採用されなかったのですが、PixiJSの知見を増やすことができたと自負しております。

もくじ

  1. PIXI.jsとは
  2. 何を作ったの?
  3. どうやって作ったの?
  4. Pixi.jsのクセ
  5. まとめ

 

PIXI.jsとは

Create beautiful digital content with the fastest, most flexible 2D WebGL renderer.

https://www.pixijs.com/

と公式さんが言っているように、速くて柔軟で無料で使用できる2Dのアニメーションが作れるJavaScriptライブラリです。WebGLの知識がなくてもハードウェアアクセラレーションで作れますよ的な。

チュートリアルやサンプルも充実してます。(ドキュメントは英語なので読みにくさは否めないですけど。)

チュートリアル:https://github.com/kittykatattack/learningPixi
デモ:https://pixijs.io/examples/#/demos-basic/container.js

なんといっても、ある程度のJavaScriptの知識があればWebGLやcanvasを使ったアニメーションが作れる!という手軽さなんだと思います。

 

何を作ったの?

カメラのシャッターとは言いつつ、黒背景+白ボーダーの三角形の矩形を8つ並べて、良きタイミングで動かすだけというシンプルなものなのです。

動きはこちらで確認できます。

実際のデモはこちら
一見、ただのスライドショーですが、写真の切り替え時にカメラのシャッターアニメーションを挟んでいます。

 

どうやって作ったの?

この章からはどうやって作ったのかを紹介していきます。
nodeモジュールを使用して構築するのでマシンにnodeインストールなど下準備が必要。バージョンはnode v12.11.1。
コードはこちらで取得できるので、ライブラリとか確認できますし、下記説明しているコード・ファイルのディレクトリなどの参照にしてください。

Github : Camera Shutter へのリンク

ではまずはpixi.jsとアニメーション部分で使用するgaspをインストールします。

$ npm i pixi.js
$ npm i gasp

画像などの素材情報の用意

今回は6枚画像を用意します。
ディレクトリ:/src/images/slideshow/
それらをjs内で使用するためjsonで指定します。

this.data = [
  {
    "name": "slide01",
    "url": "./assets/images/slideshow/img01.jpg"
  },
  {
    "name": "slide02",
    "url": "./assets/images/slideshow/img02.jpg"
  },
  {
   ...

Pixi.jsアプリケーション初期設定

ベースとなるPIXIのApplicationを作成します。 今回は全画面なのでclientWidthとclientHeightを設定。

this.app = new PIXI.Application({
    width: document.body.clientWidth, height: document.documentElement.clientHeight, backgroundColor: 0x000000,
    autoStart: true,
    autoResize: true,
    antialias:true,
    resolution: devicePixelRatio,
});

スライドショーを入れるコンテナを用意。

this.container = new PIXI.Container();

今回のメイン。シャッターオブジェクトの作成。 シャッタークラスの詳細は後述。

this.shutter = new Shutter();

画像データのロード

上記のdataで用意した画像用jsonを読み込む。Pixi.jsの画像ロードでは、nameとurlというキー名で登録してloadすれば勝手に読み込んでくれるようです。
読み込んだ画像(テクスチャ)をimageSpriteBoxesと言う配列に入れていくのと同時に、ビルドを実行します。

this.app.loader.add(this.data);
...
Object.keys(resources).forEach( (key) => {
    this.imageSpriteBoxes.push(new PIXI.Sprite(resources[key].texture))
});
this.build();

buildメソッドの中には、スライドショーを入れるコンテナ、各スライドのポジションの設定や比率を求めたり、初期設定に必要なものが実行されます。

スライドの開始

その後、firstExpression()およびslideshowStart()でスタートします。スライドショー自体はsetIntervalで回してる簡単なものですが、 毎回新たなスライドが開始するときに、シャッタークラスのshutterStart()を実行する時に関数を引数として渡しています。この関数はシャッターが完全に閉じた後に実行されるもので、次のスライドを準備し、シャッターが開く動作の開始と同時にズームアウトするという関数です。

this.setIntervalID = setInterval( () => {
    this.shutter.shutterStart({
      onComplete : () => {
        this.slideWrapBoxes[this.cNum].alpha = 0;
        if(this.cNum >= this.maxNum) {
          this.cNum = 0;
        } else {
          this.cNum++;
        };
        this.slideWrapBoxes[this.cNum].alpha = 1;
        this.zooming();
      }
    });
}, this.ssInterval);

シャッターの作成

シャッターはShutterクラスとして作成しました。 矩形の作成やポジション、アニメーションなどシャッター自身を管理します。
シャッターの断片の三角形を8つ作ります。 長辺を5000に設定。あまりにも大きいディスプレイだとキレてしまいますが、そこはご愛嬌にしています。

this.shutterlist = this.anglelist.map(obj =>{
    let tri = this.createTriangle(obj);
    this.container.addChild(tri);
    return tri;
});

断片の位置の設定

それぞれの配置をセッティング。ここは改善の余地あり。わざわざgsap.timelineでせずともよいのですが…

setPos() {
    let tl = gsap.timeline();
    tl.set(this.shutterlist[0], { x: `-=${this.xpos}`})
        .set(this.shutterlist[4], { x: `+=${this.xpos}`})
        .set(this.shutterlist[2], { y: `-=${this.xpos}`})
        .set(this.shutterlist[6], { y: `+=${this.xpos}`})
        .set(this.shutterlist[1], { x: `-=${this.ypos}`, y: `-=${this.ypos}`})
        .set(this.shutterlist[3], { x: `+=${this.ypos}`, y: `-=${this.ypos}`})
        .set(this.shutterlist[5], { x: `+=${this.ypos}`, y: `+=${this.ypos}`})
        .set(this.shutterlist[7], { x: `-=${this.ypos}`, y: `+=${this.ypos}`})
}

シャッターの開閉動作

カメラシャッターの閉じる動作と開く動作はgsapのyoyoで再現しています。

shutterStart(obj) {
    if(typeof this.startTL !== 'undefined') {
      this.startTL.kill();
    }
    let xpos = window.innerWidth / 2;
    let ypos = window.innerHeight / 2;
    this.startTL = gsap.timeline({
      repeat: 1,
      repeatDelay: 0.3,
      yoyo: true,
      yoyoEase:Power2.easeOut,
      defaults: {
        duration: this.durationIn,
        ease: Power2.easeInOut
      },
      onRepeat: () => {
        obj.onComplete();
      },
    });
    this.startTL.to(this.shutterlist, { x: xpos, y: ypos })
}

 

Pixi.jsのクセ

Pixi.jsに触れてみた感想としては、

  • Pixi.jsで何か作る時には、Containerを生成し、その中に組み込んでいく。
  • scaleで大きさを変更する時にはpivotやpositionで中央値を考慮する必要がある。
  • デフォルトではcanvas内でスクロールできなかったり、解像度をしてしないといけなかったりする。
  • 独自APIがあるので調べる必要がある。

などと言った少しのクセを知る必要がありますが、なんとかやれなくもないです。

 

まとめ

カメラのシャッターアニメーションを通じて基本的なことだけで実装しましたが、使うほどにまだまだ奥が深いライブラリだなと思わされます。例えば、アニメーションするマスクでエフェクトを作れたり、カスタムフィルタでGLSLを実行できたり、と。
今回はPixi.jsを使いましたが、もっと他にシャッターとの相性のいいライブラリがあるのかもしれません。それを探すため、まだまだengineer lifeは続くのでしょう。

御社の魅力発信、お手伝いします。

まずはお気軽にご相談ください。

この記事を書いた人

エンジニア

Shinji Nakai

ゲーム狂いのハイパーエンジニア。キッコロに似ている。

ゲーム狂いのハイパーエンジニア。キッコロに似ている。

  • SHARE