サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

EaselJSでインスタンスをドラッグする勢いで回転を加速する

ID: FN1206001 Technique: HTML5 and JavaScript

EaselJSでインスタンスをクリックした座標で回しながらドラッグする」では、初めにマウスボタンが押された座標でインスタンスを回しながらドラッグしてみました。ただし、ドラッグのアニメーションはMouseEvent.onMouseMoveイベントハンドラで行ったため、マウスを動かしたときだけインスタンスが回りました。本稿では、マウスをドラッグする勢いで回転が速まり、止めれば減速して、ドロップするとゆっくり止まるアニメーションをつくることにします(サンプルHTMLドキュメント)。


01 ドラッグをしている間同じ速さで回り続けるアニメーション
ビットマップはPNGファイル(card.png)で用意しました。HTMLドキュメントと同じ場所に画像用のフォルダ(images)をつくり、その中に納めます(図001)。そして、HTMLドキュメントの<body>要素には<canvas>要素(id属性"myCanvas")が記述され、描画のためのJavaScriptの関数(xInitialize())を呼出しているものとします。

<body onload="xInitialize()">
  <canvas id="myCanvas" width="240" height="180"></canvas>
</body>

図001■PNGファイルを画像用フォルダに納める
図001

まずは、前出「EaselJSでインスタンスをクリックした座標で回しながらドラッグする」のコード003をもとに、インスタンスが初めにクリックした座標を中心にして、ドラッグしている間同じ速さで回り続けるコードにしましょう(図002)。マウスボタンを押し続けていれば、ポインタは動かさなくてもインスタンスが回り、ボタンを放したら止まります。

図002■クリックした座標を中心にインスタンスがドラッグしている間同じ速さで回り続ける
図002左   図002右

前出「EaselJSでインスタンスをクリックした座標で回しながらドラッグする」のコード003と組立てが変わるのは、アニメーションはtick()メソッドによりフレームレートで進めることです。けれど、tick()メソッドではマウスポインタの座標がとれません。そこで、MouseEvent.onMouseMoveイベントのハンドラは、マウスポインタが動くたびにその座標を調べて変数に納めます。

後に掲げる<script要素>全体のコード001からマウス操作にともなうアニメーションの処理を抜出したのがつぎのステートメントです。初めにマウスボタンを押したインスタンス上の座標が回転の中心になるように、DisplayObject.globalToLocal()DisplayObject.localToGlobal()メソッドで補正する考え方については、前出「EaselJSでインスタンスをクリックした座標で回しながらドラッグする」をお読みください。

  1. var origin;
  2. var lastMouse;
  3. var angularVelocity = 10;
  4. var instance;
  1. function xDraw(nX, nY) {
  2.   var myBitmap = new Bitmap("images/card.png");
  1.   myBitmap.onPress = clickHandler;
  2.   stage.addChild(myBitmap);
  3.   stage.update();
  4. }
  5. function clickHandler(eventObject) {
  6.   var mouseX = eventObject.stageX;
  7.   var mouseY = eventObject.stageY;
  8.   lastMouse = new Point(mouseX, mouseY);
  9.   origin = this.globalToLocal(mouseX, mouseY);
  10.   eventObject.onMouseMove = mouseMoveHandler;
  11.   eventObject.onMouseUp = mouseUpHandler;
  12.   instance = this;
  13.   Ticker.addListener(window);
  14. }
  15. function mouseMoveHandler(eventObject) {
  16.   lastMouse = new Point(eventObject.stageX, eventObject.stageY);
  17. }
  18. function mouseUpHandler(eventObject) {
  19.   this.onMouseMove = this.onMouseUp = null;
  20.   Ticker.removeListener(window);
  21. }
  22. function tick() {
  23.   rotate();
  24.   stage.update();
  25. }
  26. function rotate() {
  27.   instance.rotation += angularVelocity;
  28.   var offsetPoint = instance.localToGlobal(origin.x, origin.y);
  29.   instance.x += lastMouse.x - offsetPoint.x;
  30.   instance.y += lastMouse.y - offsetPoint.y;
  31. }

インスタンス上でマウスボタンを押すDisplayObject.onPressイベントに設定したハンドラの関数(clickHandler())は、ドラッグに備えて支度を整えます(コード001第31〜40行目)。MouseEvent.stageXMouseEvent.stageYプロパティで調べたマウスポインタの座標と、それをDisplayObject.globalToLocal()メソッドでインスタンスから見た値に変換して、それぞれPointオブジェクトで変数(lastMouseとorigin)にとります(第32〜35行目)。後者は回転の中心座標とします。

マウス操作については、MouseEvent.onMouseMoveMouseEvent.onMouseUpイベントにハンドラの関数(mouseMoveHandler()とmouseUpHandler())を定めます(コード001第36〜37行目)。けれど、アニメーションそのものはtick()メソッドで扱いますので、Ticker.addListener()メソッドにwindowオブジェクトをリスナーとして渡します(第39行目)。なお、Tickerクラスを使ったアニメーションについては、「EaselJSで描いた星形を回す」をお読みください。

MouseEvent.onMouseMoveイベントのハンドラ(mouseMoveHandler())は、動いたマウス座標をひたすら変数(lastMouse)に上書きするだけです(コード001第42行目)。また、MouseEvent.onMouseUpイベントのハンドラ(mouseUpHandler())は、ふたつのイベントハンドラを消して、Ticker.removeListener()メソッドの呼出しによりtick()メソッドのアニメーションを止めます(第45〜46行目)。

tick()メソッドから呼出す関数rotate()が、初めにクリックした座標でインスタンスを回転します(コード001第52〜57行目)。今のところ、回す速さは一定(angularVelocity)です。回転の中心座標を定める方法は、前出「EaselJSでインスタンスをクリックした座標で回しながらドラッグする」をお読みください。<script>要素全体はつぎに掲げるとおりです。

コード001■マウスボタンが押された座標でインスタンスを同じ速さで回す
  1. <script src="easeljs/utils/UID.js"></script>
  2. <script src="easeljs/events/MouseEvent.js"></script>
  3. <script src="easeljs/geom/Point.js"></script>
  4. <script src="easeljs/geom/Matrix2D.js"></script>
  5. <script src="easeljs/display/DisplayObject.js"></script>
  6. <script src="easeljs/display/Container.js"></script>
  7. <script src="easeljs/display/Stage.js"></script>
  8. <script src="easeljs/display/Bitmap.js"></script>
  9. <script src="easeljs/utils/Ticker.js"></script>
  10. <script type="text/javascript">
  11. var stage;
  12. var origin;
  13. var lastMouse;
  14. var angularVelocity = 10;
  15. var instance;
  16. function xInitialize() {
  17.   var canvasObject = document.getElementById("myCanvas");
  18.   stage = new Stage(canvasObject);
  19.   xDraw(canvasObject.width / 2, canvasObject.height / 2);
  20. }
  21. function xDraw(nX, nY) {
  22.   var myBitmap = new Bitmap("images/card.png");
  23.   myBitmap.x = nX;
  24.   myBitmap.y = nY;
  25.   myBitmap.regX = myBitmap.image.width / 2;
  26.   myBitmap.regY = myBitmap.image.height / 2;
  27.   myBitmap.onPress = clickHandler;
  28.   stage.addChild(myBitmap);
  29.   stage.update();
  30. }
  31. function clickHandler(eventObject) {
  32.   var mouseX = eventObject.stageX;
  33.   var mouseY = eventObject.stageY;
  34.   lastMouse = new Point(mouseX, mouseY);
  35.   origin = this.globalToLocal(mouseX, mouseY);
  36.   eventObject.onMouseMove = mouseMoveHandler;
  37.   eventObject.onMouseUp = mouseUpHandler;
  38.   instance = this;
  39.   Ticker.addListener(window);
  40. }
  41. function mouseMoveHandler(eventObject) {
  42.   lastMouse = new Point(eventObject.stageX, eventObject.stageY);
  43. }
  44. function mouseUpHandler(eventObject) {
  45.   this.onMouseMove = this.onMouseUp = null;
  46.   Ticker.removeListener(window);
  47. }
  48. function tick() {
  49.   rotate();
  50.   stage.update();
  51. }
  52. function rotate() {
  53.   instance.rotation += angularVelocity;
  54.   var offsetPoint = instance.localToGlobal(origin.x, origin.y);
  55.   instance.x += lastMouse.x - offsetPoint.x;
  56.   instance.y += lastMouse.y - offsetPoint.y;
  57. }

02 マウスの動きに応じて回す速さを増す
振り回すようにドラッグしたとき、回転の勢いが増すようにしてみます。実際に滑らかなテーブルの上でカードのようなものを滑らせてみたとき、回りやすい動かし方があります。

まず、カードの中心(重心)から遠い位置に力を加えることです。真ん中をいくら激しく動かしても、なかなかカードは回りません。これは、てこの原理が働くからです。てこは支点から離れた位置(力点)をもった方が力はかかりやすくなります(図003)。つぎに、てこには垂直に力を加えるのがもっとも効率的です。自転車のペダルは水平になったとき踏み込む(力はペダルに垂直)と、加速しやすいのと同じです。

図003■てこに力を加える
図003

このような回す力の加わりやすさは、物理学では「力のモーメント」という考え方で表されます。スクリプトに戻りますと、インスタンスの中心とドラッグしている座標を結ぶ部分がてこになります。そして、ドラッグするマウスの動きがてこに加える力です(図004)。これらをベクトルとして捉えると、回す力はふたつの「ベクトルの外積」の大きさに比例します(詳しくはリンクの「力のモーメント」参照)。外積の計算式については後述します。

図004■重心からのドラッグ位置とマウスの動きをベクトルとして捉える
図004

ベクトルはPointオブジェクトで扱えます。てこのベクトル(radius)は、直前のマウスポインタの座標(lastMouse)からインスタンスの真ん中に据えた基準点の座標を差引いて求めます[*1]。そして、マウスポインタの座標の動きがもうひとつのベクトル(force)です。

ベクトルの外積は、3次元空間で使われることが多く、ふたつのベクトルのどちらにも垂直な新たなベクトルとして求められます。しかし、2次元平面に垂直なのは必ずz軸で、そのxy座標はともに0です。したがって、2次元平面上のふたつのベクトルの外積は、z座標値だけで捉えられます[*2]

てこのベクトル(radius)のxy座標を(rx, ry)、マウスポインタの動きのベクトル(force)が(fx, fy)だとすると、ふたつのベクトルの外積はつぎの式で導かれます。

2次元平面上のベクトルの外積 = rxfy - ryfx

それではマウスのドラッグする動きを、インスタンスが回る勢いとして加えてみましょう。前掲コード001につぎのような手を加えます。各行頭の番号は、後にまとめて掲げるコード002にもとづきます。

  1. var RATIO = 0.01;
  2. var angularVelocity = 0;
  1. function mouseMoveHandler(eventObject) {
  2.   var mouseX = eventObject.stageX;
  3.   var mouseY = eventObject.stageY;
  4.   var radius = new Point(lastMouse.x - instance.x, lastMouse.y - instance.y);
  5.   var force = new Point(mouseX - lastMouse.x, mouseY - lastMouse.y);
  6.   var moment = crossProduct2D(radius, force);
  7.   angularVelocity += moment * RATIO;
  8.   lastMouse = new Point(mouseX, mouseY);
  9. }
  1. function crossProduct2D(point0, point1) {
  2.   return point0.x * point1.y - point0.y * point1.x;
  3. }

MouseEvent.onMouseMoveイベントハンドラ(mouseMoveHandler())の仕事が大幅に増えました。もちろん、マウスポインタの座標を変数に納めるのは変わっていません(コード002第34〜35行目および第40行目)。

外積を求めるためのてこ(radius)とマウスの動き(force)のベクトルをPointインスタンスとしてつくり、外積計算の関数(crossProduct2D())に渡します(コード002第36〜38行目)。この関数が返す値の計算式(第57行目)は上述のとおりです。その値は角度として直に加えるには大きすぎるので、調整のための変数(RATIO)を設けて乗じました(第5および第39行目)。なお、回転角(angularVelocity)の初期値は0にしています(第6行目)。

インスタンスを回す関数(rotate())にも、以下のように1行加えました。回転角(angularVelocity)に減速率の変数(DECELERATION)を乗じて、回る速さを少しずつ下げています(コード002第54行目)。それにともない、MouseEvent.onMouseUpイベントのハンドラ(mouseUpHandler())から、アニメーションを直ちに止めるTicker.removeListener()メソッドの呼出しは除きました(第42〜44行目)。したがって、マウスボタンを放すとインスタンスの回転はすぐには止まらず、少しずつ速さを落としつつやがて止まります。

  1. var DECELERATION = 0.8;
  1. function mouseUpHandler(eventObject) {
  2.   this.onMouseMove = this.onMouseUp = null;
      // Ticker.removeListener(window);
  3. }
  1. function rotate() {
  1.   angularVelocity *= DECELERATION;
  2. }

前掲コード001に以上の修正を加えたのが、以下のコード002です。インスタンスをドラッグで振り回すと回転に勢いがつき、マウスボタンを放すと回る速さが遅くなって止まります。

コード002■インスタンスをドラッグする勢いで回る速さが増す
  1. var stage;
  2. var origin;
  3. var lastMouse;
  4. var DECELERATION = 0.8;
  5. var RATIO = 0.01;
  6. var angularVelocity = 0;
  7. var instance;
  8. function xInitialize() {
  9.   var canvasObject = document.getElementById("myCanvas");
  10.   stage = new Stage(canvasObject);
  11.   xDraw(canvasObject.width / 2, canvasObject.height / 2);
  12. }
  13. function xDraw(nX, nY) {
  14.   var myBitmap = new Bitmap("images/card.png");
  15.   myBitmap.x = nX;
  16.   myBitmap.y = nY;
  17.   myBitmap.regX = myBitmap.image.width / 2;
  18.   myBitmap.regY = myBitmap.image.height / 2;
  19.   myBitmap.onPress = clickHandler;
  20.   stage.addChild(myBitmap);
  21.   stage.update();
  22. }
  23. function clickHandler(eventObject) {
  24.   var mouseX = eventObject.stageX;
  25.   var mouseY = eventObject.stageY;
  26.   lastMouse = new Point(mouseX, mouseY);
  27.   origin = this.globalToLocal(mouseX, mouseY);
  28.   eventObject.onMouseMove = mouseMoveHandler;
  29.   eventObject.onMouseUp = mouseUpHandler;
  30.   instance = this;
  31.   Ticker.addListener(window);
  32. }
  33. function mouseMoveHandler(eventObject) {
  34.   var mouseX = eventObject.stageX;
  35.   var mouseY = eventObject.stageY;
  36.   var radius = new Point(lastMouse.x - instance.x, lastMouse.y - instance.y);
  37.   var force = new Point(mouseX - lastMouse.x, mouseY - lastMouse.y);
  38.   var moment = crossProduct2D(radius, force);
  39.   angularVelocity += moment * RATIO;
  40.   lastMouse = new Point(mouseX, mouseY);
  41. }
  42. function mouseUpHandler(eventObject) {
  43.   this.onMouseMove = this.onMouseUp = null;
  44. }
  45. function tick() {
  46.   rotate();
  47.   stage.update();
  48. }
  49. function rotate() {
  50.   instance.rotation += angularVelocity;
  51.   var offsetPoint = instance.localToGlobal(origin.x, origin.y);
  52.   instance.x += lastMouse.x - offsetPoint.x;
  53.   instance.y += lastMouse.y - offsetPoint.y;
  54.   angularVelocity *= DECELERATION;
  55. }
  56. function crossProduct2D(point0, point1) {
  57.   return point0.x * point1.y - point0.y * point1.x;
  58. }

[*1] 直前のマウスポインタの座標(lastMouse)は前掲コード001ではインスタンスを回す中心点になり、その位置がマウスのドラッグに合わせて動きます。

[*2] 3次元空間と2次元平面の外積については前出「力のモーメント」をお読みください。


03 インスタンスをドロップしたとき慣性で移動させる
もっとも、前掲コード002ではマウスボタンを放したとき、インスタンスは慣性でしばらく回り続けるものの、回転の中心座標が動かなくなります。そこで、インスタンスの移動についても、慣性を加えたいと思います。そのため、マウスボタンを放す直前のマウスの動き(force)を新たな変数(velocity)にとり、もともとマウス座標を納めていた変数(lastMouse)の値に減速しながら加えて行くことにします。手を加えるスクリプトはつぎのとおりで、各行頭の番号は後に掲げるコード003にもとづきます。

  1. var velocity = new Point(0, 0);
  1. function mouseMoveHandler(eventObject) {
  1.   velocity = force;
  2. }
  1. function updateInertia(mouseX, mouseY) {
  2.   var velocityX = velocity.x;
  3.   var velocityY = velocity.y;
  4.   lastMouse = new Point(mouseX + velocityX, mouseY + velocityY);
  5.   velocity = new Point(velocityX * DECELERATION, velocityY * DECELERATION);
  6.   angularVelocity *= DECELERATION;
  7. }

MouseEvent.onMouseMoveイベントハンドラ(mouseMoveHandler())で、マウスの動き(force)を予め宣言した変数(velocity)に納めるようにしました(コード003第8行目および第44行目)。ドラッグ中はこの変数の値は使いません。

マウスボタンを放した後、回転(rotate())に加えたい移動のアニメーションを新たな関数(updateInertia())で定めました(コード003第66〜72行目)。直前のマウスの動きを納めた変数(velocity)から取出したxy座標値を、マウス座標が納められた変数(lastMouse)に加えます(第67〜69行目)。これでtick()メソッドのアニメーションに、インスタンスの移動が加わります。そのうえで、マウスの動き(velocity)と回転(angularVelocity)の変数値を減じました(第70〜71行目)。

さて、移動のアニメーションを定めた新たな関数(updateInertia())は、回転の関数(rotate())からどのように呼出したらよいでしょう。インスタンスのドラッグ中には、この呼出しはしません。ひとつには、ドラッグ中かどうかを変数(フラグ)に置いて振分けることが考えられます。しかし、コード003は以下のように関数を変数(update)に納めることにしました。

回転の関数(rotate())の最後にあった回転角(angularVelocity)を減じるステートメント(前掲コード002第54行目)は、別の関数(updateDrag())に分けました(コード003第63〜65行目)。変数(update)には、インスタンス上でマウスボタンを押したときこの関数を納めます(第33行目)。そして、回転の関数からこの変数の関数を呼出します(第61行目)。

そのうえで、マウスボタンを放したとき、移動するアニメーションの関数(updateInertia())で変数を上書きします(第47行目)。これで、ドラッグ中とドロップ後のアニメーションが切り替わることになります。

  1. var update;
  1. function clickHandler(eventObject) {
  1.   update = updateDrag;
  1. }
  1. function mouseUpHandler(eventObject) {
  2.   update = updateInertia;
  1. }
  1. function rotate() {
  2.   var mouseX = lastMouse.x;
  3.   var mouseY = lastMouse.y;
  1.   update(mouseX, mouseY);
  2. }
  3. function updateDrag(mouseX, mouseY) {
  4.   angularVelocity *= DECELERATION;
  5. }

コード003のスクリプト全体はつぎのとおりです。これでドラッグする勢いでインスタンスの回転が速まり、マウスボタンを放せば回転と移動が減速しつつやがて止まります(図005)。

コード003■インスタンスをドラッグする勢いで回転が速まりドロップすると回転と移動が減速して止まる
  1. var stage;
  2. var origin;
  3. var lastMouse;
  4. var DECELERATION = 0.8;
  5. var RATIO = 0.01;
  6. var angularVelocity = 0;
  7. var instance;
  8. var velocity = new Point(0, 0);
  9. var update;
  10. function xInitialize() {
  11.   var canvasObject = document.getElementById('myCanvas');
  12.   stage = new Stage(canvasObject);
  13.   xDraw(canvasObject.width / 2, canvasObject.height / 2);
  14. }
  15. function xDraw(nX, nY) {
  16.   var myBitmap = new Bitmap("images/card.png");
  17.   myBitmap.x = nX;
  18.   myBitmap.y = nY;
  19.   myBitmap.regX = myBitmap.image.width / 2;
  20.   myBitmap.regY = myBitmap.image.height / 2;
  21.   myBitmap.onPress = clickHandler;
  22.   stage.addChild(myBitmap);
  23.   stage.update();
  24. }
  25. function clickHandler(eventObject) {
  26.   var mouseX = eventObject.stageX;
  27.   var mouseY = eventObject.stageY;
  28.   lastMouse = new Point(mouseX, mouseY);
  29.   origin = this.globalToLocal(mouseX, mouseY);
  30.   eventObject.onMouseMove = mouseMoveHandler;
  31.   eventObject.onMouseUp = mouseUpHandler;
  32.   instance = this;
  33.   update = updateDrag;
  34.   Ticker.addListener(window);
  35. }
  36. function mouseMoveHandler(eventObject) {
  37.   var mouseX = eventObject.stageX;
  38.   var mouseY = eventObject.stageY;
  39.   var radius = new Point(lastMouse.x - instance.x, lastMouse.y - instance.y);
  40.   var force = new Point(mouseX - lastMouse.x, mouseY - lastMouse.y);
  41.   var moment = crossProduct2D(radius, force);
  42.   angularVelocity += moment * RATIO;
  43.   lastMouse = new Point(mouseX, mouseY);
  44.   velocity = force;
  45. }
  46. function mouseUpHandler(eventObject) {
  47.   update = updateInertia;
  48.   this.onMouseMove = this.onMouseUp = null;
  49. }
  50. function tick() {
  51.   rotate();
  52.   stage.update();
  53. }
  54. function rotate() {
  55.   var mouseX = lastMouse.x;
  56.   var mouseY = lastMouse.y;
  57.   instance.rotation += angularVelocity;
  58.   var offsetPoint = instance.localToGlobal(origin.x, origin.y);
  59.   instance.x += mouseX - offsetPoint.x;
  60.   instance.y += mouseY - offsetPoint.y;
  61.   update(mouseX, mouseY);
  62. }
  63. function updateDrag(mouseX, mouseY) {
  64.   angularVelocity *= DECELERATION;
  65. }
  66. function updateInertia(mouseX, mouseY) {
  67.   var velocityX = velocity.x;
  68.   var velocityY = velocity.y;
  69.   lastMouse = new Point(mouseX + velocityX, mouseY + velocityY);
  70.   velocity = new Point(velocityX * DECELERATION, velocityY * DECELERATION);
  71.   angularVelocity *= DECELERATION;
  72. }
  73. function crossProduct2D(point0, point1) {
  74.   return point0.x * point1.y - point0.y * point1.x;
  75. }

図005■インスタンスがドラッグする勢いで回ってドロップすると回転と移動は減速する
図005左   図005右


04 外部画像の読み込み待ちとアニメーションの停止
「インスタンスをドラッグする勢いで回転を加速する」というお題をどう扱ったらよいかという範囲では、コード003でよいでしょう。ただ、実際にコンテンツとして公開するとなると、ふたつ気になることがあります。

第1に、外部画像を読込むのに、そのロード待ちをしていないことです。データがキャッシュされる前の、初めてコンテンツを開くとき、画像は表示されないかもしれません。これはPreloadJSで先読みをしておけばよいでしょう。解説は「PreloadJSで外部画像ファイルの読込みを待つ」をお読みください。

第2に、インスタンスをひとたびドラッグしてアニメーションが始まると、その後Ticker.removeListener()は呼出されません。つまり、移動と回転の大きさが実質0になって、見た目アニメーションは行われていなくても、tick()メソッドは呼出され、アニメーションの処理と描画の更新をし続けることになります。

そこで、慣性の移動ベクトルとしたPointオブジェクト(velocity)のxy座標と回転角(angularVelocity)それぞれの値の絶対値をMath.abs()メソッドで求め、合計が1未満になったときTicker.removeListener()メソッドを呼出しました(コード004第90〜91行目)。これで、アニメーションの動きがほとんどなくなると、スクリプトの処理(tick()メソッドの呼出し)もすべて終わります。

コード004■インスタンスをドラッグする勢いで回転が速まりドロップすると回転と移動が減速して止まる
  1. <script src="easeljs/utils/UID.js"></script>
  2. <script src="easeljs/events/MouseEvent.js"></script>
  3. <script src="easeljs/geom/Point.js"></script>
  4. <script src="easeljs/geom/Matrix2D.js"></script>
  5. <script src="easeljs/display/DisplayObject.js"></script>
  6. <script src="easeljs/display/Container.js"></script>
  7. <script src="easeljs/display/Stage.js"></script>
  8. <script src="easeljs/display/Bitmap.js"></script>
  9. <script src="easeljs/utils/Ticker.js"></script>
  10. <script src="preloadjs/AbstractLoader.js"></script>
  11. <script src="preloadjs/PreloadJS.js"></script>
  12. <script src="preloadjs/TagLoader.js"></script>
  13. <script src="preloadjs/XHRLoader.js"></script>
  14. <script type="text/javascript">
  15. var stage;
  16. var origin;
  17. var lastMouse;
  18. var DECELERATION = 0.8;
  19. var RATIO = 0.01;
  20. var angularVelocity = 0;
  21. var instance;
  22. var velocity = new Point(0, 0);
  23. var update;
  24. function xInitialize() {
  25.   var canvasObject = document.getElementById("myCanvas");
  26.   var position = new Point(canvasObject.width / 2, canvasObject.height / 2);
  27.   var file = {src:"images/card.png", id:"image", data:position}
  28.   var loader = new PreloadJS(false);
  29.   stage = new Stage(canvasObject);
  30.   loader.onFileLoad = xDraw;
  31.   loader.loadFile(file);
  32. }
  33. function xDraw(eventObject) {
  34.   var myImage = eventObject.result;
  35.   var position = eventObject.data
  36.   var myBitmap = new Bitmap(myImage);
  37.   myBitmap.x = position.x;
  38.   myBitmap.y = position.y;
  39.   myBitmap.regX = myImage.width / 2;
  40.   myBitmap.regY = myImage.height / 2;
  41.   myBitmap.onPress = clickHandler;
  42.   stage.addChild(myBitmap);
  43.   stage.update();
  44. }
  45. function clickHandler(eventObject) {
  46.   var mouseX = eventObject.stageX;
  47.   var mouseY = eventObject.stageY;
  48.   lastMouse = new Point(mouseX, mouseY);
  49.   origin = this.globalToLocal(mouseX, mouseY);
  50.   eventObject.onMouseMove = mouseMoveHandler;
  51.   eventObject.onMouseUp = mouseUpHandler;
  52.   instance = this;
  53.   update = updateDrag;
  54.   Ticker.addListener(window);
  55. }
  56. function mouseMoveHandler(eventObject) {
  57.   var mouseX = eventObject.stageX;
  58.   var mouseY = eventObject.stageY;
  59.   var radius = new Point(lastMouse.x - instance.x, lastMouse.y - instance.y);
  60.   var force = new Point(mouseX - lastMouse.x, mouseY - lastMouse.y);
  61.   var moment = crossProduct2D(radius, force);
  62.   angularVelocity += moment * RATIO;
  63.   lastMouse = new Point(mouseX, mouseY);
  64.   velocity = force;
  65. }
  66. function mouseUpHandler(eventObject) {
  67.   update = updateInertia;
  68.   this.onMouseMove = this.onMouseUp = null;
  69. }
  70. function tick() {
  71.   rotate();
  72.   stage.update();
  73. }
  74. function rotate() {
  75.   var mouseX = lastMouse.x;
  76.   var mouseY = lastMouse.y;
  77.   instance.rotation += angularVelocity;
  78.   var offsetPoint = instance.localToGlobal(origin.x, origin.y);
  79.   instance.x += mouseX - offsetPoint.x;
  80.   instance.y += mouseY - offsetPoint.y;
  81.   angularVelocity *= DECELERATION;
  82.   update(mouseX, mouseY);
  83. }
  84. function updateDrag(mouseX, mouseY) {
  85.   angularVelocity *= DECELERATION;
  86. }
  87. function updateInertia(mouseX, mouseY) {
  88.   var velocityX = velocity.x;
  89.   var velocityY = velocity.y;
  90.   if (Math.abs(velocityX) + Math.abs(velocityY) + Math.abs(angularVelocity) < 1) {
  91.     Ticker.removeListener(window);
  92.   } else {
  93.     lastMouse = new Point(mouseX + velocityX, mouseY + velocityY);
  94.     velocity = new Point(velocityX * DECELERATION, velocityY * DECELERATION);
  95.     angularVelocity *= DECELERATION;
  96.   }
  97. }
  98. function crossProduct2D(point0, point1) {
  99.   return point0.x * point1.y - point0.y * point1.x;
  100. }
  101. </script>


作成者: 野中文雄
作成日: 2012年6月2日


Copyright © 2001-2012 Fumio Nonaka.  All rights reserved.