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

第15回 Matrix2Dクラスで座標を回す

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

座標を回転して描く線のアニメーション

では,描いた星形を2次元平面で回そう。Shapeインスタンスそのものは,DisplayObject.rotationプロパティで回転角が定められる。けれど,今回はひとつひとつの座標を回したい。そのために用いるのが,EaselJS 0.7.0に新たに備わったMatrix2D.transformPoint()メソッドだ。第1および第2引数のxy座標をMatrix2Dオブジェクトで変換して,その結果の座標が第3引数のPointオブジェクトに与えられる。

Matrix2Dオブジェクト.transformPoint(x座標, y座標, Pointオブジェクト)

そのMatrix2Dとは何かというと,伸縮や回転,さらに移動といった座標の変換を数学的な情報としてもつオブジェクトだ※1)⁠数学では行列(Matrix)で表されるため,この名がつけられた。Matrix2Dオブジェクトの座標を変換するメソッドは数多く備わっている。伸縮と回転および移動のメソッドはつぎの表1のとおりだ。

表1 Matrix2Dクラスの伸縮と回転および移動のメソッド

Matrix2Dクラスのメソッドオブジェクトへの座標変換
scale()Matrix2Dオブジェクトを,水平あるいは垂直に引数の比率伸縮する。
rotate()Matrix2Dオブジェクトを,引数の角度(ラジアン)回転する。
translate()Matrix2Dオブジェクトを,水平あるいは垂直に引数の座標移動する。

Matrix2Dオブジェクトの座標変換は,DisplayObjectインスタンスに加えられる(⁠EaselJSのMatrix2Dオブジェクトの変換行列を適用する参照)⁠さらに,新たなMatrix2D.transformPoint()メソッドにより,Pointオブジェクトの座標も変換できるようになった。このメソッドで,前掲コード1の星形を回してみよう。

座標変換に使うMatrix2Dオブジェクトは,予め変数(matrix)に与える。Matrix2D()コンストラクタに引数を渡さないと,変換なしのデフォルトでオブジェクトがつくられる。また,インスタンスを回す角度(5度=π/36ラジアン)も変数(angle)に入れておく。そして,初期化の関数(initialize())Ticker.tickイベントにリスナー(rotate())を加える。

座標回転のリスナー関数(rotate())は,予め定めた角度(angle)Matrix2D.rotate()メソッドでオブジェクトを回転する。そして,forループで配列から取出したPointエレメントすべての座標を,Matrix2D.transformPoint()メソッドで回す。そのうえで,線描の関数(draw())を呼び出せば,直線で描かれる星形が回るアニメーションになる。

var angle = Math.PI / 36;
var matrix = new createjs.Matrix2D();
function initialize() {

  createjs.Ticker.addEventListener("tick", rotate);
}
function rotate(eventObject) {
  var count = points.length;
  matrix.identity().rotate(angle);
  for (var i = 0; i < count; i++) {
    var point = points[i];
    matrix.transformPoint(point.x, point.y, point);
  }
  draw(points);
}

なお,Matrix2D.identity()メソッドは,参照するMatrix2Dオブジェクトを変換なしのデフォルト状態にする。新たなオブジェクトをつくるより使い回す方がお得なのは前回述べたとおりだ。もっとも,回転する角度(angle)が同じなら,そのまま使い続ければ済む。これは,この後回転角をマウスポインタの位置によって変えることに備えて加えた。

これで,Matrix2DクラスのメソッドによりPointオブジェクトの座標は回転し,直線で描かれる星形が回るアニメーションになる。手を加えたスクリプト全体は,つぎのコード2のとおりだ。

図3 直線で描く星形が回る

図3 直線で描く星形が回る 図3 直線で描く星形が回る

コード2 座標を回転して描く星形のアニメーション

var stage;
var drawGraphics;
var points;
var angle = Math.PI / 36;
var matrix = new createjs.Matrix2D();
function initialize() {
  var canvasElement = document.getElementById("myCanvas");
  stage = new createjs.Stage(canvasElement);
  drawGraphics = createGraphics(canvasElement.width / 2, canvasElement.height / 2);
  points = createStarPoints(5, 65, 25);
  draw(points);
  createjs.Ticker.addEventListener("tick", rotate);
}
function rotate(eventObject) {
  var count = points.length;
  matrix.identity().rotate(angle);
  for (var i = 0; i < count; i++) {
    var point = points[i];
    matrix.transformPoint(point.x, point.y, point);
  }
  draw(points);
}
function createGraphics(x, y) {
  var drawShape = new createjs.Shape();
  drawShape.x = x;
  drawShape.y = y;
  stage.addChild(drawShape);
  return drawShape.graphics;
}
function draw(points) {
  var count = points.length;
  var point = points[count - 1];
  drawGraphics.clear()
  .beginStroke("mediumblue")
  .setStrokeStyle(3)
  .moveTo(point.x, point.y);
  for (var i = 0; i < count; i++) {
    point = points[i];
    drawGraphics.lineTo(point.x, point.y);
  }
  stage.update();
}
function createStarPoints(numVertices, longRadius, shortRadius) {
  var starPoints = [];
  var angle = Math.PI;
  var theta = angle / numVertices;
  angle /= -2;
  for (var i = 0; i < numVertices; i++) {
    starPoints.push(new createjs.Point(longRadius * Math.cos(angle), longRadius * Math.sin(angle)));
    angle += theta;
    starPoints.push(new createjs.Point(shortRadius * Math.cos(angle), shortRadius * Math.sin(angle)));
    angle += theta;
  }
  return starPoints;
}
※1
伸縮や回転,さらには移動といった座標の変換は,組み合わせてMatrix2Dオブジェクトにもたせることができる。たとえば,回転して伸縮すれば傾斜になる。DisplayObjectインスタンスに適用する場合,原点はインスタンスが配置された親オブジェクトの基準点になる。

アニメーションの回転をマウスポインタの水平位置に応じて変える

前掲コード2で,回転角の変数(angle)の値は動的に変えられる。今回の仕上げとして,マウスポインタの水平位置に応じて,星形の回る向きと速さを動的に変えてみよう。ステージの水平位置中央を基準として,マウスポインタがその左右どちらにあるかによって回る方向を決め,離れた距離に比例して速さを増す。

初期化の関数(initialize())で,ステージ中央の水平座標値は変数(stageCenterX)にとる。そして,Stage.stagemousemoveイベントにリスナー(setAngle())を加える。リスナー関数は,マウスポインタとステージ中央の水平座標の差に比例した角度を変数に与える。なお,引数に受取ったイベントオブジェクトのMouseEvent.stageXMouseEvent.stageYプロパティでポインタの座標は調べられる。

var stageCenterX;
function initialize() {

  stageCenterX = canvasElement.width / 2;
  // drawGraphics = createGraphics(canvasElement.width / 2, canvasElement.height / 2);
  drawGraphics = createGraphics(stageCenterX, canvasElement.height / 2);

  stage.addEventListener("stagemousemove", setAngle);
}
function setAngle(eventObject) {
  var mouseX = eventObject.stageX;
  angle = (mouseX - stageCenterX) * 1 / 300;
}

これで,マウスポインタの水平位置に応じて,星形の回る向きと速さが動的に変わる。ここまでの手を加えたのが,つぎのコード3だ。jsdo.itにもサンプルを掲げた。次回はさらにz座標を加えて,y軸で回してみたい。

コード3 マウスポインタの水平位置に応じてアニメーションが回る向きと速さを変える

var stage;
var drawGraphics;
var points;
var angle = Math.PI / 36;
var matrix = new createjs.Matrix2D();
var stageCenterX;
function initialize() {
  var canvasElement = document.getElementById("myCanvas");
  stage = new createjs.Stage(canvasElement);
  stageCenterX = canvasElement.width / 2;
  drawGraphics = createGraphics(stageCenterX, canvasElement.height / 2);
  points = createStarPoints(5, 65, 25);
  draw(points);
  createjs.Ticker.addEventListener("tick", rotate);
  stage.addEventListener("stagemousemove", setAngle);
}
function setAngle(eventObject) {
  var mouseX = eventObject.stageX;
  angle = (mouseX - stageCenterX) * 1 / 300;
}
function rotate(eventObject) {
  var count = points.length;
  matrix.identity().rotate(angle);
  for (var i = 0; i < count; i++) {
    var point = points[i];
    matrix.transformPoint(point.x, point.y, point);
  }
  draw(points);
}
function createGraphics(x, y) {
  var drawShape = new createjs.Shape();
  drawShape.x = x;
  drawShape.y = y;
  stage.addChild(drawShape);
  return drawShape.graphics;
}
function draw(points) {
  var count = points.length;
  var point = points[count - 1];
  drawGraphics.clear()
  .beginStroke("mediumblue")
  .setStrokeStyle(3)
  .moveTo(point.x, point.y);
  for (var i = 0; i < count; i++) {
    point = points[i];
    drawGraphics.lineTo(point.x, point.y);
  }
  stage.update();
}
function createStarPoints(numVertices, longRadius, shortRadius) {
  var starPoints = [];
  var angle = Math.PI;
  var theta = angle / numVertices;
  angle /= -2;
  for (var i = 0; i < numVertices; i++) {
    starPoints.push(new createjs.Point(longRadius * Math.cos(angle), longRadius * Math.sin(angle)));
    angle += theta;
    starPoints.push(new createjs.Point(shortRadius * Math.cos(angle), shortRadius * Math.sin(angle)));
    angle += theta;
  }
  return starPoints;
}

著者プロフィール

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

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

URLhttp://www.FumioNonaka.com/

著書