HTML5のCanvasでつくるダイナミックな表現―CreateJSを使う

第11回 マウスポインタの動きに合わせてインスタンスをランダムに落とす

この記事を読むのに必要な時間:およそ 7 分

インスタンスを垂直に落とすアニメーション

ステージにつぎつぎに置いたインスタンスに,ランダムな速さで垂直に落ちるアニメーションを加えよう。アニメーションはTicker.tickイベントで扱うのがお約束だ。そこで,以下のように初期設定の関数(initialize())でイベントリスナー(animate)を加えた。

落ちる速さはインスタンスごとにランダムにしたい。そのため,インスタンスをつくる関数(createInstance())では,垂直方向の初速に加えてフェードアウトするアルファを,ランダムな値でインスタンスのプロパティ(velocityYとvelocityAlpha)に定めている。初速は正負あり,引数(halfSpeed)でランダムな範囲の数値を受け取ることにした。負の初速は,インスタンスを始め上に飛ばしてから,次第に速度を落として落下させる。

Ticker.tickイベントのリスナー関数(animate())は,Stageオブジェクトの表示リストからすべてのインスタンスを取出して,インスタンスに定めた速度(velocityY)とアルファ(velocityAlpha)のプロパティを用いて,落下とフェードアウトのアニメーションにする。メソッドContainer.getNumChildren()は子オブジェクトの数を返し,Container.getChildAt()で引数のインデックスの子オブジェクトが表示リストから取り出せる。速度のプロパティには,重力加速度を表す定数値(2)を加えている。

function initialize() {

  createjs.Ticker.addEventListener("tick", animate);
}
function addInstance(eventObject) {
  // createInstance(stage.mouseX, stage.mouseY);
  createInstance(stage.mouseX, stage.mouseY, 15);

}
// function createInstance(x, y) {
function createInstance(x, y, halfSpeed) {
  var speed = getRandom(-halfSpeed, halfSpeed);
  var instance = createShape(2, 10);

  instance.velocityY = speed;
  instance.velocityAlpha = getRandom(-0.07, -0.01);
}
function animate(eventObject) {
  var count = stage.getNumChildren() - 1;
  for (var i = count; i > -1; i--) {
    var child = stage.getChildAt(i);
    var newY = child.y + child.velocityY;
    var newAlpha = child.alpha + child.velocityAlpha;
    child.y = newY;
    child.alpha = newAlpha;
    child.velocityY += 2;
  }
  stage.update();
}

これで,マウスポインタの動きに合わせてステージに置かれたインスタンスは,ランダムな初速から落下するようになる図3⁠。しかし,コードとしては問題がある。インスタンスをただひたすらつくるだけだからだ。ステージの下端を超えても,アルファが完全に透明になっても,すべてのインスタンスにアニメーションの処理は行われ続ける。

図3 マウスポインタの動きに合わせてステージに置かれたインスタンスがランダムな速さで落ちていく

図3 マウスポインタの動きに合わせてステージに置かれたインスタンスがランダムな速さで落ちていく

インスタンスのアルファが0以下になるか,位置がステージの下端を超えたら,表示リストから消すことにしよう。表示リストのインデックスを渡して子インスタンスを除くのはContainer.removeChildAt()メソッドだ。Ticker.tickイベントのリスナー関数(animate())には,つぎのようなif文の処理を加える。なお,ステージ下端の座標はグローバルな変数(stageHeight)で宣言して,初期設定の関数(initialize())で値を納めた。

var stageHeight;
function initialize() {
  var canvasElement = document.getElementById("myCanvas");
  stageHeight = canvasElement.height;

}

function animate(eventObject) {
  var count = stage.getNumChildren() - 1;
  for (var i = count; i > -1; i--) {
    var child = stage.getChildAt(i);
    var newY = child.y + child.velocityY;
    var newAlpha = child.alpha + child.velocityAlpha;
    if (newAlpha <= 0 || newY > stageHeight) {
      stage.removeChildAt(i);
    } else {
      child.y = newY;
      child.alpha = newAlpha;
      child.velocityY += 2;
    }
  }
  stage.update();
}

ひとつ説明を補っておく。アニメーションの関数(animate())に加えたfor文は,表示リストの子インスタンスをインデックスの大きい順に取り出している。これは,表示リストのインデックス0から順に調べると,子インスタンスを削除したとき,後から取出すインスタンスのインデックスが1ずつ繰り上がって,ずれてしまうからだ。大きい方から調べれば,子インスタンスを除いても,これから取出すインスタンスの番号は狂わない。

これでアルファや垂直位置がステージで見えなくなれば,インスタンスは表示リストから除かれる。アニメーションの見た目(前掲図3に変わりはなくても,処理の無駄は多いに省けた。script要素全体は,つぎのコード1のとおりだ。

コード1 マウスポインタの動きに合わせて表れたランダムなインスタンスが垂直に落ちる

var stage;
var stageHeight;
function initialize() {
  var canvasElement = document.getElementById("myCanvas");
  stageHeight = canvasElement.height;
  stage = new createjs.Stage(canvasElement);
  stage.addEventListener("stagemousemove", addInstance);
  createjs.Ticker.addEventListener("tick", animate);
}
function addInstance(eventObject) {
  createInstance(stage.mouseX, stage.mouseY, 15);
  stage.update();
}
function createInstance(x, y, halfSpeed) {
  var speed = getRandom(-halfSpeed, halfSpeed);
  var instance = createShape(2, 10);
  instance.x = x;
  instance.y = y;
  instance.velocityY = speed;
  instance.velocityAlpha = getRandom(-0.07, -0.01);
  stage.addChild(instance);
}
function animate(eventObject) {
  var count = stage.getNumChildren() - 1;
  for (var i = count; i > -1; i--) {
    var child = stage.getChildAt(i);
    var newY = child.y + child.velocityY;
    var newAlpha = child.alpha + child.velocityAlpha;
    if (newAlpha <= 0 || newY > stageHeight) {
      stage.removeChildAt(i);
    } else {
      child.y = newY;
      child.alpha = newAlpha;
      child.velocityY += 2;
    }
  }
  stage.update();
}
function createShape(min, max) {
  var instance = new createjs.Shape();
  var radius = getRandom(min, max);
  var color = Math.floor(Math.random() * 0xFFFFFF);
  instance.graphics.beginFill(createjs.Graphics.getRGB(color))
  .drawCircle(0, 0, radius);
  return instance;
}
function getRandom(min, max) {
  var randomNumber = Math.random() * (max - min) + min;
  return randomNumber;
}

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書