CreateJSでキラキラと輝くTwinkle表現に挑戦

CreateJSを使って、HTML5のcanvasでキラキラと輝くTwinkle表現に挑戦してみました。EaselJSのGraphicsクラスに用意されたdrawPolyStar()メソッドを使って星形のShapeインスタンスを作り、なんちゃってGlowFilterでcanvas上のテキストをキラキラと輝かせてみます。

CreateJSで作ったTwinkle表現のサンプル

下記リンクより、サンプルをご覧頂けます。canvas上をクリックすると一時停止/再生を制御できます。このサンプルは、にゃあプロジェクトさんがFlashで作られた「[AS3.0] Twinkleクラスに挑戦!(4)」を簡略化してCreateJSに移植したものです。とても美しいエフェクトなので、是非にゃあプロジェクトさんのFlash版もご覧下さい。

DisplayObjectクラスのcompositeOperationプロパティ

それでは処理の流れをステップ毎に見ていきましょう。まず、StageインスタンスのcompositeOperationプロパティで"lighter"を指定しておきます。compositeOperationプロパティは、DisplayObjectクラスに定義されていますが、StageクラスはDisplayObjectクラスを継承しているので、Stageインスタンスでも使用することができます。compositeOperationプロパティは、FlashのDisplayObject.blendModeプロパティに似た働きをします。

下記は、サンプルのコンストラクタ内のコードです。StageインスタンスのcompositeOperationに"lighter"を指定することにより、重なり合った色は加算されて表示されるようになります。

JavaScript

this.canvas = document.getElementById(canvasID);
this.stage = new Stage(this.canvas);
this.stage.compositeOperation = "lighter";

EaselJSのTextクラスでcanvasにテキストを表示する

次にEaselJSのTextクラスを使って、canvas上にテキストを表示します。EaselJSのTextクラスについては、前回の投稿「TweenJSのTimelineを使ったテキストアニメーション」で解説していますので、そちらをご覧下さい。

生成したTextインスタンスを表示リストに加えたら、Stageインスタンスのupdate()メソッドでcanvasの描画を更新します。その後でcanvasの描画ピクセルを取得する処理を行います。

JavaScript

function createText(word, font, color) {
  this.text = new Text(word, font, color);
  this.text.x = this.canvas.width >> 1;
  this.text.y = this.canvas.height >> 1;
  this.text.alpha = 0.5;
  this.text.textAlign = "center";
  this.text.textBaseline = "middle";
  this.stage.addChild(this.text);
  this.stage.update();
  createMap.apply(this);
}

canvasの描画ピクセルを取得する

続いて、canvasのコンテキストからgetImageData()メソッドでcanvasのピクセルデータを取得します。ピクセルデータの扱いについては、「HTML5 canvasのピクセル操作」で解説していますので、そちらをご覧下さい。

下記は、サンプル内の描画ピクセルを取得するメソッドです。取得したImageDataオブジェクトのdataプロパティから、canvasのピクセルデータにアクセスします。今回は描画されたピクセルだけを取得したいので、各ピクセルのalpha値を調べ、alpha値が0でない場合のみcanvas左上からの座標(x, y)に変換し、Pointインスタンスとして配列に格納します。この配列内に格納した座標は、後でランダムに取り出し、Shapeインスタンスの座標として使います。

JavaScript

function createMap() {
  var canvas = this.canvas;
  var context = canvas.getContext("2d");
  var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
  var data = imageData.data;
  var map = [];
  var w = canvas.width;
  var h = canvas.height;
  for (var i = 0, l = data.length; i < l; i += 4) {
    if (data[i + 3] === 0) {
      continue;
    }
    var c = i / 4;
    var x = c % w;
    var y = (c / w) >> 0;
    map.push(new Point(x, y));
  }
  this.map = map;
}

EaselJSのShapeクラスで星形のシェイプを生成する

次に今回のTwinkle表現の肝となる星形のShapeインスタンスを生成するメソッドを見てみます。EaselJSのShapeクラスは、Flashと同じようにgraphicsプロパティを通して、図形を描画することができます。

Graphicsクラスには、図形を描画するための様々なメソッドが用意されていますが、サンプルでは多角形を描画するdrawPolyStar()メソッドを使って、星形を描画しています。drawPolyStar()は、第1, 2引数で(x, y)座標、第3引数で半径、第4引数で角の数、第5引数で辺のくぼみ具合、第6引数で角度を指定します。

また、現在のCreateJS(HTML5のcanvas)には、GlowFilterが実装されていないため、円形のグラデーションとシャドウを使って、それっぽい雰囲気を出しています。しかし、モバイル端末では処理の重さによりFPSが極度に落ちるため、シャドウを追加しないようにしています。

下記は、サンプル内の星形のShapeインスタンスを生成するメソッドです。このメソッドは、setInterval()により指定された間隔で定期的に実行されます。生成したShapeインスタンスの座標に先程配列に格納した描画ピクセルの座標を指定することにより、テキスト上でキラキラと輝くエフェクトとなります。

JavaScript

function createTwinkle() {
  var map = this.map;
  var point = map[(Math.random() * map.length) >> 0];
  var radius = (Math.random() * 5 + 13) >> 0;
  var sides = 6;
  var pointSize = (_isMobile) ? 0.85 : 0.9;
  var angle = (Math.random() * 360) >> 0;
  var color = Graphics.getHSL(angle, 100, 75);
  var glowAlpha = (_isMobile) ? 0.25 : 0.15;
  var glowColor1 = Graphics.getHSL(angle, 100, 75, glowAlpha);
  var glowColor2 = Graphics.getHSL(angle, 100, 75, 0);
  var shape = new Shape();
  var g = shape.graphics;
  g.beginRadialGradientFill([glowColor1, glowColor2], [0, 1], 0, 0, 0, 0, 0, 30);
  g.drawCircle(0, 0, 30);
  g.endFill();
  g.beginFill(color);
  g.drawPolyStar(0, 0, radius, sides, pointSize, angle);
  g.endFill();
  shape.x = point.x;
  shape.y = point.y;
  shape.scaleX = shape.scaleY = 0;
  shape.alpha = 0;
  if (!_isMobile) {
    shape.shadow = new Shadow(color, 0, 0, 5);
  }
  this.container.addChild(shape);
  twinkleTween.apply(this, [shape]);
}

不要になったインスタンスの削除

生成したShapeインスタンスは、トゥイーン終了後に不要となるので、removeChild()で表示リストから削除します。また、インスタンスの参照を変数に保持している場合は、変数にnullを代入してメモリを解放してあげましょう。

CreateJSで色々と試した感想として、表示オブジェクトの数が多すぎるとFPSに相当なダメージを与えます。特にモバイル環境においては、その影響が顕著です。不要になったインスタンスは削除して、快適なcanvasライフを送りましょう。

JavaScript

function twinkleTween(shape) {
  var tween = Tween.get(shape)
  .to({scaleX:1, scaleY:1, alpha:1}, 300, Ease.sineOut)
  .to({scaleX:0, scaleY:0, alpha:0}, 600, Ease.sineIn);
  tween.call(removeTwinkle, [tween], this);
}

function removeTwinkle(tween) {
  var shape = tween._target;
  this.container.removeChild(shape);
}