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

第13回 モーションブラーと弾むアニメーション

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

オブジェクトをステージ下端で弾ませる

オブジェクトがステージの下端まで落ちたら,上に弾ませたい。考え方は簡単で,垂直方向の速度(velocityY)を逆転つまり-1を乗じればよい。また,ステージ下端を超えて下がってしまった垂直座標は,その分上に持ち上げる。手を加えるのは,アニメーションの関数(animate())だ。

まず,前掲コード1では,オブジェクトがステージ下端座標(stageHeight)を超えたら,Stageオブジェクトの表示リストから除くことにしている。この条件は外す。替わりに,水平座標が両端の外に出たことを新たな条件に加えよう。オブジェクトがつぎに移る水平座標は変数(newX)にとっておく。

function animate(eventObject) {
  var count = stage.getNumChildren() - 1;

  for (var i = count; i > -1; i--) {
    var child = stage.getChildAt(i);
    var newX = child.x + child.velocityX;   // 追加
    var newY = child.y + child.velocityY;
    var newAlpha = child.alpha + child.velocityAlpha;
    // if (newAlpha <= 0 || newY > stageHeight) {
    if (newAlpha <= 0 || newX > stageWidth || newX < 0) {
      stage.removeChildAt(i);
    } else {

    }
  }

}

つぎに,オブジェクトの垂直座標がステージ下端を超えたら弾ませる。垂直方向の速度(velocityY)を逆転するとともに,少し勢いが衰えるよう-0.8を乗じた。ステージ下端をはみ出た垂直位置の大きさは,ステージの高さ(stageHeight)との剰余をとる。その大きさだけ,垂直位置をステージ下端から持ち上げればよい。その後のオブジェクトを落とす処理は基本的に変わらない。

function animate(eventObject) {

  if (newAlpha <= 0 || newX > stageWidth || newX < 0) {

  } else {
    if (newY > stageHeight) {
      child.velocityY *= -0.8;
      newY = stageHeight - newY % stageHeight;
    }
    // child.x += child.velocityX;
    child.x = newX;
    child.y = newY;
    child.alpha = newAlpha;
    child.velocityX *= 0.98;
    child.velocityY += 2;
  }

}

これで,落ちたオブジェクトは,ステージ下端で弾むアニメーションになる図4⁠。残像のモーションブラーも加わった。今回のお題が目指した表現はひとまずでき上がった。スクリプト全体は以下のコード2のとおりだ。

図4 落ちたオブジェクトはステージ下端で弾む

図4 落ちたオブジェクトはステージ下端で弾む

コード2 ステージ下端まで落ちたオブジェクトを弾ませる

var stage;
var animation;
var stageWidth;
var stageHeight;
var context;
var blurFilter = new createjs.BlurFilter(2, 1, 1);
function initialize() {
  var canvasElement = document.getElementById("myCanvas");
  context = canvasElement.getContext("2d");
  stageWidth = canvasElement.width;
  stageHeight = canvasElement.height;
  stage = new createjs.Stage(canvasElement);
  stage.autoClear = false;
  animation = createAnimation("images/sprite_sheet_s.png");
  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 angle = getRandom(0, Math.PI * 2);
  var instance = animation.clone();
  instance.x = x;
  instance.y = y;
  instance.scaleX = instance.scaleY = getRandom(0.4, 1);
  instance.velocityX = Math.cos(angle) * speed;
  instance.velocityY = Math.sin(angle) * speed;
  instance.velocityAlpha = getRandom(-0.07, -0.01);
  instance.gotoAndPlay("walk");
  stage.addChild(instance);
}
function animate(eventObject) {
  var count = stage.getNumChildren() - 1;
  fadeAndBlur();
  for (var i = count; i > -1; i--) {
    var child = stage.getChildAt(i);
    var newX = child.x + child.velocityX;
    var newY = child.y + child.velocityY;
    var newAlpha = child.alpha + child.velocityAlpha;
    if (newAlpha <= 0 || newX > stageWidth || newX < 0) {
      stage.removeChildAt(i);
    } else {
      if (newY > stageHeight) {
        child.velocityY *= -0.8;
        newY = stageHeight - newY % stageHeight;
      }
      child.x = newX;
      child.y = newY;
      child.alpha = newAlpha;
      child.velocityX *= 0.98;
      child.velocityY += 2;
    }
  }
  stage.update();
}
function createAnimation(file) {
  var data = {};
  data.images = [file];
  data.frames = {width:41, height:55, regX:20, regY:27};
  data.animations = {walk: {
      frames: [0, 0, 1, 2, 2, 3],
      speed: 1 / 3
    }
  };
  var mySpriteSheet = new createjs.SpriteSheet(data);
  var mySprite = new createjs.Sprite(mySpriteSheet);
  return mySprite;
}
function fadeAndBlur() {
  blurFilter.applyFilter(context, 0, 0, stageWidth, stageHeight);
  context.fillStyle = createjs.Graphics.getRGB(0xFFFFFF, 0.2);
  context.fillRect(0, 0, stageWidth, stageHeight);
}
function getRandom(min, max) {
  var randomNumber = Math.random() * (max - min) + min;
  return randomNumber;
}

jsdo.itにもサンプルコードを掲げた。スプライトアニメーションを数多くつくったうえ,残像やぼかしを加えたので,予告したとおり重い動きになった。EaselJS 0.7.0でBlurFilterクラスのパフォーマンスが上がったとはいえ,もう少し動きを軽くできないだろうか。次回はそのあたりについて解説しよう。

著者プロフィール

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

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

URLhttp://www.FumioNonaka.com/

著書