スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

QUnit & jQuery でイベント起動処理のテストを行う

QUnitを使ったイベント起動処理のテスト方法について書く。
jQueryを使うことを前提とする。

1. 今回のテスト対象
簡単な例として、「四角形をクリックすると動く」だけのコードをテスト対象とする。
/* boxをクリックすると動く */
var mvb = {}; // namespace
/* 定数 */
mvb.con = {
  rootid: 'root', // rootdivのid
  boxid: 'box1', // boxのid
  boxtop: 20, // boxのcss top
  boxleft: 20, // boxのcss left初期値
  boxmvlen: 200, // boxの移動距離(px)
  boxclsnm: 'boxdiv', // boxのcss classname
  bgcol: 'rgb(51,153,255)' // boxの背景色
};
/* boxを移動 */
mvb.movebox = function() {
  var $box = $(this);
  var afterleft; // 移動後のleft
  if ($box.css('left') == mvb.con.boxleft + 'px') {
    afterleft = mvb.con.boxleft + mvb.con.boxmvlen;
  } else {
    afterleft = mvb.con.boxleft;
  }
  $box.animate({left: afterleft});
};
$(document).ready(function() {
  // boxを作成し、rootdivに追加
  var $box = $('<div/>').attr('id', mvb.con.boxid)
      .addClass(mvb.con.boxclsnm)
      .css('backgroundColor', mvb.con.bgcol)
      .css('top', mvb.con.boxtop).css('left', mvb.con.boxleft);
  $('#' + mvb.con.rootid).append($box);
  // イベント付与
  $box.click(mvb.movebox);
});

2. QUnit実行htmlファイル作成
テスト対象のコードがQUnit実行htmlファイル上で動くようにする。
そのため、今回の例では以下を追加している。
・テスト対象のコードが参照するCSSを記述
・テスト対象のコードが使用する要素<div id="root"></div>を記述
追加箇所を赤字にした。
なお、simpleAnime.jsがテストの対象コード、simpleAnimeTest.jsが以下で解説するテストコード。
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>イベント起動処理のテスト</title>
  <style TYPE="text/css">
  .rootdiv {
    position: relative;
  }
  .boxdiv {
    display: block;
    position: absolute;
    width: 100px;
    height: 100px;
    border-style: solid;
    border-width: 2px;
    border-color: #808080;
  }
  </style>

  <link rel="stylesheet" href="./qunit/qunit.css" type="text/css" media="screen">
  <script type="text/javascript" src="jquery.js" charset="UTF-8"></script>
  <script type="text/javascript" src="simpleAnime.js" charset="UTF-8"></script>
  <script type="text/javascript" src="./qunit/qunit.js" charset="UTF-8"></script>
  <script type="text/javascript" src="simpleAnimeTest.js" charset="UTF-8"></script>
</head>
<body>
  <h1 id="qunit-header">QUnit Test Suite</h1>
  <h2 id="qunit-banner"></h2>
  <div id="qunit-testrunner-toolbar"></div>
  <h2 id="qunit-userAgent"></h2>
  <ol id="qunit-tests"></ol>
  <div id="qunit-fixture">test markup</div>
  <div id="root"></div>
</body>
</html>

3. テストコード作成
作成した以下のテストコードについて解説する。
/* simpleAnime.js のテスト
 * jquery.js を前提としている
 */
var timeoutID;
module('$(document).ready');
asyncTest('waif for ready', 1, function() {
  timeoutID = window.setInterval(function() {
    if ( $('#' + mvb.con.boxid).length > 0) {
      ok(true, 'box has appended.');
      window.clearTimeout(timeoutID);
      start();
    }
  }, 100);
});
test('boxdiv', 4, function() {
  var $box = $('#' + mvb.con.boxid);
  strictEqual($box.hasClass(mvb.con.boxclsnm), true, 'classname');
  equal($box.css('backgroundColor').replace(/\s/g, ''), mvb.con.bgcol,
      'backgroundColor');
  equal($box.css('top'), mvb.con.boxtop + 'px', 'css top');
  equal($box.css('left'), mvb.con.boxleft + 'px', 'css left');
});

module('mvb.movebox');
asyncTest('movebox 1', 1, function() {
  // boxの位置を初期値にする
  var $box = $('#' + mvb.con.boxid);
  $box.css('left', mvb.con.boxleft);
  $box.click();
  timeoutID = window.setInterval(function() {
    if ( $box.css('left') == (mvb.con.boxleft + mvb.con.boxmvlen) + 'px') {
      ok(true, 'box moved left.');
      window.clearTimeout(timeoutID);
      start();
    }
  }, 100);
});
asyncTest('movebox 2', 1, function() {
  var $box = $('#' + mvb.con.boxid);
  $box.click();
  timeoutID = window.setInterval(function() {
    if ( $box.css('left') == mvb.con.boxleft + 'px') {
      ok(true, 'box moved right.');
      window.clearTimeout(timeoutID);
      start();
    }
  }, 100);
});

3.1 $(document).ready のテスト
asyncTest('waif for ready', ...)で、$(document).readyで指定した処理が終わるまで待つ。
asyncTest()については、[QUnit] asyncTest() および stop() を簡単に解説 で解説している。
$(document).readyの処理が終わってからテストコードが動くようにするため、最後の処理が実行されることを確認してからstart()することが望ましい。
今回の例では「クリックイベントが付与されたこと」を確認するのが望ましいのだが、簡単にできそうにないので「四角形が追加されたこと」を確認している。
($box.data('events') でセットされているイベントハンドラを参照できるのだが、jQuery内部で使用しているデータでAPIとしては提供されていない)
その後のtest('boxdiv', ...)で、$(document).readyの処理結果をチェックしている。

3.2 クリックイベントのテスト
asyncTest('movebox 1', ...)およびasyncTest('movebox 2', ...)内でクリックイベントを発行して、イベント起動処理が終わるまで待つ。
$(document).readyと同様、最後の処理が実行されることを確認してからstart()することが望ましい。
今回の例では「右(または左)に動いたこと」をチェックするだけなので、asyncTest()内でテストが完結している。必要であれば後続に処理結果チェック用のtest()を追加する。

3.3 テストコード作成時の注意事項
asyncTest()およびtest()外に記述されたコードは、start()を待たずに即実行される。
そのため、テストコードはasyncTest()およびtest()内に記述すること。

以上。

<関連エントリ>
QUnit+jQueryで、チンチロリンゲーム自動実行テストプログラムを作った
スポンサーサイト

[QUnit] asyncTest() および stop() を簡単に解説

QUnitでは、非同期テスト用のAPIとして asyncTest(), stop(), start() を提供している。
ある程度使い方がわかったので解説してみる。

1. asyncTest()
asyncTest()実行後は、start()が実行されるまで次のtest()またはasyncTest()を実行しない。
<asyncTest()の例>
  asyncTest('asyncTest', function() {
    setTimeout(function(){
      ok(true, 'asyncTest');
      start();
    }, 10);
  });
  test('after test', function() {
    ok(true, 'after test')
  });
実行結果は次のようになる。
asyncTest1.png

asyncTest()を使用しない場合、
  test('not asyncTest', function() {
    setTimeout(function(){
      ok(true, 'not asyncTest');
      start();
    }, 10);
  });
  test('after test', function() {
    ok(true, 'after test')
  });
実行結果は次のようになる。
asyncTest2.png
setTimeoutの結果を待たずにtest('after test', ...)が実行されるため、setTimeout内のテストがスキップされた状態になる。

2. stop()
asyncTest()と同様、stop()実行後も、start()が実行されるまで次のtest()またはasyncTest()を実行しない。
上記asyncTest()の例をstop()を使って書くと、
  test('stop test', function() {
    stop();
    setTimeout(function(){
      ok(true, 'stop test');
      start();
    }, 10);
  });
  test('after test', function() {
    ok(true, 'after test')
  });
となり、実行結果は次のようになる。
asyncTest3.png

3. asyncTest()およびstop()使用時の注意事項
asyncTest()もstop()も、次のtest()またはasyncTest()の実行を保留する。
しかし、test()またはasyncTest()外のコードは保留されずに実行される
例を示すと、
  var testval = 0;
  asyncTest('asyncTest', function() {
    setTimeout(function(){
      ok(true, 'testval = ' + testval);
      testval = 1;
      ok(true, 'testval = ' + testval);
      start();
    }, 10);
  });
  testval = 2;
  test('after test', function() {
    ok(true, 'testval = ' + testval);
  });
  testval = 3;
この実行結果は次のようになる。
asyncTest4.png
処理の実行順序が
asyncTest('asyncTest', ...) 実行でsetTimeout起動

testval = 2

testval = 3

10ミリ秒後にsetTimeout(...) が実行され、testval = 1 とstart()実行

test('after test', ...) 実行
となっていることがわかる。
これは、QUnitがtest()およびasyncTest()をキューイングして保留しているためで、キューイング対象外のコードは保留されずに実行される。

4. asyncTest()およびstop()を使用してできること
タイマー起動される処理、イベント起動処理(onLoadイベント含む)、サーバーと通信する処理などのテストができる。
次エントリで、QUnitを使ったイベント起動処理テストの例を書く予定。
(書きました → QUnit & jQuery でイベント起動処理のテストを行う

[QUnit]オブジェクトのダンプを取って内容を比較する

JavaScriptのテスト用プログラムを作ったので公開。
タイトルにはQUnitとあるが、ほかのツールでも使える。

[JavaScript]JSON的なダンプをとる関数を作ってみた
を参考にして、オブジェクトのダンプを取るプログラムを作成した。
objdump.js (文字コードはUTF-8)
ダンプを出力する関数 midump.objdump() の説明は以下のとおり。
/**
 * オブジェクトのダンプを出力する
 * @param {Object} obj ダンプ対象オブジェクト
 * @param {?number} dec numberを小数点以下何桁で四捨五入するか。
 *    四捨五入しない場合はnull
 * @param {boolean} linear 改行およびインデントせず、
 *    objectのmethodを出力しない場合true。それ以外の場合false
 * @param {boolean} escape 制御文字、ダブルクォートをエスケープする場合はtrue。
 *    それ以外の場合false。nullの場合のデフォルト値はture。
 *    linear == true の場合、
 *    ダブルクォートではなくシングルクォートがエスケープされる。
 * @param {?{html: boolean, dispf: boolean}} opt
 *    html: 改行を'<br />',スペースを'&nbsp;'で出力する場合true。
 *        改行を'\n',スペースを' 'で出力する場合false
 *    dispf: objectのmethodを出力する場合true。それ以外の場合false
 *    nullの場合のデフォルト値は全てtrueだが、linearの値が優先される。
 * @return {string} ダンプ結果
 */
midump.objdump = function(obj, dec, linear, escape, opt) {

テストコード用のコンパクトなダンプを出力するため、次の機能を作成した。
・1行ダンプ出力。(改行およびインデントせず、オブジェクトのメソッドを出力しない)
・numberを小数点以下何桁で四捨五入するかを引数で指定。

midump.objdump()を使った、QUnitでのテスト手順は以下のようになる。

1. オブジェクトの中身をダンプして予想されるテスト結果を作成
  midump.objdump(object, 四捨五入する小数点以下の桁, true, true)
でオブジェクトの中身をダンプする。

<ダンプするHTMLの例>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>オブジェクトの中身をダンプ</title>
  <script type="text/javascript" src="objdump.js" charset="UTF-8"></script>
  <script>
  var load = function() {
    // テスト対象のクラス
    var TestClass1 = function(arg1, arg2) {
      this.prop1 = [];
      for (var i = 0; i < 3; i++) {
        this.prop1[i] = new TestClass2(i, arg1);
      }
      this.prop2 = {};
      this.prop2.sub1 = arg2;
      this.prop2.sub2 = new TestClass2('sub2', arg2);
    };
    var TestClass2 = function(arg1, arg2) {
      this.prop3 = arg1;
      this.prop4 = arg2;
    };
    TestClass2.prototype.test = function() {
      return this.prop3;
    };
    
    var dumpdiv = document.getElementById('dump');
    dumpdiv.innerHTML = midump.objdump(new TestClass1(0.12345, 'b\n\'"'), 5, true, true);
  };
  </script>
</head>
<body onload="load();">
<div>dump result</div>
<div id="dump"></div>
</body>
</html>
このHTMLを実行すると、次のように出力される。
dump result
'{prop1:[{prop3:0,prop4:0.1235},{prop3:1,prop4:0.1235},{prop3:2,prop4:0.1235}],prop2:{sub1:"b\n\'"",sub2:{prop3:"sub2",prop4:"b\n\'""}}}'

このダンプ結果から、予想されるテスト結果は次のようになる。
// 予想される結果のダンプ
var expobjdump = '\'{prop1:[{prop3:0,prop4:0.1235},{prop3:1,prop4:0.1235},{prop3:2,prop4:0.1235}],prop2:{sub1:"b\n\'"",sub2:{prop3:"sub2",prop4:"b\n\'""}}}\'';
両端のシングルクォートが '\' になることに注意。(1行ダンプ出力結果がシングルクォートで囲われることと、エスケープ文字の関係上こうしている)

なお、1行ダンプ出力ではなく、見やすいダンプを出力したい場合は
dumpdiv.innerHTML = midump.objdump(new TestClass1(0.12345, 'b\n\'"'), 5, false, true, {html: true, dispf: true});
と書けばよい。

2. テストコードを作成
予想されるテスト結果とmidump.objdump()を使ってテストコードを作成する。
上記の例の場合、テストコードは以下のようになる。
    // テスト対象のクラス
    var TestClass1 = function(arg1, arg2) {
      this.prop1 = [];
      for (var i = 0; i < 3; i++) {
        this.prop1[i] = new TestClass2(i, arg1);
      }
      this.prop2 = {};
      this.prop2.sub1 = arg2;
      this.prop2.sub2 = new TestClass2('sub2', arg2);
    };
    var TestClass2 = function(arg1, arg2) {
      this.prop3 = arg1;
      this.prop4 = arg2;
    };
    TestClass2.prototype.test = function() {
      return this.prop3;
    };
    
    // 予想される結果のダンプ
    var expobjdump = '\'{prop1:[{prop3:0,prop4:0.1235},{prop3:1,prop4:0.1235},{prop3:2,prop4:0.1235}],prop2:{sub1:"b\n\'"",sub2:{prop3:"sub2",prop4:"b\n\'""}}}\'';

    // テスト対象のクラスと予想される結果とを比較
    var clsins = new TestClass1(0.12345, 'b\n\'"');
    test('test', function() {
      equal(midump.objdump(clsins, 5, true, false), expobjdump, 'compare clsins with expobj');
    });
比較する際のmidump.objdump()ではエスケープ処理をしないため、4番目の引数がfalseになることに注意。

このテストコードをQUnitで実行すればよい。

この方法で実際にテストしているのだが、HTMLをいじって出力し、ブラウザからコピ&ペーストしてテストコードを作成するので手間がかかる。
もっといい方法がありそうだ。

<関連エントリ>
[QUnit]deepEqualでクラスインスタンスとオブジェクトを比較する方法

[QUnit]deepEqualでクラスインスタンスとオブジェクトを比較する方法

この方法は、2011-05-16 commit版のQUnitで確認しています。
QUnit.equivの実装が変わるとdeepEqualをpassしなくなる恐れがありますので注意してください。

QUnitには、deepEqualというオブジェクトの比較に便利なassertionがある。
以下のように、あらかじめ用意した結果との比較が簡単にできる。
test('testfunc()のテスト', 1, function() {
  // 予想される結果
  var expobj = [
    {prop1: 1, prop2: 1},
    {prop1: ['a', 12, {a: null, b: 15}], prop2: null}
  ];

  var testobj = testfunc();
  deepEqual(testobj, expobj, 'compare testobj with expobj');
});

しかし、クラスインスタンスとオブジェクトを比較する場合、オブジェクトにメソッドとコンストラクタをセットしないとdeepEqualをpassしない。
そこで、メソッドとコンストラクタをセットするのに便利なプログラムを作成した。
setmethod.js(文字コードはUTF-8)
提供している関数はmiqtutl.setmethod()だけ。
  var rtn = miqtutl.setmethod(chkobj, targetobj);
で、chkobjのメソッドとコンストラクタををtargetobjにセットできる。

関数からはエラーメッセージの配列がreturnされるので、
  var rtn = miqtutl.setmethod(chkobj, targetobj);
  deepEqual(rtn, [], 'no error message');
とすることで、きちんとセットできたことを確認できる。
エラーメッセージは、メソッド付与対象のオブジェクトが存在しない場合や、メソッドと同名のプロパティが既に存在している場合などに出力される。
setmethod1.png
deepEqual(rtn, [])でリターン値をチェックすると、エラーの場合上のように表示される。

miqtutl.setmethod()を使って、以下のようにクラスインスタンスとオブジェクトとを比較できる。
// テスト対象のクラス
var TestClass1 = function(arg1, arg2) {
  this.prop1 = [];
  for (var i = 0; i < 3; i++) {
    this.prop1[i] = new TestClass2(i, arg1);
  }
  this.prop2 = {};
  this.prop2.sub1 = arg2;
  this.prop2.sub2 = new TestClass2('sub2', arg2);
};
var TestClass2 = function(arg1, arg2) {
  this.prop3 = arg1;
  this.prop4 = arg2;
};
TestClass2.prototype.test = function() {
  return this.prop3;
};

test('TestClass1のconstructorのテスト', 2, function() {
  // 予想される結果
  var expobj = {
    prop1: [{prop3: 0, prop4: 'a'}, {prop3: 1, prop4: 'a'},
        {prop3: 2, prop4: 'a'}],
    prop2: {sub1: 'b', sub2: {prop3: 'sub2', prop4: 'b'}}
  };
  // クラスインスタンスを生成し、expobjにクラスのメソッドとコンストラクタをセット
  var clsins = new TestClass1('a', 'b');
  var rtn = miqtutl.setmethod(clsins, expobj);
  deepEqual(rtn, [], 'no error message');
  // クラスインスタンスと予想される結果とを比較
  deepEqual(clsins, expobj, 'compare clsins with expobj');
});

特記事項
・miqtutl.setmethod()は、メソッドとコンストラクタをコピーしているのではなく、参照をセットしているだけ。
・メソッドをセットしてもプロパティの順序が異なるので、deepEqualはpassしてもDiffは出力される。
・再帰の深度が10になると例外がthrowされる。setmethod.jsのmaxDepthの値を変えることで、最大深度を変更できる。

以上。
予想される結果を作成するために、オブジェクトのダンプをテキスト出力するプログラムも作ったので、そのうち公開するかもしれない。
(公開しました → [QUnit]オブジェクトのダンプを取って内容を比較する

QUnitでのテストに使えそうなプログラム作成中

[QUnit]deepEqualでクラスインスタンスとオブジェクトを比較してハマる
でクラスインスタンスとオブジェクトをdeepEqualする方法を書いたのだが、比較対象オブジェクトにクラスメソッドをセットしないとdeepEqualをpassしない。

クラスメソッドをセットするだけなら、以下のようなテストプログラムを書けばいい。
var TestClass = function() {
  this.prop1 = 1;
  this.prop2 = 1;
};
TestClass.prototype.meth = function() {
  this.prop1++;
};

test('deepEqual class isntance and object', 2, function() {
  var clsins = new TestClass();
  var obj = {prop1: 1, prop2: 1};
  var errmsg = '';
  for (var prop in clsins) {
    if (typeof clsins[prop] == 'function') {
      if (obj[prop] != undefined) {
        errmsg = 'property [' + prop + '] already exists, so cannot set method.';
      } else {
        obj[prop] = clsins[prop];
      }
    }
  }
  obj.constructor = clsins.constructor;
  clsins.constructor = clsins.constructor;
  equal(errmsg, '', 'no error message');
  deepEqual(clsins, obj, 'deepEqual');
});
単純なクラスならこれでいいのだが、オブジェクトのプロパティや配列の中にクラスインスタンスがあるようなケースでは使えない。

せっかくなので、再帰的にオブジェクトや配列の中をたどって、比較対象オブジェクトにクラスメソッドをセットするプログラムを作成することにした。
動作確認はできたので、エラーケースのテストが終わったら公開するつもり。

[QUnit]deepEqualでクラスインスタンスとオブジェクトを比較してハマる

引き続きQUnitでテストしている。
QUnitにはdeepEqualというassertionがある。
配列やオブジェクトのメンバを再帰的に比較してくれるので役に立つ。

クラスのコンストラクタのテストで、以下のようなテストコードを作成した。
  var TestClass = function() {
    this.prop = 1;
  };
  test('TestClass', function(){
    var class1 = new TestClass();
    deepEqual(class1, {prop:1});
  });
メンバが同じオブジェクトなのに、deepEqualがfailedになった。しかもDiffが出ないので理由がわからない。

デバッガでたどって調べたところ、QUnit.equivの以下の箇所でfailedと判定されていた。
  // comparing constructors is more strict than using instanceof
  if ( a.constructor !== b.constructor) {
   return false;
  }
failedとなった理由は、同一クラスから生成されたインスタンスでないからだとわかった。

上記の箇所さえ通ればいいのではと思い、テストコードを変更
  var TestClass = function() {
    this.prop = 1;
  };
  test('TestClass', function(){
    var class1 = new TestClass();
    var object1 = {prop:1};
    object1.constructor = class1.constructor;
    deepEqual(class1, object1);
  });
だが再びfailed。理由はclass1.constructorが無いから。
class1.constructorは組み込みプロパティなので for ... in の対象にならず、QUnitでは抽出されない。

そこで、constructorプロパティをユーザー定義プロパティに上書きした。
  var TestClass = function() {
    this.prop = 1;
  };
  test('TestClass', function(){
    var class1 = new TestClass();
    var object1 = {prop:1};
    object1.constructor = class1.constructor;
    class1.constructor = class1.constructor;
    deepEqual(class1, object1);
  });
これで晴れてpassedになった。

めでたしめでたし・・・なのか?
QUnit.equivが変更されたらfailedになりそう。

<関連エントリ>
[QUnit]deepEqualでクラスインスタンスとオブジェクトを比較する方法
  クラスにメソッドがある場合、オブジェクトにメソッドもセットしないとdeepEqualをpassしない。
  オブジェクトのプロパティや配列中にあるクラスインスタンスを再帰的に探してメソッドを付与するプログラムを公開。

QUnitを使ってみる

昨日公開したプログラムを今更テストしている。
テストツールはQUnitを使うことにした。
今回初めて使うのだが、覚えることが少ないのですぐに使うことができた。

テスト起動用HTMLファイルを作るところでちょっと戸惑ったが、
ダウンロードしたフォルダの test\index.html をコピーして変更すればすぐできる。
変更する箇所は
    <script type="text/javascript" src="../qunit/qunit.js"></script>
の前にテスト対象プログラムを読み込む<script>タグを記述。
    <script type="text/javascript" src="test.js"></script>
    <script type="text/javascript" src="same.js"></script>
をテストプログラムを読み込む<script>タグに置き換える。
の二箇所だけ。

テストプログラムは、test()の中でAssertionを行う関数を記述するだけでいい。
AssertionのdeepEqual()は、オブジェクトや配列の中を再帰的にチェックしてくれるので
  deepEqual(diceins.camAngles_,
      [{x: 47, y: 35, z: 0}, {x: 47 + 90, y: 35, z: 0}, {x: 47, y: 35, z: 70}],
      'this.camAngles_');
のように使うことができて便利だ。
例外発生をチェックできるraises()もある。

以前ちょっと触ったJsUnitと比較すると、
・JsUnitのようにテストファイルを指定してRUNボタンを押すような手間が無い。HTMLファイルを起動するだけでQUnitのテストが実行できる。
・JsUnitでは「Test Functionを記述したhtmlは、testRunner.htmlと同一か配下のフォルダに格納しないと動かない」という制約が(自分の環境では)あったが、QUnitではそのような制約は無かった。

自分にとってはQUnitの方が使いやすいので、これからはこっちを使おう。

HTML5 Canvasで、画像ファイルを使わずに立体サイコロを振る

HTML5 Canvasで、3Dっぽい立体サイコロを振るプログラムを作った。
2D/3Dサイコロを振る | 無職のHTML5 Canvas
2D/3Dサイコロを振る
画像ファイルを使わずに、Pre3dというライブラリを使用して3Dサイコロを描いている。
一応Internet Explorerでも動くが、excanvas.jsを使用しているため動きが遅い。

以下、今後やりたいこと。
・動作確認しかやってないので、ツールを使ってちゃんとテストする。
・Pre3dのリファレンスやチュートリアルみたいなのを作成する。
・アニメーション時のメインループ回りを共通化するために作ったライブラリを洗練する。

まずはテストだな。

久しぶりに公開しようとしたらIE対応でハマる

前回の記事で書いた、HTML5 Canvasで描いた立体サイコロが回転するプログラムを作成した。

公開しようとIEで動作確認したら動かない。excanvas.jsが効いていないようだ。
開発者ツールで確認すると、excanvas.jsはちゃんと読み込まれている。
IEで動いている別プログラムと比べて調査したところ、onload時の処理を $(document).ready() に書いているのが原因だとわかった。
$(window).load() だとちゃんとexcanvas.jsが効いた。

3ヶ月前に書いた別プログラムでは、わざわざ
//$(document).ready(function() {
$(window).load(function() {
とコメントアウトされていた。3ヶ月前にハマったことをすっかり忘れていた。

そんなわけで、公開は明日に延期。

HTML5 Canvasで立体サイコロを描く

HTML5 Canvasでサイコロを振るプログラムを作ろうと思った。
平べったい2次元サイコロはすぐ作れたのだが、せっかくだから3Dっぽい立体サイコロを振ってみたい。
GIMPで立体的な画像を何種類か作ってもいいのだが、JavaScriptで描けないかと思って調べてみた。

webで3Dを描くならWebGLがいいみたいだが、自分の非力なWindowsXPノートPCで動かすのは無理があるようだ。
3Dを描くJavaScriptライブラリを探したところ、Pre3dを見つけた。
リファレンスは見当たらないが、ファイルは少ないし普通に読めるぐらいのサイズだ。

ライブラリのソースコードを読みながら描いた結果がこれ。
dice1
光が当たる方向や、光の強さを変えるメソッドが無くて、ソースコードを解析してプロパティをいじった。

次はサイコロを回転させよう。

Pre3dのソースコードを解析した結果や、サイコロを描くチュートリアルみたいなのを後でまとめようかな。

<関連エントリ>
HTML5 Canvasで、画像ファイルを使わずに立体サイコロを振る
Pre3dでHTML5 Canvas上に立体サイコロを描く手順をまとめた
プロフィール

himax64

Author: 南西
30代後半の無職です。
就活もせずダラダラ生きてます。
作ったもの

最新記事
人気記事
検索フォーム
カテゴリ
月別アーカイブ
最新コメント
最新トラックバック
RSSリンクの表示
QRコード
QRコード
カウンター
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。