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

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

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

今回から取り組む新しいお題は,前回予告したつぎのjsdo.itのサンプルだ。ワイヤーフレームで描かれた星形を,3次元空間のy軸で回している。先に申し上げておくと,CreateJSに3次元座標を扱うクラスはない。2次元座標の変換を,3次元の考え方に応用してつくる。3次元空間の扱いには少し説明が要る。そのため,お題は3回くらいをかけて仕上げるつもりだ。なお,このjsdo.itのJavaScriptコードは,もう少し簡略化することになる。

星形を頂点座標と直線で描く

そういうことで,2次元平面の動かない星形から描いていこう図1)。平面に星形を描いて終わりなら,EaselJSのGraphicsクラスにその名もGraphics.drawPolyStar()というメソッドがある(「EaselJSで描いた星形を回す参照)。しかし,EaselJSのオブジェクトは3次元空間で回せない。そこでどうするかというと,3次元の頂点座標をオブジェクトでつくり,その回転を計算して描こうというもくろみだ。したがって,ここでは頂点をまずは2次元座標で定め,それらを直線で結んで星形にする。

図1 2次元平面に線で描かれた星形

図1 2次元平面に線で描かれた星形

描画の準備から先にしよう。座標の回転にはEaselJS 0.7.0の新しいメソッドを使う。script要素には最新版のライブラリを読み込んでおく。

<script src="http://code.createjs.com/easeljs-0.7.0.min.js"></script>

body要素に加えたcanvas要素にはid属性("myCanvas")を与え,初めに呼出す関数(initialize())をonload属性で定めておくことはいつもどおりだ。描画はShapeインスタンスに行う。といっても,具体的にはShape.graphicsプロパティで参照するGraphicsオブジェクトに描くので,Shapeインスタンスはステージに置いた後触らない。そのため,変数(drawGraphics)には,Graphicsオブジェクトを直に納めることにする。

Shapeインスタンスは別の関数(createGraphics())でつくる。引数に渡したxy座標で位置を定めたら,Stageオブジェクトの表示リストに加える。そして,前述の変数(drawGraphics)に与えるGraphicsオブジェクトを,Shapeインスタンスから取り出して返した。これで,ステージに新たなShapeインスタンスが置かれ,そのGraphicsオブジェクトの参照が変数に得られる。

var stage;
var drawGraphics;

function initialize() {
  var canvasElement = document.getElementById("myCanvas");
  stage = new createjs.Stage(canvasElement);
  drawGraphics = createGraphics(canvasElement.width / 2, canvasElement.height / 2);

}
function createGraphics(x, y) {
  var drawShape = new createjs.Shape();
  drawShape.x = x;
  drawShape.y = y;
  stage.addChild(drawShape);
  return drawShape.graphics;
}

つぎに,星形の頂点座標をPointオブジェクトでつくり,それらを配列に納める。そのための関数(createStarPoints())をつぎのように定め,3つの引数で星のかたちを決められるようにしよう。3つの引数は,頂点数,および山と谷の半径だ図2)。頂点は尖った山の数,半径は中心から山の頂点と谷の底までのそれぞれの長さで決める。

function createStarPoints(頂点数, 山の半径, 谷の半径)

図2 頂点数および山と谷の半径で描く星形を定める

図2 頂点数および山と谷の半径で描く星形を定める

原点(0, 0)は星の中心にとって,山と谷の点の座標をすべて求めたい。原点から求める点までの長さと,2点を結ぶ直線が正のx軸となす角度からxy座標はつぎのように導ける(第11回「マウスポインタの動きに合わせてインスタンスをランダムに落とす」インスタンスの動きに水平方向の初速を加える参照)。

x = 距離×cos角度
y = 距離×sin角度

第11回 図4 原点から距離が1で角度θのxy座標は(cosθ, sinθ)(再掲)

第11回 図4 原点から距離が1で角度θのxy座標は(cosθ, sinθ)

では,新たな関数(createStarPoints())の3つの引数から,星形の山と谷の座標を求めてみよう。この関数は初期化の関数(initialize())で呼出し,戻り値として座標のPointエレメントが納められた配列を受け取って,変数(points)に入れることとする。

星形の山と谷の各点と原点を結んだ中心角はすべて等しい。その角度(theta)は,引数の山の頂点数(numVertices)の2倍で1周の角度2π(360度)を割れば導ける。座標は星形のてっぺん,つまり12時方向の山の点からとる。その角度(angle)は-π/2(-90度)だ。そのうえで,角度(angle)forループで増やしつつ,山と谷の点の座標を交互にPointオブジェクトにして配列(starPoints)に納める。すべての座標が求められたら,その配列を返せばよい。

var points;
function initialize() {

  points = createStarPoints(5, 65, 25);

}

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;
}

後は,配列(points)から座標を取出して,すべて直線で結ぶだけだ。その関数(draw())も新たに定めて,初期化の関数から呼出す。引数はPointエレメントが納められた配列とする。すでにGraphicsオブジェクトは変数(drawGraphics)にとってあるので,そこに直線で星形を描く。

線描の関数(draw())はGraphicsクラスのメソッドにより,Graphics.beginStroke()で線の色("mediumblue"),Graphics.setStrokeStyle()はその太さ(3)を定める。そして,Graphics.moveTo()が描き始めの座標を決めたら,forループで引数の配列から取り出したPointオブジェクトの座標を順にGraphics.lineTo()メソッドによって結べば星形が描かれる。

function initialize() {

  draw(points);
}

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();
}

さて,上記コードで直線を描くforループの組立てにひとつ工夫がある。配列の始めのPointエレメントから描き始めるなら,その座標をGraphics.moveTo()メソッドで起点に定める。そうなると,最後のエレメントの座標までGraphics.lineTo()メソッドで直線を結んだ後,改めて起点まで戻って直線を描かないと一周しない。

しかし,上記のJavaScriptコードでは,描き始めの座標を配列の最後のPointエレメント(points[count - 1])で決めた。すると,forループでは初めのPointエレメントから順に直線が描かれ,起点とした最後の座標まで結んだところで星はでき上がる。

もうひとつ補っておきたいのは,星形を描く前にGraphics.clear()メソッドを呼び出していることだ。まっさらなGraphicsオブジェクトには消す描画などない。だがこの後,星形をアニメーションさせるつもりだ。そのため,このメソッドの呼出しをすでに加えておいた。

ここまでをまとめたのがつぎのコード1だ。ステージの真ん中に5頂点の星形が直線で描かれる(前掲図1参照)。単純な線描とはいえ,座標から計算してつくった。したがって,考え方は3次元の描画にも応用できる。

コード1 星形の頂点座標を直線で結んで描く

var stage;
var drawGraphics;
var points;
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);
}
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/

著書

コメント

コメントの記入