TypeScript + CreateJSで開発(1)
[話者] 以前、HTML5を使ってPC/スマートフォンで動作するゲームのようなものを作ったんだ。動作はJavaScriptで書くわけだ。
canvasタグを一枚表示して、そのcanvasタグに動く画像を表示するのはJavaScriptだと大変らしいので、CreateJSというライブラリを使用した。ActionScript3.0経験者にはCreateJSはなじみやすいということだったからね。
そのときはJavaScriptを直接書いたのだが、TypeScriptだとさらに便利になるらしい。クラスが書きやすいとか次世代JavaScriptの書き方を学べるとか。
TypeScriptを試すぞ。
[合いの手] TypeScriptってどうすれば使えるようになるの?
[話者] Windows環境では以下のようにした。
環境
- TypeScript 1.5-alpha
- CreateJS 2014.12.12 (EaselJS 0.8, TweenJS 0.6, SoundJS 0.6, PreloadJS 0.6)
- Adobe Brackets 1.2
- brackets-typescript 0.2.0
- node.js 0.12.2
- npm 1.4.9
- Windows7 64bit
Windows7でポータブルなgitとnode.jsコマンドライン環境
https://gist.github.com/r-a-y/ead95fd11a1914657c29 の手順を参考にした。
- PortableGit取得・展開 https://github.com/msysgit/msysgit/releases/ PortableGit-1.9.5-preview20150319.7z
- 展開フォルダー内に 'home'フォルダーと'home/node'フォルダー作成
- $HOME環境変数をその'home'フォルダーにする http://markb.co.uk/portable-git-windows-setting-home-environment-variable.html
- 'PortableGit-1.9.5-preview20150319/etc/profile'の「HOME="$(cd "$HOME" ; pwd)"」の次の行に記入「HOME="/home"」
- node.exeを取得して'home/node'に配置 https://nodejs.org/download/
- npm-*zipを取得して'home/node'に展開 http://nodejs.org/dist/npm/ npm-1.4.9.zip
- home/node/node_modules/npm/bin/npmファイルを'home/node'にコピー
- 'home/node'に.gitconfigと.bashrcを配置
- .bashrcに「PATH=/home/node:$PATH」と記入
- git-bash.batを実行
- 起動時の「Welcome to Portable Git」を消す: etc/motd をrename
TypeScript + CreateJS環境を準備
- npm install typescript -g
- npm install tsd -g
- tsd init
- tsd query easeljs --action install --resolve --save
- tsd query soundjs --action install --resolve --save
- EaselJS等は個別に取得必要? http://www.createjs.com/Downloads で easeljs-0.8.0.min.js 等を取得して作業フォルダーに配置。
TypeScript編集にAdobe Brackets を使う
- TypeScript対応はPluginで可能。[File - Extension Manager] https://github.com/fdecampredon/brackets-typescript
{ "typescript": { "target": "ES5", "module": "CommonJS", "noImplicitAny": true, "sources" : [ "**/*.ts", ] } }
- フォント指定は[View - Theme]で可能
- 補完決定をenterではなくtabにする [debug - show env file] "insertHintOnTab": true
TypeScriptとJavaScriptの比較
[合いの手] TypeScriptとJavaScriptの比較を見たいなあ。
[話者] 簡単なサンプルで違いを見てみよう。
JavaScriptだとこうなる。
sample01.html
<!DOCTYPE html> <html> <!-- なにか表示するだけ --> <head> <title>Try01</title> <script src="easeljs-0.8.0.min.js"></script> <script src="sample01.js"></script> <style> body {margin:0; padding:1em; background-color:rgba(127,127,127,0); </style> </head> <body> <canvas id="theCanvas" width="320" height="240"></canvas> </body> </html>
sample01.js
window.onload = function () { main(); } function main() { var stage = new createjs.Stage('theCanvas'); var obj = new createjs.Shape(); obj.graphics.beginFill('#ff9999').drawCircle(0,0,50); obj.x = 100; obj.y = 100; stage.addChild(obj); createjs.Ticker.framerate = 30; createjs.Ticker.addEventListener('tick', handleTick); function handleTick(){ obj.x += 5; if (obj.x > stage.canvas.width) obj.x = 0; stage.update(); } }
ふむふむ
これをTypeScriptで書くとこうなる。
sample22.html
<!DOCTYPE html> <html> <head> <title>Try02</title> <script src="easeljs-0.8.0.min.js"></script> <script src="sample22.js"></script> <style> body {margin:0; padding:1em; background-color:rgba(245,245,245,1); </style> <script> </script> </head> <body> <canvas id="theCanvas" width="320" height="240"></canvas> </body> </html>
sample22.ts
/// <reference path="typings/easeljs/easeljs.d.ts" /> window.onload = function () { var m = new Main(); }; class Main { stage:createjs.Stage; obj:createjs.Shape; constructor() { this.stage = new createjs.Stage('theCanvas'); this.obj = new createjs.Shape(); this.obj.graphics.beginFill('#ff9999').drawCircle(0,0,50); this.obj.x = 100; this.obj.y = 100; this.stage.addChild(this.obj); createjs.Ticker.framerate = 30; createjs.Ticker.addEventListener('tick', this.handleTick); } handleTick = (event:createjs.Event):void => { this.obj.x += 5; if (this.obj.x > this.stage.canvas['width']) this.obj.x = 0; this.stage.update(); } };
このtsファイルを
$ tsc sample22.ts
とコンパイルすると、sample22.js が作成されて実行可能になる。
HTMLは同じだね。
TSファイルの「this.
」気になるな。
これはクラス内変数、クラス内関数にアクセスするとき必要になる。
クラス内変数、クラス内関数はclassの中で「var
」とか「function
」なしで宣言しているやつだ。それにはthisをつけるということだね。
ま、ちょっとソース読みにくくなるけど、逆にいうとクラス内変数を減らして、なるべく関数の引数とするよう自分を律する契機にはなるかも。
「handleTick = (event:createjs.Event):void => {..}
」はなんだろう?
これは関数定義だ。「function(){..}
」と「() => {..}
」はほぼ同じなんだ。しかし違いもある。コールバック関数で「function(){..}
」を使うと、その関数内のthisは実行時のthisになるのでクラス変数にアクセスできなくて実行時に以下のエラーになる。
Uncaught TypeError: Cannot read property 'x' of undefined
「handleTick = function(event:createjs.Event):void { this.obj.x ..}
」と書くとthis.obj.x
にアクセスできなくなっちゃうのか。
「handleTick = (event:createjs.Event):void => { this.obj.x ..}
」と書けばアクセスできるというわけだね。
ところで、コンパイルのとき --noImplicitAny
というオプションを付けたほうがいい、と言われているよ。つけないの?
それをつけると、if (this.obj.x > this.stage.canvas['width']) this.obj.x = 0;
の行でエラーになるんだ。
$ tsc --noImplicitAny sample22.ts sample22.ts(23,26): error TS7017: Index signature of object type implicitly has an 'any' type.
このエラー消せなかった。まあ、TypeScriptを導入したのはクラスを楽に書きたいという理由が主なので、静的型付けはあんまりこだわらなくていいかな、と思ったので --noImplicitAny
は使わないことにした。
this.stage.canvas.width
じゃなくて、this.stage.canvas['width']
という書き方になってるのは理由があるの?
前者だと
sample22.ts(23,26): error TS2339: Property 'width' does not exist on type 'Object | HTMLCanvasElement'.
というエラーになるんだ。理由は
http://www.atmarkit.co.jp/ait/articles/1501/29/news117.html
の記述にある「ドットを使ってプロパティにアクセスするとエラーになる」に該当するようだ。