1 /** 2 * enchant.js v0.8.3 3 * http://enchantjs.com 4 * 5 * Copyright UEI Corporation 6 * Released under the MIT license. 7 */ 8 9 (function(window, undefined) { 10 11 // ECMA-262 5th edition Functions 12 if (typeof Object.defineProperty !== 'function') { 13 Object.defineProperty = function(obj, prop, desc) { 14 if ('value' in desc) { 15 obj[prop] = desc.value; 16 } 17 if ('get' in desc) { 18 obj.__defineGetter__(prop, desc.get); 19 } 20 if ('set' in desc) { 21 obj.__defineSetter__(prop, desc.set); 22 } 23 return obj; 24 }; 25 } 26 if (typeof Object.defineProperties !== 'function') { 27 Object.defineProperties = function(obj, descs) { 28 for (var prop in descs) { 29 if (descs.hasOwnProperty(prop)) { 30 Object.defineProperty(obj, prop, descs[prop]); 31 } 32 } 33 return obj; 34 }; 35 } 36 if (typeof Object.create !== 'function') { 37 Object.create = function(prototype, descs) { 38 function F() { 39 } 40 41 F.prototype = prototype; 42 var obj = new F(); 43 if (descs != null) { 44 Object.defineProperties(obj, descs); 45 } 46 return obj; 47 }; 48 } 49 if (typeof Object.getPrototypeOf !== 'function') { 50 Object.getPrototypeOf = function(obj) { 51 return obj.__proto__; 52 }; 53 } 54 55 if (typeof Function.prototype.bind !== 'function') { 56 Function.prototype.bind = function(thisObject) { 57 var func = this; 58 var args = Array.prototype.slice.call(arguments, 1); 59 var Nop = function() { 60 }; 61 var bound = function() { 62 var a = args.concat(Array.prototype.slice.call(arguments)); 63 return func.apply( 64 this instanceof Nop ? this : thisObject || window, a); 65 }; 66 Nop.prototype = func.prototype; 67 bound.prototype = new Nop(); 68 return bound; 69 }; 70 } 71 72 window.getTime = (function() { 73 var origin; 74 if (window.performance && window.performance.now) { 75 origin = Date.now(); 76 return function() { 77 return origin + window.performance.now(); 78 }; 79 } else if (window.performance && window.performance.webkitNow) { 80 origin = Date.now(); 81 return function() { 82 return origin + window.performance.webkitNow(); 83 }; 84 } else { 85 return Date.now; 86 } 87 }()); 88 89 // define requestAnimationFrame 90 window.requestAnimationFrame = 91 window.requestAnimationFrame || 92 window.mozRequestAnimationFrame || 93 window.webkitRequestAnimationFrame || 94 window.msRequestAnimationFrame || 95 (function() { 96 var lastTime = window.getTime(); 97 var frame = 1000 / 60; 98 return function(func) { 99 var _id = setTimeout(function() { 100 lastTime = window.getTime(); 101 func(lastTime); 102 }, Math.max(0, lastTime + frame - window.getTime())); 103 return _id; 104 }; 105 }()); 106 107 /** 108 * グローバルにライブラリのクラスをエクスポートする. 109 * 110 * 引数に何も渡さない場合enchant.jsで定義されたクラス及びプラグインで定義されたクラス 111 * 全てがエクスポートされる. 引数が一つ以上の場合はenchant.jsで定義されたクラスのみ 112 * がデフォルトでエクスポートされ, プラグインのクラスをエクスポートしたい場合は明示的に 113 * プラグインの識別子を引数として渡す必要がある. 114 * 115 * @example 116 * enchant(); // 全てのクラスがエクスポートされる 117 * enchant(''); // enchant.js本体のクラスのみがエクスポートされる 118 * enchant('ui'); // enchant.js本体のクラスとui.enchant.jsのクラスがエクスポートされる 119 * 120 * @param {...String} [modules] エクスポートするモジュール. 複数指定できる. 121 * @function 122 * @global 123 * @name enchant 124 */ 125 var enchant = function(modules) { 126 if (modules != null) { 127 if (!(modules instanceof Array)) { 128 modules = Array.prototype.slice.call(arguments); 129 } 130 modules = modules.filter(function(module) { 131 return [module].join(); 132 }); 133 } 134 (function include(module, prefix) { 135 var submodules = [], 136 i, len; 137 for (var prop in module) { 138 if (module.hasOwnProperty(prop)) { 139 if (typeof module[prop] === 'function') { 140 window[prop] = module[prop]; 141 } else if (typeof module[prop] === 'object' && module[prop] !== null && Object.getPrototypeOf(module[prop]) === Object.prototype) { 142 if (modules == null) { 143 submodules.push(prop); 144 } else { 145 i = modules.indexOf(prefix + prop); 146 if (i !== -1) { 147 submodules.push(prop); 148 modules.splice(i, 1); 149 } 150 } 151 } 152 } 153 } 154 155 for (i = 0, len = submodules.length; i < len; i++) { 156 include(module[submodules[i]], prefix + submodules[i] + '.'); 157 } 158 }(enchant, '')); 159 160 // issue 185 161 if (enchant.Class.getInheritanceTree(window.Game).length <= enchant.Class.getInheritanceTree(window.Core).length) { 162 window.Game = window.Core; 163 } 164 165 if (modules != null && modules.length) { 166 throw new Error('Cannot load module: ' + modules.join(', ')); 167 } 168 }; 169 170 // export enchant 171 window.enchant = enchant; 172 173 window.addEventListener("message", function(msg, origin) { 174 try { 175 var data = JSON.parse(msg.data); 176 if (data.type === "event") { 177 enchant.Core.instance.dispatchEvent(new enchant.Event(data.value)); 178 } else if (data.type === "debug") { 179 switch (data.value) { 180 case "start": 181 enchant.Core.instance.start(); 182 break; 183 case "pause": 184 enchant.Core.instance.pause(); 185 break; 186 case "resume": 187 enchant.Core.instance.resume(); 188 break; 189 case "tick": 190 enchant.Core.instance._tick(); 191 break; 192 default: 193 break; 194 } 195 } 196 } catch (e) { 197 // ignore 198 } 199 }, false); 200 201 /** 202 * @name enchant.Class 203 * @class 204 * クラスのクラス. 205 * @param {Function} [superclass] 継承するクラス. 206 * @param {*} [definition] クラス定義. 207 * @constructor 208 */ 209 enchant.Class = function(superclass, definition) { 210 return enchant.Class.create(superclass, definition); 211 }; 212 213 /** 214 * クラスを作成する. 215 * 216 * ほかのクラスを継承したクラスを作成する場合, コンストラクタはデフォルトで 217 * 継承元のクラスのものが使われる. コンストラクタをオーバーライドする場合継承元の 218 * コンストラクタを適用するには明示的に呼び出す必要がある. 219 * 220 * @example 221 * var Ball = Class.create({ // 何も継承しないクラスを作成する 222 * initialize: function(radius) { ... }, // メソッド定義 223 * fall: function() { ... } 224 * }); 225 * 226 * var Ball = Class.create(Sprite); // Spriteを継承したクラスを作成する 227 * var Ball = Class.create(Sprite, { // Spriteを継承したクラスを作成する 228 * initialize: function(radius) { // コンストラクタを上書きする 229 * Sprite.call(this, radius * 2, radius * 2); // 継承元のコンストラクタを適用する 230 * this.image = core.assets['ball.gif']; 231 * } 232 * }); 233 * 234 * @param {Function} [superclass] 継承するクラス. 235 * @param {*} [definition] クラス定義. 236 * @static 237 */ 238 enchant.Class.create = function(superclass, definition) { 239 if (superclass == null && definition) { 240 throw new Error("superclass is undefined (enchant.Class.create)"); 241 } else if (superclass == null) { 242 throw new Error("definition is undefined (enchant.Class.create)"); 243 } 244 245 if (arguments.length === 0) { 246 return enchant.Class.create(Object, definition); 247 } else if (arguments.length === 1 && typeof arguments[0] !== 'function') { 248 return enchant.Class.create(Object, arguments[0]); 249 } 250 251 for (var prop in definition) { 252 if (definition.hasOwnProperty(prop)) { 253 if (typeof definition[prop] === 'object' && definition[prop] !== null && Object.getPrototypeOf(definition[prop]) === Object.prototype) { 254 if (!('enumerable' in definition[prop])) { 255 definition[prop].enumerable = true; 256 } 257 } else { 258 definition[prop] = { value: definition[prop], enumerable: true, writable: true }; 259 } 260 } 261 } 262 var Constructor = function() { 263 if (this instanceof Constructor) { 264 Constructor.prototype.initialize.apply(this, arguments); 265 } else { 266 return new Constructor(); 267 } 268 }; 269 Constructor.prototype = Object.create(superclass.prototype, definition); 270 Constructor.prototype.constructor = Constructor; 271 if (Constructor.prototype.initialize == null) { 272 Constructor.prototype.initialize = function() { 273 superclass.apply(this, arguments); 274 }; 275 } 276 277 var tree = this.getInheritanceTree(superclass); 278 for (var i = 0, l = tree.length; i < l; i++) { 279 if (typeof tree[i]._inherited === 'function') { 280 tree[i]._inherited(Constructor); 281 break; 282 } 283 } 284 285 return Constructor; 286 }; 287 288 /** 289 * クラスの継承関係を取得する. 290 * @param {Function} コンストラクタ. 291 * @return {Function[]} 親のコンストラクタ. 292 */ 293 enchant.Class.getInheritanceTree = function(Constructor) { 294 var ret = []; 295 var C = Constructor; 296 var proto = C.prototype; 297 while (C !== Object) { 298 ret.push(C); 299 proto = Object.getPrototypeOf(proto); 300 C = proto.constructor; 301 } 302 return ret; 303 }; 304 305 /** 306 * @namespace 307 * enchant.js の環境変数. 308 * new Core() を呼ぶ前に変更することで変更することで, 動作設定を変えることができる. 309 */ 310 enchant.ENV = { 311 /** 312 * enchant.jsのバージョン. 313 * @type String 314 */ 315 VERSION: '0.8.3', 316 /** 317 * 実行中のブラウザの種類. 318 * @type String 319 */ 320 BROWSER: (function(ua) { 321 if (/Eagle/.test(ua)) { 322 return 'eagle'; 323 } else if (/Opera/.test(ua)) { 324 return 'opera'; 325 } else if (/MSIE|Trident/.test(ua)) { 326 return 'ie'; 327 } else if (/Chrome/.test(ua)) { 328 return 'chrome'; 329 } else if (/(?:Macintosh|Windows).*AppleWebKit/.test(ua)) { 330 return 'safari'; 331 } else if (/(?:iPhone|iPad|iPod).*AppleWebKit/.test(ua)) { 332 return 'mobilesafari'; 333 } else if (/Firefox/.test(ua)) { 334 return 'firefox'; 335 } else if (/Android/.test(ua)) { 336 return 'android'; 337 } else { 338 return ''; 339 } 340 }(navigator.userAgent)), 341 /** 342 * 実行中のブラウザに対応するCSSのベンダープレフィックス. 343 * @type String 344 */ 345 VENDOR_PREFIX: (function() { 346 var ua = navigator.userAgent; 347 if (ua.indexOf('Opera') !== -1) { 348 return 'O'; 349 } else if (/MSIE|Trident/.test(ua)) { 350 return 'ms'; 351 } else if (ua.indexOf('WebKit') !== -1) { 352 return 'webkit'; 353 } else if (navigator.product === 'Gecko') { 354 return 'Moz'; 355 } else { 356 return ''; 357 } 358 }()), 359 /** 360 * ブラウザがタッチ入力をサポートしているかどうか. 361 * @type Boolean 362 */ 363 TOUCH_ENABLED: (function() { 364 var div = document.createElement('div'); 365 div.setAttribute('ontouchstart', 'return'); 366 return typeof div.ontouchstart === 'function'; 367 }()), 368 /** 369 * 実行中の環境がRetina DisplayのiPhoneかどうか. 370 * @type Boolean 371 */ 372 RETINA_DISPLAY: (function() { 373 if (navigator.userAgent.indexOf('iPhone') !== -1 && window.devicePixelRatio === 2) { 374 var viewport = document.querySelector('meta[name="viewport"]'); 375 if (viewport == null) { 376 viewport = document.createElement('meta'); 377 document.head.appendChild(viewport); 378 } 379 viewport.setAttribute('content', 'width=640'); 380 return true; 381 } else { 382 return false; 383 } 384 }()), 385 /** 386 * サウンドの再生にHTMLAudioElement/WebAudioの代わりにflashのプレーヤーを使うかどうか. 387 * @type Boolean 388 */ 389 USE_FLASH_SOUND: (function() { 390 var ua = navigator.userAgent; 391 var vendor = navigator.vendor || ""; 392 // non-local access, not on mobile mobile device, not on safari 393 return (location.href.indexOf('http') === 0 && ua.indexOf('Mobile') === -1 && vendor.indexOf('Apple') !== -1); 394 }()), 395 /** 396 * クリック/タッチ時の規定の動作を許可するhtmlタグ名. 397 * ここに追加したhtmlタグへのイベントはpreventDefaultされない. 398 * @type String[] 399 */ 400 USE_DEFAULT_EVENT_TAGS: ['input', 'textarea', 'select', 'area'], 401 /** 402 * SurfaceのメソッドとしてアクセスできるようにするCanvasRenderingContext2Dのメソッド. 403 * @type String[] 404 */ 405 CANVAS_DRAWING_METHODS: [ 406 'putImageData', 'drawImage', 'drawFocusRing', 'fill', 'stroke', 407 'clearRect', 'fillRect', 'strokeRect', 'fillText', 'strokeText' 408 ], 409 /** 410 * キーバインドのテーブル. 411 * デフォルトで 'left, 'up', 'right', 'down' のイベントが使用可能. 412 * @example 413 * enchant.ENV.KEY_BIND_TABLE = { 414 * 37: 'left', 415 * 38: 'up', 416 * 39: 'right', 417 * 40: 'down', 418 * 32: 'a', //-> スペースキーをaボタンとして使う. 419 * }; 420 * @type Object 421 */ 422 KEY_BIND_TABLE: { 423 37: 'left', 424 38: 'up', 425 39: 'right', 426 40: 'down' 427 }, 428 /** 429 * キー押下時の規定の動作を抑止するキーコード. 430 * ここに追加したキーによるイベントはpreventDefaultされる. 431 * @type Number[] 432 */ 433 PREVENT_DEFAULT_KEY_CODES: [37, 38, 39, 40], 434 /** 435 * Mobile Safariでサウンドの再生を有効にするかどうか. 436 * @type Boolean 437 */ 438 SOUND_ENABLED_ON_MOBILE_SAFARI: true, 439 /** 440 * "touch to start" のシーンを使用するかどうか. 441 * Mobile SafariでWebAudioのサウンドを再生するためには, 442 * 一度タッチイベントハンドラ内で音声を流す必要があるため, 443 * Mobile Safariでの実行時にはこのシーンが追加される. 444 * falseにすることで, このシーンを表示しないようにできるが, 445 * その場合は, 自身の責任でサウンドを有効化する必要がある. 446 * @type Boolean 447 */ 448 USE_TOUCH_TO_START_SCENE: true, 449 /** 450 * WebAudioを有効にするどうか. 451 * trueならサウンドの再生の際HTMLAudioElementの代わりにWebAudioAPIを使用する. 452 * @type Boolean 453 */ 454 USE_WEBAUDIO: (function() { 455 return location.protocol !== 'file:'; 456 }()), 457 /** 458 * アニメーション機能を有効にするかどうか. 459 * trueだと, Node#tlにTimelineオブジェクトが作成される. 460 * @type Boolean 461 */ 462 USE_ANIMATION: true, 463 /** 464 * タッチ位置の判定範囲. 465 * 判定範囲はCOLOR_DETECTION_LEVEL * 2 + 1の正方形になる. 466 * @type Boolean 467 */ 468 COLOR_DETECTION_LEVEL: 2 469 }; 470 471 /** 472 * @scope enchant.Event.prototype 473 */ 474 enchant.Event = enchant.Class.create({ 475 /** 476 * @name enchant.Event 477 * @class 478 * DOM Event風味の独自イベント実装を行ったクラス. 479 * ただしフェーズの概念はなし. 480 * @param {String} type Eventのタイプ 481 * @constructs 482 */ 483 initialize: function(type) { 484 /** 485 * イベントのタイプ. 486 * @type String 487 */ 488 this.type = type; 489 /** 490 * イベントのターゲット. 491 * @type * 492 */ 493 this.target = null; 494 /** 495 * イベント発生位置のx座標. 496 * @type Number 497 */ 498 this.x = 0; 499 /** 500 * イベント発生位置のy座標. 501 * @type Number 502 */ 503 this.y = 0; 504 /** 505 * イベントを発行したオブジェクトを基準とするイベント発生位置のx座標. 506 * @type Number 507 */ 508 this.localX = 0; 509 /** 510 * イベントを発行したオブジェクトを基準とするイベント発生位置のy座標. 511 * @type Number 512 */ 513 this.localY = 0; 514 }, 515 _initPosition: function(pageX, pageY) { 516 var core = enchant.Core.instance; 517 this.x = this.localX = (pageX - core._pageX) / core.scale; 518 this.y = this.localY = (pageY - core._pageY) / core.scale; 519 } 520 }); 521 522 /** 523 * Coreのロード完了時に発生するイベント. 524 * 525 * 画像のプリロードを行う場合ロードが完了するのを待ってゲーム開始時の処理を行う必要がある. 526 * 発行するオブジェクト: {@link enchant.Core} 527 * 528 * @example 529 * var core = new Core(320, 320); 530 * core.preload('player.gif'); 531 * core.onload = function() { 532 * ... // ゲーム開始時の処理を記述 533 * }; 534 * core.start(); 535 * @type String 536 */ 537 enchant.Event.LOAD = 'load'; 538 539 /** 540 * エラーの発生をCoreに伝える際に発生するイベント. 541 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Surface}, {@link enchant.WebAudioSound}, {@link enchant.DOMSound} 542 */ 543 enchant.Event.ERROR = 'error'; 544 545 /** 546 * 表示サイズが変わったときに発生するイベント. 547 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 548 @type String 549 */ 550 enchant.Event.CORE_RESIZE = 'coreresize'; 551 552 /** 553 * Coreのロード進行中に発生するイベント. 554 * プリロードする画像が一枚ロードされる度に発行される. 発行するオブジェクト: {@link enchant.LoadingScene} 555 * @type String 556 */ 557 enchant.Event.PROGRESS = 'progress'; 558 559 /** 560 * フレーム開始時に発生するイベント. 561 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Node} 562 * @type String 563 */ 564 enchant.Event.ENTER_FRAME = 'enterframe'; 565 566 /** 567 * フレーム終了時に発生するイベント. 568 * 発行するオブジェクト: {@link enchant.Core} 569 * @type String 570 */ 571 enchant.Event.EXIT_FRAME = 'exitframe'; 572 573 /** 574 * Sceneが開始したとき発生するイベント. 575 * 発行するオブジェクト: {@link enchant.Scene} 576 * @type String 577 */ 578 enchant.Event.ENTER = 'enter'; 579 580 /** 581 * Sceneが終了したとき発生するイベント. 582 * 発行するオブジェクト: {@link enchant.Scene} 583 * @type String 584 */ 585 enchant.Event.EXIT = 'exit'; 586 587 /** 588 * Nodeに子が追加されたとき発生するイベント. 589 * 発行するオブジェクト: {@link enchant.Group}, {@link enchant.Scene} 590 * @type String 591 */ 592 enchant.Event.CHILD_ADDED = 'childadded'; 593 594 /** 595 * NodeがGroupに追加されたとき発生するイベント. 596 * 発行するオブジェクト: {@link enchant.Node} 597 * @type String 598 */ 599 enchant.Event.ADDED = 'added'; 600 601 /** 602 * NodeがSceneに追加されたとき発生するイベント. 603 * 発行するオブジェクト: {@link enchant.Node} 604 * @type String 605 */ 606 enchant.Event.ADDED_TO_SCENE = 'addedtoscene'; 607 608 /** 609 * Nodeから子が削除されたとき発生するイベント. 610 * 発行するオブジェクト: {@link enchant.Group}, {@link enchant.Scene} 611 * @type String 612 * @type String 613 */ 614 enchant.Event.CHILD_REMOVED = 'childremoved'; 615 616 /** 617 * NodeがGroupから削除されたとき発生するイベント. 618 * 発行するオブジェクト: {@link enchant.Node} 619 * @type String 620 */ 621 enchant.Event.REMOVED = 'removed'; 622 623 /** 624 * NodeがSceneから削除されたとき発生するイベント. 625 * 発行するオブジェクト: {@link enchant.Node} 626 * @type String 627 */ 628 enchant.Event.REMOVED_FROM_SCENE = 'removedfromscene'; 629 630 /** 631 * Nodeに対するタッチが始まったとき発生するイベント. 632 * クリックもタッチとして扱われる. 発行するオブジェクト: {@link enchant.Node} 633 * @type String 634 */ 635 enchant.Event.TOUCH_START = 'touchstart'; 636 637 /** 638 * Nodeに対するタッチが移動したとき発生するイベント. 639 * クリックもタッチとして扱われる. 発行するオブジェクト: {@link enchant.Node} 640 * @type String 641 */ 642 enchant.Event.TOUCH_MOVE = 'touchmove'; 643 644 /** 645 * Nodeに対するタッチが終了したとき発生するイベント. 646 * クリックもタッチとして扱われる. 発行するオブジェクト: {@link enchant.Node} 647 * @type String 648 */ 649 enchant.Event.TOUCH_END = 'touchend'; 650 651 /** 652 * Entityがレンダリングされるときに発生するイベント. 653 * 発行するオブジェクト: {@link enchant.Entity} 654 * @type String 655 */ 656 enchant.Event.RENDER = 'render'; 657 658 /** 659 * ボタン入力が始まったとき発生するイベント. 660 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 661 * @type String 662 */ 663 enchant.Event.INPUT_START = 'inputstart'; 664 665 /** 666 * ボタン入力が変化したとき発生するイベント. 667 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 668 * @type String 669 */ 670 enchant.Event.INPUT_CHANGE = 'inputchange'; 671 672 /** 673 * ボタン入力が終了したとき発生するイベント. 674 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 675 * @type String 676 */ 677 enchant.Event.INPUT_END = 'inputend'; 678 679 /** 680 * 入力が変化したとき発生するイベント. 681 * ボタン入力が変化したとき発生する内部的なイベント. 682 * 発行するオブジェクト: {@link enchant.InputSource} 683 * @type String 684 */ 685 enchant.Event.INPUT_STATE_CHANGED = 'inputstatechanged'; 686 687 /** 688 * leftボタンが押された発生するイベント. 689 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 690 * @type String 691 */ 692 enchant.Event.LEFT_BUTTON_DOWN = 'leftbuttondown'; 693 694 /** 695 * leftボタンが離された発生するイベント. 696 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 697 * @type String 698 */ 699 enchant.Event.LEFT_BUTTON_UP = 'leftbuttonup'; 700 701 /** 702 * rightボタンが押された発生するイベント. 703 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 704 * @type String 705 */ 706 enchant.Event.RIGHT_BUTTON_DOWN = 'rightbuttondown'; 707 708 /** 709 * rightボタンが離された発生するイベント. 710 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 711 * @type String 712 */ 713 enchant.Event.RIGHT_BUTTON_UP = 'rightbuttonup'; 714 715 /** 716 * upボタンが押された発生するイベント. 717 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 718 * @type String 719 */ 720 enchant.Event.UP_BUTTON_DOWN = 'upbuttondown'; 721 722 /** 723 * upボタンが離された発生するイベント. 724 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 725 * @type String 726 */ 727 enchant.Event.UP_BUTTON_UP = 'upbuttonup'; 728 729 /** 730 * downボタンが離された発生するイベント. 731 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 732 * @type String 733 */ 734 enchant.Event.DOWN_BUTTON_DOWN = 'downbuttondown'; 735 736 /** 737 * downボタンが離された発生するイベント. 738 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 739 * @type String 740 */ 741 enchant.Event.DOWN_BUTTON_UP = 'downbuttonup'; 742 743 /** 744 * aボタンが押された発生するイベント. 745 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 746 * @type String 747 */ 748 enchant.Event.A_BUTTON_DOWN = 'abuttondown'; 749 750 /** 751 * aボタンが離された発生するイベント. 752 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 753 * @type String 754 */ 755 enchant.Event.A_BUTTON_UP = 'abuttonup'; 756 757 /** 758 * bボタンが押された発生するイベント. 759 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 760 * @type String 761 */ 762 enchant.Event.B_BUTTON_DOWN = 'bbuttondown'; 763 764 /** 765 * bボタンが離された発生するイベント. 766 * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene} 767 * @type String 768 */ 769 enchant.Event.B_BUTTON_UP = 'bbuttonup'; 770 771 /** 772 * アクションがタイムラインに追加された時に発行されるイベント. 773 * looped が設定されている時も, アクションは一度タイムラインから削除されもう一度追加される. 774 * @type String 775 */ 776 enchant.Event.ADDED_TO_TIMELINE = "addedtotimeline"; 777 778 /** 779 * アクションがタイムラインから削除された時に発行されるイベント. 780 * looped が設定されている時も, アクションは一度タイムラインから削除されもう一度追加される. 781 * @type String 782 */ 783 enchant.Event.REMOVED_FROM_TIMELINE = "removedfromtimeline"; 784 785 /** 786 * アクションが開始された時に発行されるイベント. 787 * @type String 788 */ 789 enchant.Event.ACTION_START = "actionstart"; 790 791 /** 792 * アクションが終了するときに発行されるイベント. 793 * @type String 794 */ 795 enchant.Event.ACTION_END = "actionend"; 796 797 /** 798 * アクションが1フレーム経過するときに発行されるイベント. 799 * @type String 800 */ 801 enchant.Event.ACTION_TICK = "actiontick"; 802 803 /** 804 * アクションが追加された時に, タイムラインに対して発行されるイベント. 805 * @type String 806 */ 807 enchant.Event.ACTION_ADDED = "actionadded"; 808 809 /** 810 * アクションが削除された時に, タイムラインに対して発行されるイベント. 811 * @type String 812 */ 813 enchant.Event.ACTION_REMOVED = "actionremoved"; 814 815 /** 816 * フレームアニメーションが終了したときに発生するイベント. フレームの再生がnullに到達したことを意味する. 817 * @type String 818 */ 819 enchant.Event.ANIMATION_END = "animationend"; 820 821 /** 822 * @scope enchant.EventTarget.prototype 823 */ 824 enchant.EventTarget = enchant.Class.create({ 825 /** 826 * @name enchant.EventTarget 827 * @class 828 * DOM Event風味の独自イベント実装を行ったクラス. 829 * ただしフェーズの概念はなし. 830 * @constructs 831 */ 832 initialize: function() { 833 this._listeners = {}; 834 }, 835 /** 836 * イベントリスナを追加する. 837 * @param {String} type イベントのタイプ. 838 * @param {Function(e:enchant.Event)} listener 追加するイベントリスナ. 839 */ 840 addEventListener: function(type, listener) { 841 var listeners = this._listeners[type]; 842 if (listeners == null) { 843 this._listeners[type] = [listener]; 844 } else if (listeners.indexOf(listener) === -1) { 845 listeners.unshift(listener); 846 847 } 848 }, 849 /** 850 * addEventListener と同じ. 851 * @param {String} type イベントのタイプ. 852 * @param {Function(e:enchant.Event)} listener 追加するイベントリスナ. 853 * @see enchant.EventTarget#addEventListener 854 */ 855 on: function() { 856 this.addEventListener.apply(this, arguments); 857 }, 858 /** 859 * イベントリスナを削除する. 860 * @param {String} type イベントのタイプ. 861 * @param {Function(e:enchant.Event)} listener 削除するイベントリスナ. 862 */ 863 removeEventListener: function(type, listener) { 864 var listeners = this._listeners[type]; 865 if (listeners != null) { 866 var i = listeners.indexOf(listener); 867 if (i !== -1) { 868 listeners.splice(i, 1); 869 } 870 } 871 }, 872 /** 873 * すべてのイベントリスナを削除する. 874 * @param {String} type イベントのタイプ. 875 */ 876 clearEventListener: function(type) { 877 if (type != null) { 878 delete this._listeners[type]; 879 } else { 880 this._listeners = {}; 881 } 882 }, 883 /** 884 * イベントを発行する. 885 * @param {enchant.Event} e 発行するイベント. 886 */ 887 dispatchEvent: function(e) { 888 e.target = this; 889 e.localX = e.x - this._offsetX; 890 e.localY = e.y - this._offsetY; 891 if (this['on' + e.type] != null){ 892 this['on' + e.type](e); 893 } 894 var listeners = this._listeners[e.type]; 895 if (listeners != null) { 896 listeners = listeners.slice(); 897 for (var i = 0, len = listeners.length; i < len; i++) { 898 listeners[i].call(this, e); 899 } 900 } 901 } 902 }); 903 904 (function() { 905 var core; 906 /** 907 * @scope enchant.Core.prototype 908 */ 909 enchant.Core = enchant.Class.create(enchant.EventTarget, { 910 /** 911 * @name enchant.Core 912 * @class 913 * アプリケーションのメインループ, シーンを管理するクラス. 914 * 915 * インスタンスは一つしか存在することができず, すでにインスタンスが存在する状態で 916 * コンストラクタを実行した場合既存のものが上書きされる. 存在するインスタンスには 917 * {@link enchant.Core.instance} からアクセスできる. 918 * 919 * @param {Number} [width=320] 画面の横幅. 920 * @param {Number} [height=320] 画面の高さ. 921 * @constructs 922 * @extends enchant.EventTarget 923 */ 924 initialize: function(width, height) { 925 if (window.document.body === null) { 926 // @TODO postpone initialization after window.onload 927 throw new Error("document.body is null. Please excute 'new Core()' in window.onload."); 928 } 929 930 enchant.EventTarget.call(this); 931 var initial = true; 932 if (core) { 933 initial = false; 934 core.stop(); 935 } 936 core = enchant.Core.instance = this; 937 938 this._calledTime = 0; 939 this._mousedownID = 0; 940 this._surfaceID = 0; 941 this._soundID = 0; 942 943 this._scenes = []; 944 945 width = width || 320; 946 height = height || 320; 947 948 var stage = document.getElementById('enchant-stage'); 949 var scale, sWidth, sHeight; 950 if (!stage) { 951 stage = document.createElement('div'); 952 stage.id = 'enchant-stage'; 953 stage.style.position = 'absolute'; 954 955 if (document.body.firstChild) { 956 document.body.insertBefore(stage, document.body.firstChild); 957 } else { 958 document.body.appendChild(stage); 959 } 960 scale = Math.min( 961 window.innerWidth / width, 962 window.innerHeight / height 963 ); 964 this._pageX = stage.getBoundingClientRect().left; 965 this._pageY = stage.getBoundingClientRect().top; 966 } else { 967 var style = window.getComputedStyle(stage); 968 sWidth = parseInt(style.width, 10); 969 sHeight = parseInt(style.height, 10); 970 if (sWidth && sHeight) { 971 scale = Math.min( 972 sWidth / width, 973 sHeight / height 974 ); 975 } else { 976 scale = 1; 977 } 978 while (stage.firstChild) { 979 stage.removeChild(stage.firstChild); 980 } 981 stage.style.position = 'relative'; 982 983 var bounding = stage.getBoundingClientRect(); 984 this._pageX = Math.round(window.scrollX || window.pageXOffset + bounding.left); 985 this._pageY = Math.round(window.scrollY || window.pageYOffset + bounding.top); 986 } 987 stage.style.fontSize = '12px'; 988 stage.style.webkitTextSizeAdjust = 'none'; 989 stage.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)'; 990 this._element = stage; 991 992 this.addEventListener('coreresize', this._oncoreresize); 993 994 this._width = width; 995 this._height = height; 996 this.scale = scale; 997 998 /** 999 * フレームレート. 1000 * @type Number 1001 */ 1002 this.fps = 30; 1003 /** 1004 * アプリの開始からのフレーム数. 1005 * @type Number 1006 */ 1007 this.frame = 0; 1008 /** 1009 * アプリが実行可能な状態かどうか. 1010 * @type Boolean 1011 */ 1012 this.ready = false; 1013 /** 1014 * アプリが実行状態かどうか. 1015 * @type Boolean 1016 */ 1017 this.running = false; 1018 /** 1019 * ロードされた画像をパスをキーとして保存するオブジェクト. 1020 * @type Object 1021 */ 1022 this.assets = {}; 1023 var assets = this._assets = []; 1024 (function detectAssets(module) { 1025 if (module.assets) { 1026 enchant.Core.instance.preload(module.assets); 1027 } 1028 for (var prop in module) { 1029 if (module.hasOwnProperty(prop)) { 1030 if (typeof module[prop] === 'object' && module[prop] !== null && Object.getPrototypeOf(module[prop]) === Object.prototype) { 1031 detectAssets(module[prop]); 1032 } 1033 } 1034 } 1035 }(enchant)); 1036 1037 /** 1038 * 現在のScene. Sceneスタック中の一番上のScene. 1039 * @type enchant.Scene 1040 */ 1041 this.currentScene = null; 1042 /** 1043 * ルートScene. Sceneスタック中の一番下のScene. 1044 * @type enchant.Scene 1045 */ 1046 this.rootScene = new enchant.Scene(); 1047 this.pushScene(this.rootScene); 1048 /** 1049 * ローディング時に表示されるScene. 1050 * @type enchant.Scene 1051 */ 1052 this.loadingScene = new enchant.LoadingScene(); 1053 1054 /** 1055 * 一度でも {@link enchant.Core#start} が呼ばれたことがあるかどうか. 1056 [/lang:ja] 1057 * Indicates whether or not {@link enchant.Core#start} has been called. 1058 [/lang] 1059 * @type Boolean 1060 * @private 1061 */ 1062 this._activated = false; 1063 1064 this._offsetX = 0; 1065 this._offsetY = 0; 1066 1067 /** 1068 * アプリに対する入力状態を保存するオブジェクト. 1069 * @type Object 1070 */ 1071 this.input = {}; 1072 1073 this.keyboardInputManager = new enchant.KeyboardInputManager(window.document, this.input); 1074 this.keyboardInputManager.addBroadcastTarget(this); 1075 this._keybind = this.keyboardInputManager._binds; 1076 1077 if (!enchant.ENV.KEY_BIND_TABLE) { 1078 enchant.ENV.KEY_BIND_TABLE = {}; 1079 } 1080 1081 for (var prop in enchant.ENV.KEY_BIND_TABLE) { 1082 this.keybind(prop, enchant.ENV.KEY_BIND_TABLE[prop]); 1083 } 1084 1085 if (initial) { 1086 stage = enchant.Core.instance._element; 1087 var evt; 1088 document.addEventListener('keydown', function(e) { 1089 core.dispatchEvent(new enchant.Event('keydown')); 1090 if (enchant.ENV.PREVENT_DEFAULT_KEY_CODES.indexOf(e.keyCode) !== -1) { 1091 e.preventDefault(); 1092 e.stopPropagation(); 1093 } 1094 }, true); 1095 1096 if (enchant.ENV.TOUCH_ENABLED) { 1097 stage.addEventListener('touchstart', function(e) { 1098 var tagName = (e.target.tagName).toLowerCase(); 1099 if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) { 1100 e.preventDefault(); 1101 if (!core.running) { 1102 e.stopPropagation(); 1103 } 1104 } 1105 }, true); 1106 stage.addEventListener('touchmove', function(e) { 1107 var tagName = (e.target.tagName).toLowerCase(); 1108 if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) { 1109 e.preventDefault(); 1110 if (!core.running) { 1111 e.stopPropagation(); 1112 } 1113 } 1114 }, true); 1115 stage.addEventListener('touchend', function(e) { 1116 var tagName = (e.target.tagName).toLowerCase(); 1117 if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) { 1118 e.preventDefault(); 1119 if (!core.running) { 1120 e.stopPropagation(); 1121 } 1122 } 1123 }, true); 1124 } 1125 stage.addEventListener('mousedown', function(e) { 1126 var tagName = (e.target.tagName).toLowerCase(); 1127 if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) { 1128 e.preventDefault(); 1129 core._mousedownID++; 1130 if (!core.running) { 1131 e.stopPropagation(); 1132 } 1133 } 1134 }, true); 1135 stage.addEventListener('mousemove', function(e) { 1136 var tagName = (e.target.tagName).toLowerCase(); 1137 if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) { 1138 e.preventDefault(); 1139 if (!core.running) { 1140 e.stopPropagation(); 1141 } 1142 } 1143 }, true); 1144 stage.addEventListener('mouseup', function(e) { 1145 var tagName = (e.target.tagName).toLowerCase(); 1146 if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) { 1147 e.preventDefault(); 1148 if (!core.running) { 1149 e.stopPropagation(); 1150 } 1151 } 1152 }, true); 1153 core._touchEventTarget = {}; 1154 if (enchant.ENV.TOUCH_ENABLED) { 1155 stage.addEventListener('touchstart', function(e) { 1156 var core = enchant.Core.instance; 1157 var evt = new enchant.Event(enchant.Event.TOUCH_START); 1158 var touches = e.changedTouches; 1159 var touch, target; 1160 for (var i = 0, l = touches.length; i < l; i++) { 1161 touch = touches[i]; 1162 evt._initPosition(touch.pageX, touch.pageY); 1163 target = core.currentScene._determineEventTarget(evt); 1164 core._touchEventTarget[touch.identifier] = target; 1165 target.dispatchEvent(evt); 1166 } 1167 }, false); 1168 stage.addEventListener('touchmove', function(e) { 1169 var core = enchant.Core.instance; 1170 var evt = new enchant.Event(enchant.Event.TOUCH_MOVE); 1171 var touches = e.changedTouches; 1172 var touch, target; 1173 for (var i = 0, l = touches.length; i < l; i++) { 1174 touch = touches[i]; 1175 target = core._touchEventTarget[touch.identifier]; 1176 if (target) { 1177 evt._initPosition(touch.pageX, touch.pageY); 1178 target.dispatchEvent(evt); 1179 } 1180 } 1181 }, false); 1182 stage.addEventListener('touchend', function(e) { 1183 var core = enchant.Core.instance; 1184 var evt = new enchant.Event(enchant.Event.TOUCH_END); 1185 var touches = e.changedTouches; 1186 var touch, target; 1187 for (var i = 0, l = touches.length; i < l; i++) { 1188 touch = touches[i]; 1189 target = core._touchEventTarget[touch.identifier]; 1190 if (target) { 1191 evt._initPosition(touch.pageX, touch.pageY); 1192 target.dispatchEvent(evt); 1193 delete core._touchEventTarget[touch.identifier]; 1194 } 1195 } 1196 }, false); 1197 } 1198 stage.addEventListener('mousedown', function(e) { 1199 var core = enchant.Core.instance; 1200 var evt = new enchant.Event(enchant.Event.TOUCH_START); 1201 evt._initPosition(e.pageX, e.pageY); 1202 var target = core.currentScene._determineEventTarget(evt); 1203 core._touchEventTarget[core._mousedownID] = target; 1204 target.dispatchEvent(evt); 1205 }, false); 1206 stage.addEventListener('mousemove', function(e) { 1207 var core = enchant.Core.instance; 1208 var evt = new enchant.Event(enchant.Event.TOUCH_MOVE); 1209 evt._initPosition(e.pageX, e.pageY); 1210 var target = core._touchEventTarget[core._mousedownID]; 1211 if (target) { 1212 target.dispatchEvent(evt); 1213 } 1214 }, false); 1215 stage.addEventListener('mouseup', function(e) { 1216 var core = enchant.Core.instance; 1217 var evt = new enchant.Event(enchant.Event.TOUCH_END); 1218 evt._initPosition(e.pageX, e.pageY); 1219 var target = core._touchEventTarget[core._mousedownID]; 1220 if (target) { 1221 target.dispatchEvent(evt); 1222 } 1223 delete core._touchEventTarget[core._mousedownID]; 1224 }, false); 1225 } 1226 }, 1227 /** 1228 * 画面の横幅. 1229 * @type Number 1230 */ 1231 width: { 1232 get: function() { 1233 return this._width; 1234 }, 1235 set: function(w) { 1236 this._width = w; 1237 this._dispatchCoreResizeEvent(); 1238 } 1239 }, 1240 /** 1241 * 画面の高さ. 1242 * @type Number 1243 */ 1244 height: { 1245 get: function() { 1246 return this._height; 1247 }, 1248 set: function(h) { 1249 this._height = h; 1250 this._dispatchCoreResizeEvent(); 1251 } 1252 }, 1253 /** 1254 * 画面の表示倍率. 1255 * @type Number 1256 */ 1257 scale: { 1258 get: function() { 1259 return this._scale; 1260 }, 1261 set: function(s) { 1262 this._scale = s; 1263 this._dispatchCoreResizeEvent(); 1264 } 1265 }, 1266 _dispatchCoreResizeEvent: function() { 1267 var e = new enchant.Event('coreresize'); 1268 e.width = this._width; 1269 e.height = this._height; 1270 e.scale = this._scale; 1271 this.dispatchEvent(e); 1272 }, 1273 _oncoreresize: function(e) { 1274 this._element.style.width = Math.floor(this._width * this._scale) + 'px'; 1275 this._element.style.height = Math.floor(this._height * this._scale) + 'px'; 1276 var scene; 1277 for (var i = 0, l = this._scenes.length; i < l; i++) { 1278 scene = this._scenes[i]; 1279 scene.dispatchEvent(e); 1280 } 1281 }, 1282 /** 1283 * ファイルのプリロードを行う. 1284 * 1285 * プリロードを行うよう設定されたファイルは {@link enchant.Core#start} が実行されるとき 1286 * ロードが行われる. 全てのファイルのロードが完了したときはCoreオブジェクトからload 1287 * イベントが発行され, Coreオブジェクトのassetsプロパティから画像ファイルの場合は 1288 * Surfaceオブジェクトとして, 音声ファイルの場合はSoundオブジェクトとして, 1289 * その他の場合は文字列としてアクセスできるようになる. 1290 * 1291 * なおこのSurfaceオブジェクトは {@link enchant.Surface.load} を使って作成されたものである 1292 * ため直接画像操作を行うことはできない. enchant.Surface.loadの項を参照. 1293 * 1294 * @example 1295 * core.preload('player.gif'); 1296 * core.onload = function() { 1297 * var sprite = new Sprite(32, 32); 1298 * sprite.image = core.assets['player.gif']; // パス名でアクセス 1299 * ... 1300 * }; 1301 * core.start(); 1302 * 1303 * @param {...String|String[]} assets プリロードするファイルのパス. 複数指定できる. 1304 * @return {enchant.Core} this 1305 */ 1306 preload: function(assets) { 1307 var a; 1308 if (!(assets instanceof Array)) { 1309 if (typeof assets === 'object') { 1310 a = []; 1311 for (var name in assets) { 1312 if (assets.hasOwnProperty(name)) { 1313 a.push([ assets[name], name ]); 1314 } 1315 } 1316 assets = a; 1317 } else { 1318 assets = Array.prototype.slice.call(arguments); 1319 } 1320 } 1321 Array.prototype.push.apply(this._assets, assets); 1322 return this; 1323 }, 1324 /** 1325 * ファイルのロードを行う. 1326 * 1327 * @param {String} src ロードするファイルのパス. 1328 * @param {String} [alias] ロードするファイルに設定したい名前. 1329 * @param {Function} [callback] ファイルのロードが完了したときに呼び出される関数. 1330 * @param {Function} [onerror] ファイルのロードに失敗したときに呼び出される関数. 1331 * @return {enchant.Deferred} ファイル読み込み後に起動するDeferredオブジェクト. 1332 */ 1333 load: function(src, alias, callback, onerror) { 1334 var assetName; 1335 if (typeof arguments[1] === 'string') { 1336 assetName = alias; 1337 callback = callback || function() {}; 1338 onerror = onerror || function() {}; 1339 } else { 1340 assetName = src; 1341 var tempCallback = callback; 1342 callback = arguments[1] || function() {}; 1343 onerror = tempCallback || function() {}; 1344 } 1345 1346 var ext = enchant.Core.findExt(src); 1347 1348 return enchant.Deferred.next(function() { 1349 var d = new enchant.Deferred(); 1350 var _callback = function(e) { 1351 d.call(e); 1352 callback.call(this, e); 1353 }; 1354 var _onerror = function(e) { 1355 d.fail(e); 1356 onerror.call(this, e); 1357 }; 1358 if (enchant.Core._loadFuncs[ext]) { 1359 enchant.Core.instance.assets[assetName] = enchant.Core._loadFuncs[ext](src, ext, _callback, _onerror); 1360 } else { 1361 var req = new XMLHttpRequest(); 1362 req.open('GET', src, true); 1363 req.onreadystatechange = function() { 1364 if (req.readyState === 4) { 1365 if (req.status !== 200 && req.status !== 0) { 1366 // throw new Error(req.status + ': ' + 'Cannot load an asset: ' + src); 1367 var e = new enchant.Event('error'); 1368 e.message = req.status + ': ' + 'Cannot load an asset: ' + src; 1369 _onerror.call(enchant.Core.instance, e); 1370 } 1371 1372 var type = req.getResponseHeader('Content-Type') || ''; 1373 if (type.match(/^image/)) { 1374 core.assets[assetName] = enchant.Surface.load(src, _callback, _onerror); 1375 } else if (type.match(/^audio/)) { 1376 core.assets[assetName] = enchant.Sound.load(src, type, _callback, _onerror); 1377 } else { 1378 core.assets[assetName] = req.responseText; 1379 _callback.call(enchant.Core.instance, new enchant.Event('load')); 1380 } 1381 } 1382 }; 1383 req.send(null); 1384 } 1385 return d; 1386 }); 1387 }, 1388 /** 1389 * アプリを起動する. 1390 * 1391 * enchant.Core#fpsで設定されたフレームレートに従って {@link enchant.Core#currentScene} の 1392 * フレームの更新が行われるようになる. プリロードする画像が存在する場合はロードが 1393 * 始まりローディング画面が表示される. 1394 * @return {enchant.Deferred} ローディング画面終了後に起動するDeferredオブジェクト. 1395 */ 1396 start: function(deferred) { 1397 var onloadTimeSetter = function() { 1398 this.frame = 0; 1399 this.removeEventListener('load', onloadTimeSetter); 1400 }; 1401 this.addEventListener('load', onloadTimeSetter); 1402 1403 this.currentTime = window.getTime(); 1404 this.running = true; 1405 this.ready = true; 1406 1407 if (!this._activated) { 1408 this._activated = true; 1409 if (enchant.ENV.BROWSER === 'mobilesafari' && 1410 enchant.ENV.USE_WEBAUDIO && 1411 enchant.ENV.USE_TOUCH_TO_START_SCENE) { 1412 var d = new enchant.Deferred(); 1413 var scene = this._createTouchToStartScene(); 1414 scene.addEventListener(enchant.Event.TOUCH_START, function waitTouch() { 1415 this.removeEventListener(enchant.Event.TOUCH_START, waitTouch); 1416 var a = new enchant.WebAudioSound(); 1417 a.buffer = enchant.WebAudioSound.audioContext.createBuffer(1, 1, 48000); 1418 a.play(); 1419 core.removeScene(scene); 1420 core.start(d); 1421 }, false); 1422 core.pushScene(scene); 1423 return d; 1424 } 1425 } 1426 1427 this._requestNextFrame(0); 1428 1429 var ret = this._requestPreload() 1430 .next(function() { 1431 enchant.Core.instance.loadingScene.dispatchEvent(new enchant.Event(enchant.Event.LOAD)); 1432 }); 1433 1434 if (deferred) { 1435 ret.next(function(arg) { 1436 deferred.call(arg); 1437 }) 1438 .error(function(arg) { 1439 deferred.fail(arg); 1440 }); 1441 } 1442 1443 return ret; 1444 }, 1445 _requestPreload: function() { 1446 var o = {}; 1447 var loaded = 0, 1448 len = 0, 1449 loadFunc = function() { 1450 var e = new enchant.Event('progress'); 1451 e.loaded = ++loaded; 1452 e.total = len; 1453 core.loadingScene.dispatchEvent(e); 1454 }; 1455 this._assets 1456 .reverse() 1457 .forEach(function(asset) { 1458 var src, name; 1459 if (asset instanceof Array) { 1460 src = asset[0]; 1461 name = asset[1]; 1462 } else { 1463 src = name = asset; 1464 } 1465 if (!o[name]) { 1466 o[name] = this.load(src, name, loadFunc); 1467 len++; 1468 } 1469 }, this); 1470 1471 this.pushScene(this.loadingScene); 1472 return enchant.Deferred.parallel(o); 1473 }, 1474 _createTouchToStartScene: function() { 1475 var label = new enchant.Label('Touch to Start'), 1476 size = Math.round(core.width / 10), 1477 scene = new enchant.Scene(); 1478 1479 label.color = '#fff'; 1480 label.font = (size - 1) + 'px bold Helvetica,Arial,sans-serif'; 1481 label.textAlign = 'center'; 1482 label.width = core.width; 1483 label.height = label._boundHeight; 1484 label.y = (core.height - label.height) / 2; 1485 1486 scene.backgroundColor = '#000'; 1487 scene.addChild(label); 1488 1489 return scene; 1490 }, 1491 /** 1492 * アプリをデバッグモードで開始する. 1493 * 1494 * {@link enchant.Core#_debug} フラグを true にすることでもデバッグモードをオンにすることができる 1495 * @return {enchant.Deferred} ローディング画面終了後に起動するDeferredオブジェクト. 1496 */ 1497 debug: function() { 1498 this._debug = true; 1499 return this.start(); 1500 }, 1501 actualFps: { 1502 get: function() { 1503 return this._actualFps || this.fps; 1504 } 1505 }, 1506 /** 1507 * 次のフレームの実行を要求する. 1508 * @param {Number} delay requestAnimationFrameを呼び出すまでの遅延時間. 1509 * @private 1510 */ 1511 _requestNextFrame: function(delay) { 1512 if (!this.ready) { 1513 return; 1514 } 1515 if (this.fps >= 60 || delay <= 16) { 1516 this._calledTime = window.getTime(); 1517 window.requestAnimationFrame(this._callTick); 1518 } else { 1519 setTimeout(function() { 1520 var core = enchant.Core.instance; 1521 core._calledTime = window.getTime(); 1522 window.requestAnimationFrame(core._callTick); 1523 }, Math.max(0, delay)); 1524 } 1525 }, 1526 /** 1527 * Core#_tickを呼び出す. 1528 * @param {Number} time 呼び出し時の時間. 1529 * @private 1530 */ 1531 _callTick: function(time) { 1532 enchant.Core.instance._tick(time); 1533 }, 1534 _tick: function(time) { 1535 var e = new enchant.Event('enterframe'); 1536 var now = window.getTime(); 1537 var elapsed = e.elapsed = now - this.currentTime; 1538 this.currentTime = now; 1539 1540 this._actualFps = elapsed > 0 ? (1000 / elapsed) : 0; 1541 1542 var nodes = this.currentScene.childNodes.slice(); 1543 var push = Array.prototype.push; 1544 while (nodes.length) { 1545 var node = nodes.pop(); 1546 node.age++; 1547 node.dispatchEvent(e); 1548 if (node.childNodes) { 1549 push.apply(nodes, node.childNodes); 1550 } 1551 } 1552 1553 this.currentScene.age++; 1554 this.currentScene.dispatchEvent(e); 1555 this.dispatchEvent(e); 1556 1557 this.dispatchEvent(new enchant.Event('exitframe')); 1558 this.frame++; 1559 now = window.getTime(); 1560 1561 this._requestNextFrame(1000 / this.fps - (now - this._calledTime)); 1562 }, 1563 getTime: function() { 1564 return window.getTime(); 1565 }, 1566 /** 1567 * アプリを停止する. 1568 * 1569 * フレームは更新されず, ユーザの入力も受け付けなくなる. 1570 * {@link enchant.Core#resume} で再開できる. 1571 */ 1572 stop: function() { 1573 this.ready = false; 1574 this.running = false; 1575 }, 1576 /** 1577 * アプリを一時停止する. 1578 * 1579 * フレームは更新されず, ユーザの入力は受け付ける. 1580 * {@link enchant.Core#resume} で再開できる. 1581 */ 1582 pause: function() { 1583 this.ready = false; 1584 }, 1585 /** 1586 * アプリを再開する. 1587 */ 1588 resume: function() { 1589 if (this.ready) { 1590 return; 1591 } 1592 this.currentTime = window.getTime(); 1593 this.ready = true; 1594 this.running = true; 1595 this._requestNextFrame(0); 1596 }, 1597 1598 /** 1599 * 新しいSceneに移行する. 1600 * 1601 * Sceneはスタック状に管理されており, 表示順序もスタックに積み上げられた順に従う. 1602 * enchant.Core#pushSceneを行うとSceneをスタックの一番上に積むことができる. スタックの 1603 * 一番上のSceneに対してはフレームの更新が行われる. 1604 * 1605 * @param {enchant.Scene} scene 移行する新しいScene. 1606 * @return {enchant.Scene} 新しいScene. 1607 */ 1608 pushScene: function(scene) { 1609 this._element.appendChild(scene._element); 1610 if (this.currentScene) { 1611 this.currentScene.dispatchEvent(new enchant.Event('exit')); 1612 } 1613 this.currentScene = scene; 1614 this.currentScene.dispatchEvent(new enchant.Event('enter')); 1615 return this._scenes.push(scene); 1616 }, 1617 /** 1618 * 現在のSceneを終了させ前のSceneに戻る. 1619 * 1620 * Sceneはスタック状に管理されており, 表示順序もスタックに積み上げられた順に従う. 1621 * enchant.Core#popSceneを行うとスタックの一番上のSceneを取り出すことができる. 1622 * 1623 * @return {enchant.Scene} 終了させたScene. 1624 */ 1625 popScene: function() { 1626 if (this.currentScene === this.rootScene) { 1627 return this.currentScene; 1628 } 1629 this._element.removeChild(this.currentScene._element); 1630 this.currentScene.dispatchEvent(new enchant.Event('exit')); 1631 this.currentScene = this._scenes[this._scenes.length - 2]; 1632 this.currentScene.dispatchEvent(new enchant.Event('enter')); 1633 return this._scenes.pop(); 1634 }, 1635 /** 1636 * 現在のSceneを別のSceneにおきかえる. 1637 * 1638 * {@link enchant.Core#popScene}, {@link enchant.Core#pushScene}を同時に行う. 1639 * 1640 * @param {enchant.Scene} scene おきかえるScene. 1641 * @return {enchant.Scene} 新しいScene. 1642 */ 1643 replaceScene: function(scene) { 1644 this.popScene(); 1645 return this.pushScene(scene); 1646 }, 1647 /** 1648 * Sceneを削除する. 1649 * 1650 * Sceneスタック中からSceneを削除する. 1651 * 1652 * @param {enchant.Scene} scene 削除するScene. 1653 * @return {enchant.Scene} 削除したScene. 1654 */ 1655 removeScene: function(scene) { 1656 if (this.currentScene === scene) { 1657 return this.popScene(); 1658 } else { 1659 var i = this._scenes.indexOf(scene); 1660 if (i !== -1) { 1661 this._scenes.splice(i, 1); 1662 this._element.removeChild(scene._element); 1663 return scene; 1664 } else { 1665 return null; 1666 } 1667 } 1668 }, 1669 _buttonListener: function(e) { 1670 this.currentScene.dispatchEvent(e); 1671 }, 1672 /** 1673 * キーバインドを設定する. 1674 * 1675 * @param {Number} key キーバインドを設定するキーコード. 1676 * @param {String} button 割り当てるボタン. 1677 * @return {enchant.Core} this 1678 */ 1679 keybind: function(key, button) { 1680 this.keyboardInputManager.keybind(key, button); 1681 this.addEventListener(button + 'buttondown', this._buttonListener); 1682 this.addEventListener(button + 'buttonup', this._buttonListener); 1683 return this; 1684 }, 1685 /** 1686 * キーバインドを削除する. 1687 * @param {Number} key 削除するキーコード. 1688 * @return {enchant.Core} this 1689 */ 1690 keyunbind: function(key) { 1691 var button = this._keybind[key]; 1692 this.keyboardInputManager.keyunbind(key); 1693 this.removeEventListener(button + 'buttondown', this._buttonListener); 1694 this.removeEventListener(button + 'buttonup', this._buttonListener); 1695 return this; 1696 }, 1697 changeButtonState: function(button, bool) { 1698 this.keyboardInputManager.changeState(button, bool); 1699 }, 1700 /** 1701 * Core#startが呼ばれてから経過した時間を取得する. 1702 * @return {Number} 経過した時間 (秒) 1703 */ 1704 getElapsedTime: function() { 1705 return this.frame / this.fps; 1706 } 1707 }); 1708 1709 /** 1710 * 拡張子に対応したアセットのロード関数. 1711 * ロード関数はファイルのパス, 拡張子, コールバックを引数に取り, 1712 * 対応したクラスのインスタンスを返す必要がある. 1713 * コールバックはEvent.LOADとEvent.ERRORでハンドルする. 1714 * @static 1715 * @private 1716 * @type Object 1717 */ 1718 enchant.Core._loadFuncs = {}; 1719 enchant.Core._loadFuncs['jpg'] = 1720 enchant.Core._loadFuncs['jpeg'] = 1721 enchant.Core._loadFuncs['gif'] = 1722 enchant.Core._loadFuncs['png'] = 1723 enchant.Core._loadFuncs['bmp'] = function(src, ext, callback, onerror) { 1724 return enchant.Surface.load(src, callback, onerror); 1725 }; 1726 enchant.Core._loadFuncs['mp3'] = 1727 enchant.Core._loadFuncs['aac'] = 1728 enchant.Core._loadFuncs['m4a'] = 1729 enchant.Core._loadFuncs['wav'] = 1730 enchant.Core._loadFuncs['ogg'] = function(src, ext, callback, onerror) { 1731 return enchant.Sound.load(src, 'audio/' + ext, callback, onerror); 1732 }; 1733 1734 /** 1735 * ファイルパスを取り, 拡張子を返す. 1736 * @param {String} path ファイルパス. 1737 * @return {*} 1738 */ 1739 enchant.Core.findExt = function(path) { 1740 var matched = path.match(/\.\w+$/); 1741 if (matched && matched.length > 0) { 1742 return matched[0].slice(1).toLowerCase(); 1743 } 1744 1745 // for data URI 1746 if (path.indexOf('data:') === 0) { 1747 return path.split(/[\/;]/)[1].toLowerCase(); 1748 } 1749 return null; 1750 }; 1751 1752 /** 1753 * 現在のCoreインスタンス. 1754 * @type enchant.Core 1755 * @static 1756 */ 1757 enchant.Core.instance = null; 1758 }()); 1759 1760 /** 1761 * @name enchant.Game 1762 * @class 1763 * enchant.Game is moved to {@link enchant.Core} from v0.6 1764 * @deprecated 1765 */ 1766 enchant.Game = enchant.Core; 1767 1768 /** 1769 * @scope enchant.InputManager.prototype 1770 */ 1771 enchant.InputManager = enchant.Class.create(enchant.EventTarget, { 1772 /** 1773 * @name enchant.InputManager 1774 * @class 1775 * 入力を管理するためのクラス. 1776 * @param {*} valueStore 入力の状態を保持させるオブジェクト. 1777 * @param {*} [source=this] イベントに付加される入力のソース. 1778 * @constructs 1779 * @extends enchant.EventTarget 1780 */ 1781 initialize: function(valueStore, source) { 1782 enchant.EventTarget.call(this); 1783 1784 /** 1785 * 入力の変化を通知する対象を保持する配列. 1786 * @type enchant.EventTarget[] 1787 */ 1788 this.broadcastTarget = []; 1789 /** 1790 * 入力の状態を保持する連想配列. 1791 * @type Object 1792 */ 1793 this.valueStore = valueStore; 1794 /** 1795 * イベントに付加される入力のソース. 1796 * @type Object 1797 */ 1798 this.source = source || this; 1799 1800 this._binds = {}; 1801 1802 this._stateHandler = function(e) { 1803 var id = e.source.identifier; 1804 var name = this._binds[id]; 1805 this.changeState(name, e.data); 1806 }.bind(this); 1807 }, 1808 /** 1809 * 特定の入力に名前をつける. 1810 * 入力はフラグとイベントで監視できるようになる. 1811 * @param {enchant.InputSource} inputSource {@link enchant.InputSource} のインスタンス. 1812 * @param {String} name 入力につける名前. 1813 */ 1814 bind: function(inputSource, name) { 1815 inputSource.addEventListener(enchant.Event.INPUT_STATE_CHANGED, this._stateHandler); 1816 this._binds[inputSource.identifier] = name; 1817 }, 1818 /** 1819 * 入力のバインドを解除する. 1820 * @param {enchant.InputSource} inputSource {@link enchant.InputSource} のインスタンス. 1821 */ 1822 unbind: function(inputSource) { 1823 inputSource.removeEventListener(enchant.Event.INPUT_STATE_CHANGED, this._stateHandler); 1824 delete this._binds[inputSource.identifier]; 1825 }, 1826 /** 1827 * 入力の変化を通知する対象を追加する. 1828 * @param {enchant.EventTarget} eventTarget イベントの通知を設定したい対象. 1829 */ 1830 addBroadcastTarget: function(eventTarget) { 1831 var i = this.broadcastTarget.indexOf(eventTarget); 1832 if (i === -1) { 1833 this.broadcastTarget.push(eventTarget); 1834 } 1835 }, 1836 /** 1837 * 入力の変化を通知する対象を削除する. 1838 * @param {enchant.EventTarget} eventTarget イベントの通知を削除したい対象. 1839 */ 1840 removeBroadcastTarget: function(eventTarget) { 1841 var i = this.broadcastTarget.indexOf(eventTarget); 1842 if (i !== -1) { 1843 this.broadcastTarget.splice(i, 1); 1844 } 1845 }, 1846 /** 1847 * イベントを {@link enchant.InputManager#broadcastTarget} に発行する. 1848 * @param {enchant.Event} e イベント. 1849 */ 1850 broadcastEvent: function(e) { 1851 var target = this.broadcastTarget; 1852 for (var i = 0, l = target.length; i < l; i++) { 1853 target[i].dispatchEvent(e); 1854 } 1855 }, 1856 /** 1857 * 入力の状態を変更する. 1858 * @param {String} name 入力の名前. 1859 * @param {*} data 入力の状態. 1860 */ 1861 changeState: function(name, data) { 1862 } 1863 }); 1864 1865 /** 1866 * @scope enchant.InputSource.prototype 1867 */ 1868 enchant.InputSource = enchant.Class.create(enchant.EventTarget, { 1869 /** 1870 * @name enchant.InputSource 1871 * @class 1872 * 任意の入力をラップするクラス. 1873 * @param {String} identifier 入力のid. 1874 * @constructs 1875 * @extends enchant.EventTarget 1876 */ 1877 initialize: function(identifier) { 1878 enchant.EventTarget.call(this); 1879 /** 1880 * 入力のid. 1881 * @type String 1882 */ 1883 this.identifier = identifier; 1884 }, 1885 /** 1886 * 入力の状態変更をイベントで通知する. 1887 * @param {*} data 新しい状態. 1888 */ 1889 notifyStateChange: function(data) { 1890 var e = new enchant.Event(enchant.Event.INPUT_STATE_CHANGED); 1891 e.data = data; 1892 e.source = this; 1893 this.dispatchEvent(e); 1894 } 1895 }); 1896 1897 /** 1898 * @scope enchant.BinaryInputManager.prototype 1899 */ 1900 enchant.BinaryInputManager = enchant.Class.create(enchant.InputManager, { 1901 /** 1902 * @name enchant.BinaryInputManager 1903 * @class 1904 * 入力を管理するためのクラス. 1905 * @param {*} flagStore 入力のフラグを保持させるオブジェクト. 1906 * @param {String} activeEventNameSuffix イベント名の接尾辞. 1907 * @param {String} inactiveEventNameSuffix イベント名の接尾辞. 1908 * @param {*} [source=this] イベントに付加される入力のソース. 1909 * @constructs 1910 * @extends enchant.InputManager 1911 */ 1912 initialize: function(flagStore, activeEventNameSuffix, inactiveEventNameSuffix, source) { 1913 enchant.InputManager.call(this, flagStore, source); 1914 /** 1915 * アクティブな入力の数. 1916 * @type Number 1917 */ 1918 this.activeInputsNum = 0; 1919 /** 1920 * BinaryInputManagerが発行するイベント名の接尾辞. 1921 * @type String 1922 */ 1923 this.activeEventNameSuffix = activeEventNameSuffix; 1924 /** 1925 * BinaryInputManagerが発行するイベント名の接尾辞. 1926 * @type String 1927 */ 1928 this.inactiveEventNameSuffix = inactiveEventNameSuffix; 1929 }, 1930 /** 1931 * 特定の入力に名前をつける. 1932 * @param {enchant.BinaryInputSource} inputSource {@link enchant.InputSource}のインスタンス. 1933 * @param {String} name 入力につける名前. 1934 * @see enchant.InputManager#bind 1935 */ 1936 bind: function(binaryInputSource, name) { 1937 enchant.InputManager.prototype.bind.call(this, binaryInputSource, name); 1938 this.valueStore[name] = false; 1939 }, 1940 /** 1941 * 入力のバインドを解除する. 1942 * @param {enchant.BinaryInputSource} inputSource {@link enchant.InputSource}のインスタンス. 1943 * @see enchant.InputManager#unbind 1944 */ 1945 unbind: function(binaryInputSource) { 1946 var name = this._binds[binaryInputSource.identifier]; 1947 enchant.InputManager.prototype.unbind.call(this, binaryInputSource); 1948 delete this.valueStore[name]; 1949 }, 1950 /** 1951 * 入力の状態を変更する. 1952 * @param {String} name 入力の名前. 1953 * @param {Boolean} bool 入力の状態. 1954 */ 1955 changeState: function(name, bool) { 1956 if (bool) { 1957 this._down(name); 1958 } else { 1959 this._up(name); 1960 } 1961 }, 1962 _down: function(name) { 1963 var inputEvent; 1964 if (!this.valueStore[name]) { 1965 this.valueStore[name] = true; 1966 inputEvent = new enchant.Event((this.activeInputsNum++) ? 'inputchange' : 'inputstart'); 1967 inputEvent.source = this.source; 1968 this.broadcastEvent(inputEvent); 1969 } 1970 var downEvent = new enchant.Event(name + this.activeEventNameSuffix); 1971 downEvent.source = this.source; 1972 this.broadcastEvent(downEvent); 1973 }, 1974 _up: function(name) { 1975 var inputEvent; 1976 if (this.valueStore[name]) { 1977 this.valueStore[name] = false; 1978 inputEvent = new enchant.Event((--this.activeInputsNum) ? 'inputchange' : 'inputend'); 1979 inputEvent.source = this.source; 1980 this.broadcastEvent(inputEvent); 1981 } 1982 var upEvent = new enchant.Event(name + this.inactiveEventNameSuffix); 1983 upEvent.source = this.source; 1984 this.broadcastEvent(upEvent); 1985 } 1986 }); 1987 1988 /** 1989 * @scope enchant.BinaryInputSource.prototype 1990 */ 1991 enchant.BinaryInputSource = enchant.Class.create(enchant.InputSource, { 1992 /** 1993 * @name enchant.BinaryInputSource 1994 * @class 1995 * 任意の2値入力をラップするクラス. 1996 * @param {String} identifier 入力のid. 1997 * @constructs 1998 * @extends enchant.InputSource 1999 */ 2000 initialize: function(identifier) { 2001 enchant.InputSource.call(this, identifier); 2002 } 2003 }); 2004 2005 /** 2006 * @scope enchant.KeyboardInputManager.prototype 2007 */ 2008 enchant.KeyboardInputManager = enchant.Class.create(enchant.BinaryInputManager, { 2009 /** 2010 * @name enchant.KeyboardInputManager 2011 * @class 2012 * キーボード入力を管理するためのクラス. 2013 * @param {HTMLElement} dom element that will be watched. 2014 * @param {*} flagStore object that store input flag. 2015 * @constructs 2016 * @extends enchant.BinaryInputManager 2017 */ 2018 initialize: function(domElement, flagStore) { 2019 enchant.BinaryInputManager.call(this, flagStore, 'buttondown', 'buttonup'); 2020 this._attachDOMEvent(domElement, 'keydown', true); 2021 this._attachDOMEvent(domElement, 'keyup', false); 2022 }, 2023 /** 2024 * キーコードに対応したBinaryInputSourceを使って{@link enchant.BinaryInputManager#bind} を呼び出す. 2025 * @param {Number} keyCode キーコード. 2026 * @param {String} name 入力の名前. 2027 */ 2028 keybind: function(keyCode, name) { 2029 this.bind(enchant.KeyboardInputSource.getByKeyCode('' + keyCode), name); 2030 }, 2031 /** 2032 * キーコードに対応したBinaryInputSourceを使って{@link enchant.BinaryInputManager#unbind} を呼び出す. 2033 * @param {Number} keyCode キーコード. 2034 */ 2035 keyunbind: function(keyCode) { 2036 this.unbind(enchant.KeyboardInputSource.getByKeyCode('' + keyCode)); 2037 }, 2038 _attachDOMEvent: function(domElement, eventType, state) { 2039 domElement.addEventListener(eventType, function(e) { 2040 var core = enchant.Core.instance; 2041 if (!core || !core.running) { 2042 return; 2043 } 2044 var code = e.keyCode; 2045 var source = enchant.KeyboardInputSource._instances[code]; 2046 if (source) { 2047 source.notifyStateChange(state); 2048 } 2049 }, true); 2050 } 2051 }); 2052 2053 /** 2054 * @scope enchant.KeyboardInputSource.prototype 2055 */ 2056 enchant.KeyboardInputSource = enchant.Class.create(enchant.BinaryInputSource, { 2057 /** 2058 * @name enchant.KeyboardInputSource 2059 * @class 2060 * キーボード入力をラップするBinaryInputSource. 2061 * キーコードをidとして持つ. 2062 * @param {String} keyCode キーコード. 2063 * @constructs 2064 * @extends enchant.BinaryInputSource 2065 */ 2066 initialize: function(keyCode) { 2067 enchant.BinaryInputSource.call(this, keyCode); 2068 } 2069 }); 2070 /** 2071 * @private 2072 */ 2073 enchant.KeyboardInputSource._instances = {}; 2074 /** 2075 * @static 2076 * キーコードに対応したインスタンスを取得する. 2077 * @param {Number} keyCode キーコード. 2078 * @return {enchant.KeyboardInputSource} instance. 2079 */ 2080 enchant.KeyboardInputSource.getByKeyCode = function(keyCode) { 2081 if (!this._instances[keyCode]) { 2082 this._instances[keyCode] = new enchant.KeyboardInputSource(keyCode); 2083 } 2084 return this._instances[keyCode]; 2085 }; 2086 2087 /** 2088 * @scope enchant.Node.prototype 2089 */ 2090 enchant.Node = enchant.Class.create(enchant.EventTarget, { 2091 /** 2092 * @name enchant.Node 2093 * @class 2094 * Sceneをルートとした表示オブジェクトツリーに属するオブジェクトの基底クラス. 2095 * 直接使用することはない. 2096 * @constructs 2097 * @extends enchant.EventTarget 2098 */ 2099 initialize: function() { 2100 enchant.EventTarget.call(this); 2101 2102 this._dirty = false; 2103 2104 this._matrix = [ 1, 0, 0, 1, 0, 0 ]; 2105 2106 this._x = 0; 2107 this._y = 0; 2108 this._offsetX = 0; 2109 this._offsetY = 0; 2110 2111 /** 2112 * Nodeが画面に表示されてから経過したフレーム数. 2113 * {@link enchant.Event.ENTER_FRAME} イベントを受け取る前にインクリメントされる. 2114 * (ENTER_FRAME イベントのリスナが初めて実行される時に 1 となる.) 2115 * @type Number 2116 */ 2117 this.age = 0; 2118 2119 /** 2120 * Nodeの親Node. 2121 * @type enchant.Group 2122 */ 2123 this.parentNode = null; 2124 /** 2125 * Nodeが属しているScene. 2126 * @type enchant.Scene 2127 */ 2128 this.scene = null; 2129 2130 this.addEventListener('touchstart', function(e) { 2131 if (this.parentNode) { 2132 this.parentNode.dispatchEvent(e); 2133 } 2134 }); 2135 this.addEventListener('touchmove', function(e) { 2136 if (this.parentNode) { 2137 this.parentNode.dispatchEvent(e); 2138 } 2139 }); 2140 this.addEventListener('touchend', function(e) { 2141 if (this.parentNode) { 2142 this.parentNode.dispatchEvent(e); 2143 } 2144 }); 2145 2146 // Nodeが生成される際に, tl プロパティに Timeline オブジェクトを追加している. 2147 if (enchant.ENV.USE_ANIMATION) { 2148 this.tl = new enchant.Timeline(this); 2149 } 2150 }, 2151 /** 2152 * Nodeを移動する. 2153 * @param {Number} x 移動先のx座標. 2154 * @param {Number} y 移動先のy座標. 2155 */ 2156 moveTo: function(x, y) { 2157 this.x = x; 2158 this.y = y; 2159 }, 2160 /** 2161 * Nodeを移動する. 2162 * @param {Number} x 移動するx軸方向の距離. 2163 * @param {Number} y 移動するy軸方向の距離. 2164 */ 2165 moveBy: function(x, y) { 2166 this.x += x; 2167 this.y += y; 2168 }, 2169 /** 2170 * Nodeのx座標. 2171 * @type Number 2172 */ 2173 x: { 2174 get: function() { 2175 return this._x; 2176 }, 2177 set: function(x) { 2178 if(this._x !== x) { 2179 this._x = x; 2180 this._dirty = true; 2181 } 2182 } 2183 }, 2184 /** 2185 * Nodeのy座標. 2186 * @type Number 2187 */ 2188 y: { 2189 get: function() { 2190 return this._y; 2191 }, 2192 set: function(y) { 2193 if(this._y !== y) { 2194 this._y = y; 2195 this._dirty = true; 2196 } 2197 } 2198 }, 2199 _updateCoordinate: function() { 2200 var node = this; 2201 var tree = [ node ]; 2202 var parent = node.parentNode; 2203 var scene = this.scene; 2204 while (parent && node._dirty) { 2205 tree.unshift(parent); 2206 node = node.parentNode; 2207 parent = node.parentNode; 2208 } 2209 var matrix = enchant.Matrix.instance; 2210 var stack = matrix.stack; 2211 var mat = []; 2212 var newmat, ox, oy; 2213 stack.push(tree[0]._matrix); 2214 for (var i = 1, l = tree.length; i < l; i++) { 2215 node = tree[i]; 2216 newmat = []; 2217 matrix.makeTransformMatrix(node, mat); 2218 matrix.multiply(stack[stack.length - 1], mat, newmat); 2219 node._matrix = newmat; 2220 stack.push(newmat); 2221 ox = (typeof node._originX === 'number') ? node._originX : node._width / 2 || 0; 2222 oy = (typeof node._originY === 'number') ? node._originY : node._height / 2 || 0; 2223 var vec = [ ox, oy ]; 2224 matrix.multiplyVec(newmat, vec, vec); 2225 node._offsetX = vec[0] - ox; 2226 node._offsetY = vec[1] - oy; 2227 node._dirty = false; 2228 } 2229 matrix.reset(); 2230 }, 2231 remove: function() { 2232 if (this.parentNode) { 2233 this.parentNode.removeChild(this); 2234 } 2235 if (this.childNodes) { 2236 var childNodes = this.childNodes.slice(); 2237 for(var i = childNodes.length-1; i >= 0; i--) { 2238 childNodes[i].remove(); 2239 } 2240 } 2241 2242 this.clearEventListener(); 2243 } 2244 }); 2245 2246 var _intersectBetweenClassAndInstance = function(Class, instance) { 2247 var ret = []; 2248 var c; 2249 for (var i = 0, l = Class.collection.length; i < l; i++) { 2250 c = Class.collection[i]; 2251 if (instance._intersectOne(c)) { 2252 ret.push(c); 2253 } 2254 } 2255 return ret; 2256 }; 2257 2258 var _intersectBetweenClassAndClass = function(Class1, Class2) { 2259 var ret = []; 2260 var c1, c2; 2261 for (var i = 0, l = Class1.collection.length; i < l; i++) { 2262 c1 = Class1.collection[i]; 2263 for (var j = 0, ll = Class2.collection.length; j < ll; j++) { 2264 c2 = Class2.collection[j]; 2265 if (c1._intersectOne(c2)) { 2266 ret.push([ c1, c2 ]); 2267 } 2268 } 2269 } 2270 return ret; 2271 }; 2272 2273 var _intersectStrictBetweenClassAndInstance = function(Class, instance) { 2274 var ret = []; 2275 var c; 2276 for (var i = 0, l = Class.collection.length; i < l; i++) { 2277 c = Class.collection[i]; 2278 if (instance._intersectStrictOne(c)) { 2279 ret.push(c); 2280 } 2281 } 2282 return ret; 2283 }; 2284 2285 var _intersectStrictBetweenClassAndClass = function(Class1, Class2) { 2286 var ret = []; 2287 var c1, c2; 2288 for (var i = 0, l = Class1.collection.length; i < l; i++) { 2289 c1 = Class1.collection[i]; 2290 for (var j = 0, ll = Class2.collection.length; j < ll; j++) { 2291 c2 = Class2.collection[j]; 2292 if (c1._intersectStrictOne(c2)) { 2293 ret.push([ c1, c2 ]); 2294 } 2295 } 2296 } 2297 return ret; 2298 }; 2299 2300 var _staticIntersect = function(other) { 2301 if (other instanceof enchant.Entity) { 2302 return _intersectBetweenClassAndInstance(this, other); 2303 } else if (typeof other === 'function' && other.collection) { 2304 return _intersectBetweenClassAndClass(this, other); 2305 } 2306 return false; 2307 }; 2308 2309 var _staticIntersectStrict = function(other) { 2310 if (other instanceof enchant.Entity) { 2311 return _intersectStrictBetweenClassAndInstance(this, other); 2312 } else if (typeof other === 'function' && other.collection) { 2313 return _intersectStrictBetweenClassAndClass(this, other); 2314 } 2315 return false; 2316 }; 2317 2318 var _nodePrototypeClearEventListener = enchant.Node.prototype.clearEventListener; 2319 2320 /** 2321 * @scope enchant.Entity.prototype 2322 */ 2323 enchant.Entity = enchant.Class.create(enchant.Node, { 2324 /** 2325 * @name enchant.Entity 2326 * @class 2327 * DOM上で表示する実体を持ったクラス. 直接使用することはない. 2328 * @constructs 2329 * @extends enchant.Node 2330 */ 2331 initialize: function() { 2332 var core = enchant.Core.instance; 2333 enchant.Node.call(this); 2334 2335 this._rotation = 0; 2336 this._scaleX = 1; 2337 this._scaleY = 1; 2338 2339 this._touchEnabled = true; 2340 this._clipping = false; 2341 2342 this._originX = null; 2343 this._originY = null; 2344 2345 this._width = 0; 2346 this._height = 0; 2347 this._backgroundColor = null; 2348 this._debugColor = '#0000ff'; 2349 this._opacity = 1; 2350 this._visible = true; 2351 this._buttonMode = null; 2352 2353 this._style = {}; 2354 this.__styleStatus = {}; 2355 2356 this._isContainedInCollection = false; 2357 2358 /** 2359 * Entityを描画する際の合成処理を設定する. 2360 * Canvas上に描画する際のみ有効. 2361 * CanvasのコンテキストのglobalCompositeOperationにセットされる. 2362 * @type String 2363 */ 2364 this.compositeOperation = null; 2365 2366 /** 2367 * Entityにボタンの機能を設定する. 2368 * Entityに対するタッチ, クリックをleft, right, up, down, a, bいずれかの 2369 * ボタン入力として割り当てる. 2370 * @type String 2371 */ 2372 this.buttonMode = null; 2373 /** 2374 * Entityが押されているかどうか. 2375 * {@link enchant.Entity.buttonMode} が設定されているときだけ機能する. 2376 * @type Boolean 2377 */ 2378 this.buttonPressed = false; 2379 this.addEventListener('touchstart', function() { 2380 if (!this.buttonMode) { 2381 return; 2382 } 2383 this.buttonPressed = true; 2384 this.dispatchEvent(new enchant.Event(this.buttonMode + 'buttondown')); 2385 core.changeButtonState(this.buttonMode, true); 2386 }); 2387 this.addEventListener('touchend', function() { 2388 if (!this.buttonMode) { 2389 return; 2390 } 2391 this.buttonPressed = false; 2392 this.dispatchEvent(new enchant.Event(this.buttonMode + 'buttonup')); 2393 core.changeButtonState(this.buttonMode, false); 2394 }); 2395 2396 this.enableCollection(); 2397 }, 2398 /** 2399 * Entityの横幅. 2400 * @type Number 2401 */ 2402 width: { 2403 get: function() { 2404 return this._width; 2405 }, 2406 set: function(width) { 2407 if(this._width !== width) { 2408 this._width = width; 2409 this._dirty = true; 2410 } 2411 } 2412 }, 2413 /** 2414 * Entityの高さ. 2415 * @type Number 2416 */ 2417 height: { 2418 get: function() { 2419 return this._height; 2420 }, 2421 set: function(height) { 2422 if(this._height !== height) { 2423 this._height = height; 2424 this._dirty = true; 2425 } 2426 } 2427 }, 2428 /** 2429 * Entityの背景色. 2430 * CSSの'color'プロパティと同様の形式で指定できる. 2431 * @type String 2432 */ 2433 backgroundColor: { 2434 get: function() { 2435 return this._backgroundColor; 2436 }, 2437 set: function(color) { 2438 this._backgroundColor = color; 2439 } 2440 }, 2441 /** 2442 * Entityのデバッグの枠色. 2443 * CSSの'color'プロパティと同様の形式で指定できる. 2444 * @type String 2445 */ 2446 debugColor: { 2447 get: function() { 2448 return this._debugColor; 2449 }, 2450 set: function(color) { 2451 this._debugColor = color; 2452 } 2453 }, 2454 /** 2455 * Entityの透明度. 2456 * 0から1までの値を設定する(0が完全な透明, 1が完全な不透明). 2457 * @type Number 2458 */ 2459 opacity: { 2460 get: function() { 2461 return this._opacity; 2462 }, 2463 set: function(opacity) { 2464 this._opacity = parseFloat(opacity); 2465 } 2466 }, 2467 /** 2468 * Entityを表示するかどうかを指定する. 2469 * @type Boolean 2470 */ 2471 visible: { 2472 get: function() { 2473 return this._visible; 2474 }, 2475 set: function(visible) { 2476 this._visible = visible; 2477 } 2478 }, 2479 /** 2480 * Entityのタッチを有効にするかどうかを指定する. 2481 * @type Boolean 2482 */ 2483 touchEnabled: { 2484 get: function() { 2485 return this._touchEnabled; 2486 }, 2487 set: function(enabled) { 2488 this._touchEnabled = enabled; 2489 if (enabled) { 2490 this._style.pointerEvents = 'all'; 2491 } else { 2492 this._style.pointerEvents = 'none'; 2493 } 2494 } 2495 }, 2496 /** 2497 * Entityの矩形が交差しているかどうかにより衝突判定を行う. 2498 * @param {*} other 衝突判定を行うEntityなどx, y, width, heightプロパティを持ったObject. 2499 * @return {Boolean} 衝突判定の結果. 2500 */ 2501 intersect: function(other) { 2502 if (other instanceof enchant.Entity) { 2503 return this._intersectOne(other); 2504 } else if (typeof other === 'function' && other.collection) { 2505 return _intersectBetweenClassAndInstance(other, this); 2506 } 2507 return false; 2508 }, 2509 _intersectOne: function(other) { 2510 if (this._dirty) { 2511 this._updateCoordinate(); 2512 } if (other._dirty) { 2513 other._updateCoordinate(); 2514 } 2515 return this._offsetX < other._offsetX + other.width && other._offsetX < this._offsetX + this.width && 2516 this._offsetY < other._offsetY + other.height && other._offsetY < this._offsetY + this.height; 2517 }, 2518 intersectStrict: function(other) { 2519 if (other instanceof enchant.Entity) { 2520 return this._intersectStrictOne(other); 2521 } else if (typeof other === 'function' && other.collection) { 2522 return _intersectStrictBetweenClassAndInstance(other, this); 2523 } 2524 return false; 2525 }, 2526 _intersectStrictOne: function(other) { 2527 if (this._dirty) { 2528 this._updateCoordinate(); 2529 } if (other._dirty) { 2530 other._updateCoordinate(); 2531 } 2532 var rect1 = this.getOrientedBoundingRect(), 2533 rect2 = other.getOrientedBoundingRect(), 2534 lt1 = rect1.leftTop, rt1 = rect1.rightTop, 2535 lb1 = rect1.leftBottom, rb1 = rect1.rightBottom, 2536 lt2 = rect2.leftTop, rt2 = rect2.rightTop, 2537 lb2 = rect2.leftBottom, rb2 = rect2.rightBottom, 2538 ltx1 = lt1[0], lty1 = lt1[1], rtx1 = rt1[0], rty1 = rt1[1], 2539 lbx1 = lb1[0], lby1 = lb1[1], rbx1 = rb1[0], rby1 = rb1[1], 2540 ltx2 = lt2[0], lty2 = lt2[1], rtx2 = rt2[0], rty2 = rt2[1], 2541 lbx2 = lb2[0], lby2 = lb2[1], rbx2 = rb2[0], rby2 = rb2[1], 2542 t1 = [ rtx1 - ltx1, rty1 - lty1 ], 2543 r1 = [ rbx1 - rtx1, rby1 - rty1 ], 2544 b1 = [ lbx1 - rbx1, lby1 - rby1 ], 2545 l1 = [ ltx1 - lbx1, lty1 - lby1 ], 2546 t2 = [ rtx2 - ltx2, rty2 - lty2 ], 2547 r2 = [ rbx2 - rtx2, rby2 - rty2 ], 2548 b2 = [ lbx2 - rbx2, lby2 - rby2 ], 2549 l2 = [ ltx2 - lbx2, lty2 - lby2 ], 2550 cx1 = (ltx1 + rtx1 + lbx1 + rbx1) >> 2, 2551 cy1 = (lty1 + rty1 + lby1 + rby1) >> 2, 2552 cx2 = (ltx2 + rtx2 + lbx2 + rbx2) >> 2, 2553 cy2 = (lty2 + rty2 + lby2 + rby2) >> 2, 2554 i, j, poss1, poss2, dirs1, dirs2, pos1, pos2, dir1, dir2, 2555 px1, py1, px2, py2, dx1, dy1, dx2, dy2, vx, vy, c, c1, c2; 2556 if (t1[0] * (cy2 - lty1) - t1[1] * (cx2 - ltx1) > 0 && 2557 r1[0] * (cy2 - rty1) - r1[1] * (cx2 - rtx1) > 0 && 2558 b1[0] * (cy2 - rby1) - b1[1] * (cx2 - rbx1) > 0 && 2559 l1[0] * (cy2 - lby1) - l1[1] * (cx2 - lbx1) > 0) { 2560 return true; 2561 } else if (t2[0] * (cy1 - lty2) - t2[1] * (cx1 - ltx2) > 0 && 2562 r2[0] * (cy1 - rty2) - r2[1] * (cx1 - rtx2) > 0 && 2563 b2[0] * (cy1 - rby2) - b2[1] * (cx1 - rbx2) > 0 && 2564 l2[0] * (cy1 - lby2) - l2[1] * (cx1 - lbx2) > 0) { 2565 return true; 2566 } else { 2567 poss1 = [ lt1, rt1, rb1, lb1 ]; 2568 poss2 = [ lt2, rt2, rb2, lb2 ]; 2569 dirs1 = [ t1, r1, b1, l1 ]; 2570 dirs2 = [ t2, r2, b2, l2 ]; 2571 for (i = 0; i < 4; i++) { 2572 pos1 = poss1[i]; 2573 px1 = pos1[0]; py1 = pos1[1]; 2574 dir1 = dirs1[i]; 2575 dx1 = dir1[0]; dy1 = dir1[1]; 2576 for (j = 0; j < 4; j++) { 2577 pos2 = poss2[j]; 2578 px2 = pos2[0]; py2 = pos2[1]; 2579 dir2 = dirs2[j]; 2580 dx2 = dir2[0]; dy2 = dir2[1]; 2581 c = dx1 * dy2 - dy1 * dx2; 2582 if (c !== 0) { 2583 vx = px2 - px1; 2584 vy = py2 - py1; 2585 c1 = (vx * dy1 - vy * dx1) / c; 2586 c2 = (vx * dy2 - vy * dx2) / c; 2587 if (0 < c1 && c1 < 1 && 0 < c2 && c2 < 1) { 2588 return true; 2589 } 2590 } 2591 } 2592 } 2593 return false; 2594 } 2595 }, 2596 /** 2597 * Entityの中心点どうしの距離により衝突判定を行う. 2598 * @param {*} other 衝突判定を行うEntityなどx, y, width, heightプロパティを持ったObject. 2599 * @param {Number} [distance] 衝突したと見なす最大の距離. デフォルト値は二つのEntityの横幅と高さの平均. 2600 * @return {Boolean} 衝突判定の結果. 2601 */ 2602 within: function(other, distance) { 2603 if (this._dirty) { 2604 this._updateCoordinate(); 2605 } if (other._dirty) { 2606 other._updateCoordinate(); 2607 } 2608 if (distance == null) { 2609 distance = (this.width + this.height + other.width + other.height) / 4; 2610 } 2611 var _; 2612 return (_ = this._offsetX - other._offsetX + (this.width - other.width) / 2) * _ + 2613 (_ = this._offsetY - other._offsetY + (this.height - other.height) / 2) * _ < distance * distance; 2614 }, 2615 /** 2616 * Entityを拡大縮小する. 2617 * @param {Number} x 拡大するx軸方向の倍率. 2618 * @param {Number} [y] 拡大するy軸方向の倍率. 2619 */ 2620 scale: function(x, y) { 2621 this._scaleX *= x; 2622 this._scaleY *= (y != null) ? y : x; 2623 this._dirty = true; 2624 }, 2625 /** 2626 * Entityを回転する. 2627 * @param {Number} deg 回転する角度 (度数法). 2628 */ 2629 rotate: function(deg) { 2630 this.rotation += deg; 2631 }, 2632 /** 2633 * Entityのx軸方向の倍率. 2634 * @type Number 2635 */ 2636 scaleX: { 2637 get: function() { 2638 return this._scaleX; 2639 }, 2640 set: function(scaleX) { 2641 if(this._scaleX !== scaleX) { 2642 this._scaleX = scaleX; 2643 this._dirty = true; 2644 } 2645 } 2646 }, 2647 /** 2648 * Entityのy軸方向の倍率. 2649 * @type Number 2650 */ 2651 scaleY: { 2652 get: function() { 2653 return this._scaleY; 2654 }, 2655 set: function(scaleY) { 2656 if(this._scaleY !== scaleY) { 2657 this._scaleY = scaleY; 2658 this._dirty = true; 2659 } 2660 } 2661 }, 2662 /** 2663 * Entityの回転角 (度数法). 2664 * @type Number 2665 */ 2666 rotation: { 2667 get: function() { 2668 return this._rotation; 2669 }, 2670 set: function(rotation) { 2671 if(this._rotation !== rotation) { 2672 this._rotation = rotation; 2673 this._dirty = true; 2674 } 2675 } 2676 }, 2677 /** 2678 * 回転・拡大縮小の基準点のX座標 2679 * @type Number 2680 */ 2681 originX: { 2682 get: function() { 2683 return this._originX; 2684 }, 2685 set: function(originX) { 2686 if(this._originX !== originX) { 2687 this._originX = originX; 2688 this._dirty = true; 2689 } 2690 } 2691 }, 2692 /** 2693 * 回転・拡大縮小の基準点のY座標 2694 * @type Number 2695 */ 2696 originY: { 2697 get: function() { 2698 return this._originY; 2699 }, 2700 set: function(originY) { 2701 if(this._originY !== originY) { 2702 this._originY = originY; 2703 this._dirty = true; 2704 } 2705 } 2706 }, 2707 /** 2708 * インスタンスをコレクションの対象にする. 2709 * デフォルトで呼び出される. 2710 */ 2711 enableCollection: function() { 2712 this.addEventListener('addedtoscene', this._addSelfToCollection); 2713 this.addEventListener('removedfromscene', this._removeSelfFromCollection); 2714 if (this.scene) { 2715 this._addSelfToCollection(); 2716 } 2717 }, 2718 /** 2719 * インスタンスをコレクションの対象から除外する. 2720 */ 2721 disableCollection: function() { 2722 this.removeEventListener('addedtoscene', this._addSelfToCollection); 2723 this.removeEventListener('removedfromscene', this._removeSelfFromCollection); 2724 if (this.scene) { 2725 this._removeSelfFromCollection(); 2726 } 2727 }, 2728 /**#nocode+*/ 2729 clearEventListener: function() { 2730 _nodePrototypeClearEventListener.apply(this,arguments); 2731 if (this.scene) { 2732 this._removeSelfFromCollection(); 2733 } 2734 }, 2735 /**#nocode-*/ 2736 _addSelfToCollection: function() { 2737 if (this._isContainedInCollection) { 2738 return; 2739 } 2740 2741 var Constructor = this.getConstructor(); 2742 Constructor._collectionTarget.forEach(function(C) { 2743 C.collection.push(this); 2744 }, this); 2745 2746 this._isContainedInCollection = true; 2747 }, 2748 _removeSelfFromCollection: function() { 2749 if (!this._isContainedInCollection) { 2750 return; 2751 } 2752 2753 var Constructor = this.getConstructor(); 2754 Constructor._collectionTarget.forEach(function(C) { 2755 var i = C.collection.indexOf(this); 2756 if (i !== -1) { 2757 C.collection.splice(i, 1); 2758 } 2759 }, this); 2760 2761 this._isContainedInCollection = false; 2762 }, 2763 getBoundingRect: function() { 2764 var w = this.width || 0; 2765 var h = this.height || 0; 2766 var mat = this._matrix; 2767 var m11w = mat[0] * w, m12w = mat[1] * w, 2768 m21h = mat[2] * h, m22h = mat[3] * h, 2769 mdx = mat[4], mdy = mat[5]; 2770 var xw = [ mdx, m11w + mdx, m21h + mdx, m11w + m21h + mdx ].sort(function(a, b) { return a - b; }); 2771 var yh = [ mdy, m12w + mdy, m22h + mdy, m12w + m22h + mdy ].sort(function(a, b) { return a - b; }); 2772 2773 return { 2774 left: xw[0], 2775 top: yh[0], 2776 width: xw[3] - xw[0], 2777 height: yh[3] - yh[0] 2778 }; 2779 }, 2780 getOrientedBoundingRect: function() { 2781 var w = this.width || 0; 2782 var h = this.height || 0; 2783 var mat = this._matrix; 2784 var m11w = mat[0] * w, m12w = mat[1] * w, 2785 m21h = mat[2] * h, m22h = mat[3] * h, 2786 mdx = mat[4], mdy = mat[5]; 2787 2788 return { 2789 leftTop: [ mdx, mdy ], 2790 rightTop: [ m11w + mdx, m12w + mdy ], 2791 leftBottom: [ m21h + mdx, m22h + mdy ], 2792 rightBottom: [ m11w + m21h + mdx, m12w + m22h + mdy ] 2793 }; 2794 }, 2795 getConstructor: function() { 2796 return Object.getPrototypeOf(this).constructor; 2797 } 2798 }); 2799 2800 var _collectizeConstructor = function(Constructor) { 2801 if (Constructor._collective) { 2802 return; 2803 } 2804 var rel = enchant.Class.getInheritanceTree(Constructor); 2805 var i = rel.indexOf(enchant.Entity); 2806 if (i !== -1) { 2807 Constructor._collectionTarget = rel.splice(0, i + 1); 2808 } else { 2809 Constructor._collectionTarget = []; 2810 } 2811 Constructor.intersect = _staticIntersect; 2812 Constructor.intersectStrict = _staticIntersectStrict; 2813 Constructor.collection = []; 2814 Constructor._collective = true; 2815 }; 2816 2817 _collectizeConstructor(enchant.Entity); 2818 2819 enchant.Entity._inherited = function(subclass) { 2820 _collectizeConstructor(subclass); 2821 }; 2822 2823 /** 2824 * @scope enchant.Sprite.prototype 2825 */ 2826 enchant.Sprite = enchant.Class.create(enchant.Entity, { 2827 /** 2828 * @name enchant.Sprite 2829 * @class 2830 * 画像表示機能を持ったクラス. Entity を継承している. 2831 * @param {Number} width Spriteの横幅. 2832 * @param {Number} height Spriteの高さ. 2833 * 2834 * @example 2835 * var bear = new Sprite(32, 32); 2836 * bear.image = core.assets['chara1.gif']; 2837 * 2838 * @constructs 2839 * @extends enchant.Entity 2840 */ 2841 initialize: function(width, height) { 2842 enchant.Entity.call(this); 2843 2844 this.width = width; 2845 this.height = height; 2846 this._image = null; 2847 this._debugColor = '#ff0000'; 2848 this._frameLeft = 0; 2849 this._frameTop = 0; 2850 this._frame = 0; 2851 this._frameSequence = null; 2852 }, 2853 /** 2854 * Spriteで表示する画像. 2855 * @type enchant.Surface 2856 */ 2857 image: { 2858 get: function() { 2859 return this._image; 2860 }, 2861 set: function(image) { 2862 if (image === undefined) { 2863 throw new Error('Assigned value on Sprite.image is undefined. Please double-check image path, and check if the image you want to use is preload before use.'); 2864 } 2865 if (image === this._image) { 2866 return; 2867 } 2868 this._image = image; 2869 this._computeFramePosition(); 2870 } 2871 }, 2872 /** 2873 * 表示するフレームのインデックス. 2874 * Spriteと同じ横幅と高さを持ったフレームが{@link enchant.Sprite#image}プロパティの画像に左上から順に 2875 * 配列されていると見て, 0から始まるインデックスを指定することでフレームを切り替える. 2876 * 2877 * 数値の配列が指定された場合, それらを毎フレーム順に切り替える. 2878 * ループするが, null値が含まれているとそこでループをストップする. 2879 * 2880 * @example 2881 * var sprite = new Sprite(32, 32); 2882 * sprite.frame = [0, 1, 0, 2] 2883 * //-> 0, 1, 0, 2, 0, 1, 0, 2,.. 2884 * sprite.frame = [0, 1, 0, 2, null] 2885 * //-> 0, 1, 0, 2, (2, 2,.. :stop) 2886 * 2887 * @type Number|Array 2888 */ 2889 frame: { 2890 get: function() { 2891 return this._frame; 2892 }, 2893 set: function(frame) { 2894 if (((this._frameSequence == null) && (this._frame === frame)) || (this._deepCompareToPreviousFrame(frame))) { 2895 return; 2896 } 2897 if (frame instanceof Array) { 2898 this._frameSequence = frame; 2899 } else { 2900 this._frameSequence = null; 2901 this._frame = frame; 2902 this._computeFramePosition(); 2903 } 2904 } 2905 }, 2906 _frameSequence: { 2907 get: function() { 2908 return this.__frameSequence; 2909 }, 2910 set: function(frameSequence) { 2911 if(frameSequence && !this.__frameSequence) { 2912 this.addEventListener(enchant.Event.ENTER_FRAME, this._rotateFrameSequence); 2913 } else if(!frameSequence && this.__frameSequence) { 2914 this.removeEventListener(enchant.Event.ENTER_FRAME, this._rotateFrameSequence); 2915 } 2916 if(frameSequence) { 2917 this.__frameSequence = frameSequence.slice(); 2918 this._originalFrameSequence = frameSequence.slice(); 2919 this._rotateFrameSequence(); 2920 } else { 2921 this.__frameSequence = null; 2922 this._originalFrameSequence = null; 2923 } 2924 } 2925 }, 2926 /** 2927 * @private 2928 */ 2929 _deepCompareToPreviousFrame: function(frameArray) { 2930 if (frameArray === this._originalFrameSequence) { 2931 return true; 2932 } 2933 if (frameArray == null || this._originalFrameSequence == null) { 2934 return false; 2935 } 2936 if (!(frameArray instanceof Array)) { 2937 return false; 2938 } 2939 if (frameArray.length !== this._originalFrameSequence.length) { 2940 return false; 2941 } 2942 for (var i = 0; i < frameArray.length; ++i) { 2943 if (frameArray[i] !== this._originalFrameSequence[i]){ 2944 return false; 2945 } 2946 } 2947 return true; 2948 }, 2949 /** 2950 * 0 <= frame 2951 * 0以下の動作は未定義. 2952 * @private 2953 */ 2954 _computeFramePosition: function() { 2955 var image = this._image; 2956 var row; 2957 if (image != null) { 2958 row = image.width / this._width | 0; 2959 this._frameLeft = (this._frame % row | 0) * this._width; 2960 this._frameTop = (this._frame / row | 0) * this._height % image.height; 2961 } 2962 }, 2963 _rotateFrameSequence: function() { 2964 var frameSequence = this._frameSequence; 2965 if (frameSequence && frameSequence.length !== 0) { 2966 var nextFrame = frameSequence.shift(); 2967 if (nextFrame === null) { 2968 this._frameSequence = null; 2969 this.dispatchEvent(new enchant.Event(enchant.Event.ANIMATION_END)); 2970 } else { 2971 this._frame = nextFrame; 2972 this._computeFramePosition(); 2973 frameSequence.push(nextFrame); 2974 } 2975 } 2976 }, 2977 /**#nocode+*/ 2978 width: { 2979 get: function() { 2980 return this._width; 2981 }, 2982 set: function(width) { 2983 this._width = width; 2984 this._computeFramePosition(); 2985 this._dirty = true; 2986 } 2987 }, 2988 height: { 2989 get: function() { 2990 return this._height; 2991 }, 2992 set: function(height) { 2993 this._height = height; 2994 this._computeFramePosition(); 2995 this._dirty = true; 2996 } 2997 }, 2998 /**#nocode-*/ 2999 cvsRender: function(ctx) { 3000 var image = this._image, 3001 w = this._width, h = this._height, 3002 iw, ih, elem, sx, sy, sw, sh; 3003 if (image && w !== 0 && h !== 0) { 3004 iw = image.width; 3005 ih = image.height; 3006 if (iw < w || ih < h) { 3007 ctx.fillStyle = enchant.Surface._getPattern(image); 3008 ctx.fillRect(0, 0, w, h); 3009 } else { 3010 elem = image._element; 3011 sx = this._frameLeft; 3012 sy = Math.min(this._frameTop, ih - h); 3013 // IE9 doesn't allow for negative or 0 widths/heights when drawing on the CANVAS element 3014 sw = Math.max(0.01, Math.min(iw - sx, w)); 3015 sh = Math.max(0.01, Math.min(ih - sy, h)); 3016 ctx.drawImage(elem, sx, sy, sw, sh, 0, 0, w, h); 3017 } 3018 } 3019 }, 3020 domRender: (function() { 3021 if (enchant.ENV.VENDOR_PREFIX === 'ms') { 3022 return function(element) { 3023 if (this._image) { 3024 if (this._image._css) { 3025 this._style['background-image'] = this._image._css; 3026 this._style['background-position'] = 3027 -this._frameLeft + 'px ' + 3028 -this._frameTop + 'px'; 3029 } else if (this._image._element) { 3030 } 3031 } 3032 }; 3033 } else { 3034 return function(element) { 3035 if (this._image) { 3036 if (this._image._css) { 3037 this._style['background-image'] = this._image._css; 3038 this._style['background-position'] = 3039 -this._frameLeft + 'px ' + 3040 -this._frameTop + 'px'; 3041 } else if (this._image._element) { 3042 } 3043 } 3044 }; 3045 } 3046 }()) 3047 }); 3048 3049 /** 3050 * @scope enchant.Label.prototype 3051 */ 3052 enchant.Label = enchant.Class.create(enchant.Entity, { 3053 /** 3054 * @name enchant.Label 3055 * @class 3056 * Label クラス. 3057 * @constructs 3058 * @extends enchant.Entity 3059 */ 3060 initialize: function(text) { 3061 enchant.Entity.call(this); 3062 3063 this.text = text || ''; 3064 this.width = 300; 3065 this.font = '14px serif'; 3066 this.textAlign = 'left'; 3067 3068 this._debugColor = '#ff0000'; 3069 }, 3070 /**#nocode+*/ 3071 width: { 3072 get: function() { 3073 return this._width; 3074 }, 3075 set: function(width) { 3076 this._width = width; 3077 this._dirty = true; 3078 // issue #164 3079 this.updateBoundArea(); 3080 } 3081 }, 3082 /**#nocode-*/ 3083 /** 3084 * 表示するテキスト. 3085 * DOM レンダラを利用している場合 (DOMScene 以下にある場合) 改行タグ (br) も利用できますが, 3086 * ユーザから入力したり, サーバから取得した文字列を表示する場合, XSS 脆弱性などに注意してください. 3087 * Canvas レンダラを利用できる場合でも, 改行タグ (br, BR) は改行に変換されます. 3088 * @type String 3089 */ 3090 text: { 3091 get: function() { 3092 return this._text; 3093 }, 3094 set: function(text) { 3095 text = '' + text; 3096 if(this._text === text) { 3097 return; 3098 } 3099 this._text = text; 3100 text = text.replace(/<br ?\/?>/gi, '<br/>'); 3101 this._splitText = text.split('<br/>'); 3102 this.updateBoundArea(); 3103 for (var i = 0, l = this._splitText.length; i < l; i++) { 3104 text = this._splitText[i]; 3105 var metrics = this.getMetrics(text); 3106 this._splitText[i] = {}; 3107 this._splitText[i].text = text; 3108 this._splitText[i].height = metrics.height; 3109 this._splitText[i].width = metrics.width; 3110 } 3111 } 3112 }, 3113 /** 3114 * テキストの水平位置の指定. 3115 * CSSの'text-align'プロパティと同様の形式で指定できる. 3116 * @type String 3117 */ 3118 textAlign: { 3119 get: function() { 3120 return this._style['text-align']; 3121 }, 3122 set: function(textAlign) { 3123 this._style['text-align'] = textAlign; 3124 this.updateBoundArea(); 3125 } 3126 }, 3127 /** 3128 * フォントの指定. 3129 * CSSの'font'プロパティと同様の形式で指定できる. 3130 * @type String 3131 */ 3132 font: { 3133 get: function() { 3134 return this._style.font; 3135 }, 3136 set: function(font) { 3137 this._style.font = font; 3138 this.updateBoundArea(); 3139 } 3140 }, 3141 /** 3142 * 文字色の指定. 3143 * CSSの'color'プロパティと同様の形式で指定できる. 3144 * @type String 3145 */ 3146 color: { 3147 get: function() { 3148 return this._style.color; 3149 }, 3150 set: function(color) { 3151 this._style.color = color; 3152 } 3153 }, 3154 cvsRender: function(ctx) { 3155 var x, y = 0; 3156 var labelWidth = this.width; 3157 var charWidth, amount, line, text, c, buf, increase, length; 3158 var bufWidth; 3159 if (this._splitText) { 3160 ctx.textBaseline = 'top'; 3161 ctx.font = this.font; 3162 ctx.fillStyle = this.color || '#000000'; 3163 charWidth = ctx.measureText(' ').width; 3164 amount = labelWidth / charWidth; 3165 for (var i = 0, l = this._splitText.length; i < l; i++) { 3166 line = this._splitText[i]; 3167 text = line.text; 3168 c = 0; 3169 while (text.length > c + amount || ctx.measureText(text.slice(c, c + amount)).width > labelWidth) { 3170 buf = ''; 3171 increase = amount; 3172 length = 0; 3173 while (increase > 0) { 3174 if (ctx.measureText(buf).width < labelWidth) { 3175 length += increase; 3176 buf = text.slice(c, c + length); 3177 } else { 3178 length -= increase; 3179 buf = text.slice(c, c + length); 3180 } 3181 increase = increase / 2 | 0; 3182 } 3183 ctx.fillText(buf, 0, y); 3184 y += line.height - 1; 3185 c += length; 3186 } 3187 buf = text.slice(c, c + text.length); 3188 if (this.textAlign === 'right') { 3189 x = labelWidth - ctx.measureText(buf).width; 3190 } else if (this.textAlign === 'center') { 3191 x = (labelWidth - ctx.measureText(buf).width) / 2; 3192 } else { 3193 x = 0; 3194 } 3195 ctx.fillText(buf, x, y); 3196 y += line.height - 1; 3197 } 3198 } 3199 }, 3200 domRender: function(element) { 3201 if (element.innerHTML !== this._text) { 3202 element.innerHTML = this._text; 3203 } 3204 }, 3205 detectRender: function(ctx) { 3206 ctx.fillRect(this._boundOffset, 0, this._boundWidth, this._boundHeight); 3207 }, 3208 updateBoundArea: function() { 3209 var metrics = this.getMetrics(); 3210 this._boundWidth = metrics.width; 3211 this._boundHeight = metrics.height; 3212 if (this.textAlign === 'right') { 3213 this._boundOffset = this.width - this._boundWidth; 3214 } else if (this.textAlign === 'center') { 3215 this._boundOffset = (this.width - this._boundWidth) / 2; 3216 } else { 3217 this._boundOffset = 0; 3218 } 3219 }, 3220 getMetrics: function(text) { 3221 var ret = {}; 3222 var div, width, height; 3223 if (document.body) { 3224 div = document.createElement('div'); 3225 for (var prop in this._style) { 3226 if(prop !== 'width' && prop !== 'height') { 3227 div.style[prop] = this._style[prop]; 3228 } 3229 } 3230 text = text || this._text; 3231 div.innerHTML = text.replace(/ /g, ' '); 3232 div.style.whiteSpace = 'noWrap'; 3233 div.style.lineHeight = 1; 3234 document.body.appendChild(div); 3235 var computedStyle = getComputedStyle(div); 3236 ret.height = parseInt(computedStyle.height, 10) + 1; 3237 div.style.position = 'absolute'; 3238 ret.width = parseInt(computedStyle.width, 10) + 1; 3239 document.body.removeChild(div); 3240 } else { 3241 ret.width = this.width; 3242 ret.height = this.height; 3243 } 3244 return ret; 3245 } 3246 }); 3247 3248 /** 3249 * @scope enchant.Map.prototype 3250 */ 3251 enchant.Map = enchant.Class.create(enchant.Entity, { 3252 /** 3253 * @name enchant.Map 3254 * @class 3255 * タイルセットからマップを生成して表示するクラス. 3256 * @param {Number} tileWidth タイルの横幅. 3257 * @param {Number} tileHeight タイルの高さ. 3258 * @constructs 3259 * @extends enchant.Entity 3260 */ 3261 initialize: function(tileWidth, tileHeight) { 3262 var core = enchant.Core.instance; 3263 3264 enchant.Entity.call(this); 3265 3266 var surface = new enchant.Surface(core.width, core.height); 3267 this._surface = surface; 3268 var canvas = surface._element; 3269 canvas.style.position = 'absolute'; 3270 if (enchant.ENV.RETINA_DISPLAY && core.scale === 2) { 3271 canvas.width = core.width * 2; 3272 canvas.height = core.height * 2; 3273 this._style.webkitTransformOrigin = '0 0'; 3274 this._style.webkitTransform = 'scale(0.5)'; 3275 } else { 3276 canvas.width = core.width; 3277 canvas.height = core.height; 3278 } 3279 this._context = canvas.getContext('2d'); 3280 3281 this._tileWidth = tileWidth || 0; 3282 this._tileHeight = tileHeight || 0; 3283 this._image = null; 3284 this._data = [ 3285 [ 3286 [] 3287 ] 3288 ]; 3289 this._dirty = false; 3290 this._tight = false; 3291 3292 this.touchEnabled = false; 3293 3294 /** 3295 * タイルが衝突判定を持つかを表す値の二元配列. 3296 * @type Number[][] 3297 */ 3298 this.collisionData = null; 3299 3300 this._listeners['render'] = null; 3301 this.addEventListener('render', function() { 3302 if(this._dirty) { 3303 this._previousOffsetX = this._previousOffsetY = null; 3304 } 3305 }); 3306 }, 3307 /** 3308 * データを設定する. 3309 * タイルががimageプロパティの画像に左上から順に配列されていると見て, 0から始まる 3310 * インデックスの二元配列を設定する.複数指定された場合は後のものから順に表示される. 3311 * @param {...Number[][]} data タイルのインデックスの二元配列. 複数指定できる. 3312 */ 3313 loadData: function(data) { 3314 this._data = Array.prototype.slice.apply(arguments); 3315 this._dirty = true; 3316 3317 this._tight = false; 3318 for (var i = 0, len = this._data.length; i < len; i++) { 3319 var c = 0; 3320 data = this._data[i]; 3321 for (var y = 0, l = data.length; y < l; y++) { 3322 for (var x = 0, ll = data[y].length; x < ll; x++) { 3323 if (data[y][x] >= 0) { 3324 c++; 3325 } 3326 } 3327 } 3328 if (c / (data.length * data[0].length) > 0.2) { 3329 this._tight = true; 3330 break; 3331 } 3332 } 3333 }, 3334 /** 3335 * ある座標のタイルが何か調べる. 3336 * @param {Number} x マップ上の点のx座標. 3337 * @param {Number} y マップ上の点のy座標. 3338 * @return {*} ある座標のタイルのデータ. 3339 */ 3340 checkTile: function(x, y) { 3341 if (x < 0 || this.width <= x || y < 0 || this.height <= y) { 3342 return false; 3343 } 3344 var width = this._image.width; 3345 var height = this._image.height; 3346 var tileWidth = this._tileWidth || width; 3347 var tileHeight = this._tileHeight || height; 3348 x = x / tileWidth | 0; 3349 y = y / tileHeight | 0; 3350 // return this._data[y][x]; 3351 var data = this._data[0]; 3352 return data[y][x]; 3353 }, 3354 /** 3355 * Map上に障害物があるかどうかを判定する. 3356 * @param {Number} x 判定を行うマップ上の点のx座標. 3357 * @param {Number} y 判定を行うマップ上の点のy座標. 3358 * @return {Boolean} 障害物があるかどうか. 3359 */ 3360 hitTest: function(x, y) { 3361 if (x < 0 || this.width <= x || y < 0 || this.height <= y) { 3362 return false; 3363 } 3364 var width = this._image.width; 3365 var height = this._image.height; 3366 var tileWidth = this._tileWidth || width; 3367 var tileHeight = this._tileHeight || height; 3368 x = x / tileWidth | 0; 3369 y = y / tileHeight | 0; 3370 if (this.collisionData != null) { 3371 return this.collisionData[y] && !!this.collisionData[y][x]; 3372 } else { 3373 for (var i = 0, len = this._data.length; i < len; i++) { 3374 var data = this._data[i]; 3375 var n; 3376 if (data[y] != null && (n = data[y][x]) != null && 3377 0 <= n && n < (width / tileWidth | 0) * (height / tileHeight | 0)) { 3378 return true; 3379 } 3380 } 3381 return false; 3382 } 3383 }, 3384 /** 3385 * Mapで表示するタイルセット画像. 3386 * @type enchant.Surface 3387 */ 3388 image: { 3389 get: function() { 3390 return this._image; 3391 }, 3392 set: function(image) { 3393 var core = enchant.Core.instance; 3394 3395 this._image = image; 3396 if (enchant.ENV.RETINA_DISPLAY && core.scale === 2) { 3397 var img = new enchant.Surface(image.width * 2, image.height * 2); 3398 var tileWidth = this._tileWidth || image.width; 3399 var tileHeight = this._tileHeight || image.height; 3400 var row = image.width / tileWidth | 0; 3401 var col = image.height / tileHeight | 0; 3402 for (var y = 0; y < col; y++) { 3403 for (var x = 0; x < row; x++) { 3404 img.draw(image, x * tileWidth, y * tileHeight, tileWidth, tileHeight, 3405 x * tileWidth * 2, y * tileHeight * 2, tileWidth * 2, tileHeight * 2); 3406 } 3407 } 3408 this._doubledImage = img; 3409 } 3410 this._dirty = true; 3411 } 3412 }, 3413 /** 3414 * Mapのタイルの横幅. 3415 * @type Number 3416 */ 3417 tileWidth: { 3418 get: function() { 3419 return this._tileWidth; 3420 }, 3421 set: function(tileWidth) { 3422 if(this._tileWidth !== tileWidth) { 3423 this._tileWidth = tileWidth; 3424 this._dirty = true; 3425 } 3426 } 3427 }, 3428 /** 3429 * Mapのタイルの高さ. 3430 * @type Number 3431 */ 3432 tileHeight: { 3433 get: function() { 3434 return this._tileHeight; 3435 }, 3436 set: function(tileHeight) { 3437 if(this._tileHeight !== tileHeight) { 3438 this._tileHeight = tileHeight; 3439 this._dirty = true; 3440 } 3441 } 3442 }, 3443 /** 3444 * @private 3445 */ 3446 width: { 3447 get: function() { 3448 return this._tileWidth * this._data[0][0].length; 3449 } 3450 }, 3451 /** 3452 * @private 3453 */ 3454 height: { 3455 get: function() { 3456 return this._tileHeight * this._data[0].length; 3457 } 3458 }, 3459 /** 3460 * @private 3461 */ 3462 redraw: function(x, y, width, height) { 3463 if (this._image == null) { 3464 return; 3465 } 3466 3467 var image, tileWidth, tileHeight, dx, dy; 3468 if (this._doubledImage) { 3469 image = this._doubledImage; 3470 tileWidth = this._tileWidth * 2; 3471 tileHeight = this._tileHeight * 2; 3472 dx = -this._offsetX * 2; 3473 dy = -this._offsetY * 2; 3474 x *= 2; 3475 y *= 2; 3476 width *= 2; 3477 height *= 2; 3478 } else { 3479 image = this._image; 3480 tileWidth = this._tileWidth; 3481 tileHeight = this._tileHeight; 3482 dx = -this._offsetX; 3483 dy = -this._offsetY; 3484 } 3485 var row = image.width / tileWidth | 0; 3486 var col = image.height / tileHeight | 0; 3487 var left = Math.max((x + dx) / tileWidth | 0, 0); 3488 var top = Math.max((y + dy) / tileHeight | 0, 0); 3489 var right = Math.ceil((x + dx + width) / tileWidth); 3490 var bottom = Math.ceil((y + dy + height) / tileHeight); 3491 3492 var source = image._element; 3493 var context = this._context; 3494 var canvas = context.canvas; 3495 context.clearRect(x, y, width, height); 3496 for (var i = 0, len = this._data.length; i < len; i++) { 3497 var data = this._data[i]; 3498 var r = Math.min(right, data[0].length); 3499 var b = Math.min(bottom, data.length); 3500 for (y = top; y < b; y++) { 3501 for (x = left; x < r; x++) { 3502 var n = data[y][x]; 3503 if (0 <= n && n < row * col) { 3504 var sx = (n % row) * tileWidth; 3505 var sy = (n / row | 0) * tileHeight; 3506 context.drawImage(source, sx, sy, tileWidth, tileHeight, 3507 x * tileWidth - dx, y * tileHeight - dy, tileWidth, tileHeight); 3508 } 3509 } 3510 } 3511 } 3512 }, 3513 /** 3514 * @private 3515 */ 3516 updateBuffer: function() { 3517 if (this._visible === undefined || this._visible) { 3518 var core = enchant.Core.instance; 3519 if (this._dirty || this._previousOffsetX == null) { 3520 this.redraw(0, 0, core.width, core.height); 3521 } else if (this._offsetX !== this._previousOffsetX || 3522 this._offsetY !== this._previousOffsetY) { 3523 if (this._tight) { 3524 var x = -this._offsetX; 3525 var y = -this._offsetY; 3526 var px = -this._previousOffsetX; 3527 var py = -this._previousOffsetY; 3528 var w1 = x - px + core.width; 3529 var w2 = px - x + core.width; 3530 var h1 = y - py + core.height; 3531 var h2 = py - y + core.height; 3532 if (w1 > this._tileWidth && w2 > this._tileWidth && 3533 h1 > this._tileHeight && h2 > this._tileHeight) { 3534 var sx, sy, dx, dy, sw, sh; 3535 if (w1 < w2) { 3536 sx = 0; 3537 dx = px - x; 3538 sw = w1; 3539 } else { 3540 sx = x - px; 3541 dx = 0; 3542 sw = w2; 3543 } 3544 if (h1 < h2) { 3545 sy = 0; 3546 dy = py - y; 3547 sh = h1; 3548 } else { 3549 sy = y - py; 3550 dy = 0; 3551 sh = h2; 3552 } 3553 3554 if (core._buffer == null) { 3555 core._buffer = document.createElement('canvas'); 3556 core._buffer.width = this._context.canvas.width; 3557 core._buffer.height = this._context.canvas.height; 3558 } 3559 var context = core._buffer.getContext('2d'); 3560 if (this._doubledImage) { 3561 context.clearRect(0, 0, sw * 2, sh * 2); 3562 context.drawImage(this._context.canvas, 3563 sx * 2, sy * 2, sw * 2, sh * 2, 0, 0, sw * 2, sh * 2); 3564 context = this._context; 3565 context.clearRect(dx * 2, dy * 2, sw * 2, sh * 2); 3566 context.drawImage(core._buffer, 3567 0, 0, sw * 2, sh * 2, dx * 2, dy * 2, sw * 2, sh * 2); 3568 } else { 3569 context.clearRect(0, 0, sw, sh); 3570 context.drawImage(this._context.canvas, 3571 sx, sy, sw, sh, 0, 0, sw, sh); 3572 context = this._context; 3573 context.clearRect(dx, dy, sw, sh); 3574 context.drawImage(core._buffer, 3575 0, 0, sw, sh, dx, dy, sw, sh); 3576 } 3577 3578 if (dx === 0) { 3579 this.redraw(sw, 0, core.width - sw, core.height); 3580 } else { 3581 this.redraw(0, 0, core.width - sw, core.height); 3582 } 3583 if (dy === 0) { 3584 this.redraw(0, sh, core.width, core.height - sh); 3585 } else { 3586 this.redraw(0, 0, core.width, core.height - sh); 3587 } 3588 } else { 3589 this.redraw(0, 0, core.width, core.height); 3590 } 3591 } else { 3592 this.redraw(0, 0, core.width, core.height); 3593 } 3594 } 3595 this._previousOffsetX = this._offsetX; 3596 this._previousOffsetY = this._offsetY; 3597 } 3598 }, 3599 cvsRender: function(ctx) { 3600 if (this.width !== 0 && this.height !== 0) { 3601 var core = enchant.Core.instance; 3602 this.updateBuffer(); 3603 ctx.save(); 3604 ctx.setTransform(1, 0, 0, 1, 0, 0); 3605 var cvs = this._context.canvas; 3606 ctx.drawImage(cvs, 0, 0, core.width, core.height); 3607 ctx.restore(); 3608 } 3609 }, 3610 domRender: function(element) { 3611 if (this._image) { 3612 this.updateBuffer(); 3613 this._style['background-image'] = this._surface._css; 3614 // bad performance 3615 this._style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'matrix(1, 0, 0, 1, 0, 0)'; 3616 } 3617 } 3618 }); 3619 3620 3621 /** 3622 * @scope enchant.Group.prototype 3623 */ 3624 enchant.Group = enchant.Class.create(enchant.Node, { 3625 /** 3626 * @name enchant.Group 3627 * @class 3628 * 複数の {@link enchant.Node} を子に持つことができるクラス. 3629 * 3630 * @example 3631 * var stage = new Group(); 3632 * stage.addChild(player); 3633 * stage.addChild(enemy); 3634 * stage.addChild(map); 3635 * stage.addEventListener('enterframe', function() { 3636 * // playerの座標に従って全体をスクロールする 3637 * if (this.x > 64 - player.x) { 3638 * this.x = 64 - player.x; 3639 * } 3640 * }); 3641 * @constructs 3642 * @extends enchant.Node 3643 */ 3644 initialize: function() { 3645 /** 3646 * 子のNode. 3647 * @type enchant.Node[] 3648 */ 3649 this.childNodes = []; 3650 3651 enchant.Node.call(this); 3652 3653 this._rotation = 0; 3654 this._scaleX = 1; 3655 this._scaleY = 1; 3656 3657 this._originX = null; 3658 this._originY = null; 3659 3660 this.__dirty = false; 3661 3662 [enchant.Event.ADDED_TO_SCENE, enchant.Event.REMOVED_FROM_SCENE] 3663 .forEach(function(event) { 3664 this.addEventListener(event, function(e) { 3665 this.childNodes.forEach(function(child) { 3666 child.scene = this.scene; 3667 child.dispatchEvent(e); 3668 }, this); 3669 }); 3670 }, this); 3671 }, 3672 /** 3673 * GroupにNodeを追加する. 3674 * @param {enchant.Node} node 追加するNode. 3675 */ 3676 addChild: function(node) { 3677 if (node.parentNode) { 3678 node.parentNode.removeChild(node); 3679 } 3680 this.childNodes.push(node); 3681 node.parentNode = this; 3682 var childAdded = new enchant.Event('childadded'); 3683 childAdded.node = node; 3684 childAdded.next = null; 3685 this.dispatchEvent(childAdded); 3686 node.dispatchEvent(new enchant.Event('added')); 3687 if (this.scene) { 3688 node.scene = this.scene; 3689 var addedToScene = new enchant.Event('addedtoscene'); 3690 node.dispatchEvent(addedToScene); 3691 } 3692 }, 3693 /** 3694 * GroupにNodeを挿入する. 3695 * @param {enchant.Node} node 挿入するNode. 3696 * @param {enchant.Node} reference 挿入位置の前にあるNode. 3697 */ 3698 insertBefore: function(node, reference) { 3699 if (node.parentNode) { 3700 node.parentNode.removeChild(node); 3701 } 3702 var i = this.childNodes.indexOf(reference); 3703 if (i !== -1) { 3704 this.childNodes.splice(i, 0, node); 3705 node.parentNode = this; 3706 var childAdded = new enchant.Event('childadded'); 3707 childAdded.node = node; 3708 childAdded.next = reference; 3709 this.dispatchEvent(childAdded); 3710 node.dispatchEvent(new enchant.Event('added')); 3711 if (this.scene) { 3712 node.scene = this.scene; 3713 var addedToScene = new enchant.Event('addedtoscene'); 3714 node.dispatchEvent(addedToScene); 3715 } 3716 } else { 3717 this.addChild(node); 3718 } 3719 }, 3720 /** 3721 * GroupからNodeを削除する. 3722 * @param {enchant.Node} node 削除するNode. 3723 */ 3724 removeChild: function(node) { 3725 var i; 3726 if ((i = this.childNodes.indexOf(node)) !== -1) { 3727 this.childNodes.splice(i, 1); 3728 node.parentNode = null; 3729 var childRemoved = new enchant.Event('childremoved'); 3730 childRemoved.node = node; 3731 this.dispatchEvent(childRemoved); 3732 node.dispatchEvent(new enchant.Event('removed')); 3733 if (this.scene) { 3734 node.scene = null; 3735 var removedFromScene = new enchant.Event('removedfromscene'); 3736 node.dispatchEvent(removedFromScene); 3737 } 3738 } 3739 }, 3740 /** 3741 * 最初の子Node. 3742 * @type enchant.Node 3743 */ 3744 firstChild: { 3745 get: function() { 3746 return this.childNodes[0]; 3747 } 3748 }, 3749 /** 3750 * 最後の子Node. 3751 * @type enchant.Node 3752 */ 3753 lastChild: { 3754 get: function() { 3755 return this.childNodes[this.childNodes.length - 1]; 3756 } 3757 }, 3758 /** 3759 * Groupの回転角 (度数法). 3760 * @type Number 3761 */ 3762 rotation: { 3763 get: function() { 3764 return this._rotation; 3765 }, 3766 set: function(rotation) { 3767 if(this._rotation !== rotation) { 3768 this._rotation = rotation; 3769 this._dirty = true; 3770 } 3771 } 3772 }, 3773 /** 3774 * Groupのx軸方向の倍率. 3775 * @type Number 3776 * @see enchant.Group#originX 3777 * @see enchant.Group#originY 3778 */ 3779 scaleX: { 3780 get: function() { 3781 return this._scaleX; 3782 }, 3783 set: function(scale) { 3784 if(this._scaleX !== scale) { 3785 this._scaleX = scale; 3786 this._dirty = true; 3787 } 3788 } 3789 }, 3790 /** 3791 * Groupのy軸方向の倍率. 3792 * @type Number 3793 * @see enchant.Group#originX 3794 * @see enchant.Group#originY 3795 */ 3796 scaleY: { 3797 get: function() { 3798 return this._scaleY; 3799 }, 3800 set: function(scale) { 3801 if(this._scaleY !== scale) { 3802 this._scaleY = scale; 3803 this._dirty = true; 3804 } 3805 } 3806 }, 3807 /** 3808 * 回転・拡大縮小の基準点のX座標 3809 * @type Number 3810 */ 3811 originX: { 3812 get: function() { 3813 return this._originX; 3814 }, 3815 set: function(originX) { 3816 if(this._originX !== originX) { 3817 this._originX = originX; 3818 this._dirty = true; 3819 } 3820 } 3821 }, 3822 /** 3823 * 回転・拡大縮小の基準点のX座標 3824 * @type Number 3825 */ 3826 originY: { 3827 get: function() { 3828 return this._originY; 3829 }, 3830 set: function(originY) { 3831 if(this._originY !== originY) { 3832 this._originY = originY; 3833 this._dirty = true; 3834 } 3835 } 3836 }, 3837 /**#nocode+*/ 3838 _dirty: { 3839 get: function() { 3840 return this.__dirty; 3841 }, 3842 set: function(dirty) { 3843 dirty = !!dirty; 3844 this.__dirty = dirty; 3845 if (dirty) { 3846 for (var i = 0, l = this.childNodes.length; i < l; i++) { 3847 this.childNodes[i]._dirty = true; 3848 } 3849 } 3850 } 3851 } 3852 /**#nocode-*/ 3853 }); 3854 3855 enchant.Matrix = enchant.Class.create({ 3856 initialize: function() { 3857 this.reset(); 3858 }, 3859 reset: function() { 3860 this.stack = []; 3861 this.stack.push([ 1, 0, 0, 1, 0, 0 ]); 3862 }, 3863 makeTransformMatrix: function(node, dest) { 3864 var x = node._x; 3865 var y = node._y; 3866 var width = node.width || 0; 3867 var height = node.height || 0; 3868 var rotation = node._rotation || 0; 3869 var scaleX = (typeof node._scaleX === 'number') ? node._scaleX : 1; 3870 var scaleY = (typeof node._scaleY === 'number') ? node._scaleY : 1; 3871 var theta = rotation * Math.PI / 180; 3872 var tmpcos = Math.cos(theta); 3873 var tmpsin = Math.sin(theta); 3874 var w = (typeof node._originX === 'number') ? node._originX : width / 2; 3875 var h = (typeof node._originY === 'number') ? node._originY : height / 2; 3876 var a = scaleX * tmpcos; 3877 var b = scaleX * tmpsin; 3878 var c = scaleY * tmpsin; 3879 var d = scaleY * tmpcos; 3880 dest[0] = a; 3881 dest[1] = b; 3882 dest[2] = -c; 3883 dest[3] = d; 3884 dest[4] = (-a * w + c * h + x + w); 3885 dest[5] = (-b * w - d * h + y + h); 3886 }, 3887 multiply: function(m1, m2, dest) { 3888 var a11 = m1[0], a21 = m1[2], adx = m1[4], 3889 a12 = m1[1], a22 = m1[3], ady = m1[5]; 3890 var b11 = m2[0], b21 = m2[2], bdx = m2[4], 3891 b12 = m2[1], b22 = m2[3], bdy = m2[5]; 3892 3893 dest[0] = a11 * b11 + a21 * b12; 3894 dest[1] = a12 * b11 + a22 * b12; 3895 dest[2] = a11 * b21 + a21 * b22; 3896 dest[3] = a12 * b21 + a22 * b22; 3897 dest[4] = a11 * bdx + a21 * bdy + adx; 3898 dest[5] = a12 * bdx + a22 * bdy + ady; 3899 }, 3900 multiplyVec: function(mat, vec, dest) { 3901 var x = vec[0], y = vec[1]; 3902 var m11 = mat[0], m21 = mat[2], mdx = mat[4], 3903 m12 = mat[1], m22 = mat[3], mdy = mat[5]; 3904 dest[0] = m11 * x + m21 * y + mdx; 3905 dest[1] = m12 * x + m22 * y + mdy; 3906 } 3907 }); 3908 enchant.Matrix.instance = new enchant.Matrix(); 3909 3910 enchant.DetectColorManager = enchant.Class.create({ 3911 initialize: function(reso, max) { 3912 this.reference = []; 3913 this.colorResolution = reso || 16; 3914 this.max = max || 1; 3915 this.capacity = Math.pow(this.colorResolution, 3); 3916 for (var i = 1, l = this.capacity; i < l; i++) { 3917 this.reference[i] = null; 3918 } 3919 }, 3920 attachDetectColor: function(sprite) { 3921 var i = this.reference.indexOf(null); 3922 if (i === -1) { 3923 i = 1; 3924 } 3925 this.reference[i] = sprite; 3926 return this._getColor(i); 3927 }, 3928 detachDetectColor: function(sprite) { 3929 var i = this.reference.indexOf(sprite); 3930 if (i !== -1) { 3931 this.reference[i] = null; 3932 } 3933 }, 3934 _getColor: function(n) { 3935 var C = this.colorResolution; 3936 var d = C / this.max; 3937 return [ 3938 parseInt((n / C / C) % C, 10) / d, 3939 parseInt((n / C) % C, 10) / d, 3940 parseInt(n % C, 10) / d, 3941 1.0 3942 ]; 3943 }, 3944 _decodeDetectColor: function(color, i) { 3945 i = i || 0; 3946 var C = this.colorResolution; 3947 return ~~(color[i] * C * C * C / 256) + 3948 ~~(color[i + 1] * C * C / 256) + 3949 ~~(color[i + 2] * C / 256); 3950 }, 3951 getSpriteByColor: function(color) { 3952 return this.reference[this._decodeDetectColor(color)]; 3953 }, 3954 getSpriteByColors: function(rgba) { 3955 var i, l, id, result, 3956 score = 0, 3957 found = {}; 3958 3959 for (i = 0, l = rgba.length; i < l; i+= 4) { 3960 id = this._decodeDetectColor(rgba, i); 3961 found[id] = (found[id] || 0) + 1; 3962 } 3963 for (id in found) { 3964 if (found[id] > score) { 3965 score = found[id]; 3966 result = id; 3967 } 3968 } 3969 3970 return this.reference[result]; 3971 } 3972 }); 3973 3974 enchant.DomManager = enchant.Class.create({ 3975 initialize: function(node, elementDefinition) { 3976 var core = enchant.Core.instance; 3977 this.layer = null; 3978 this.targetNode = node; 3979 if (typeof elementDefinition === 'string') { 3980 this.element = document.createElement(elementDefinition); 3981 } else if (elementDefinition instanceof HTMLElement) { 3982 this.element = elementDefinition; 3983 } 3984 this.style = this.element.style; 3985 this.style.position = 'absolute'; 3986 this.style[enchant.ENV.VENDOR_PREFIX + 'TransformOrigin'] = '0px 0px'; 3987 if (core._debug) { 3988 this.style.border = '1px solid blue'; 3989 this.style.margin = '-1px'; 3990 } 3991 3992 var manager = this; 3993 this._setDomTarget = function() { 3994 manager.layer._touchEventTarget = manager.targetNode; 3995 }; 3996 this._attachEvent(); 3997 }, 3998 getDomElement: function() { 3999 return this.element; 4000 }, 4001 getDomElementAsNext: function() { 4002 return this.element; 4003 }, 4004 getNextManager: function(manager) { 4005 var i = this.targetNode.parentNode.childNodes.indexOf(manager.targetNode); 4006 if (i !== this.targetNode.parentNode.childNodes.length - 1) { 4007 return this.targetNode.parentNode.childNodes[i + 1]._domManager; 4008 } else { 4009 return null; 4010 } 4011 }, 4012 addManager: function(childManager, nextManager) { 4013 var nextElement; 4014 if (nextManager) { 4015 nextElement = nextManager.getDomElementAsNext(); 4016 } 4017 var element = childManager.getDomElement(); 4018 if (element instanceof Array) { 4019 element.forEach(function(child) { 4020 if (nextElement) { 4021 this.element.insertBefore(child, nextElement); 4022 } else { 4023 this.element.appendChild(child); 4024 } 4025 }, this); 4026 } else { 4027 if (nextElement) { 4028 this.element.insertBefore(element, nextElement); 4029 } else { 4030 this.element.appendChild(element); 4031 } 4032 } 4033 this.setLayer(this.layer); 4034 }, 4035 removeManager: function(childManager) { 4036 if (childManager instanceof enchant.DomlessManager) { 4037 childManager._domRef.forEach(function(element) { 4038 this.element.removeChild(element); 4039 }, this); 4040 } else { 4041 this.element.removeChild(childManager.element); 4042 } 4043 this.setLayer(this.layer); 4044 }, 4045 setLayer: function(layer) { 4046 this.layer = layer; 4047 var node = this.targetNode; 4048 var manager; 4049 if (node.childNodes) { 4050 for (var i = 0, l = node.childNodes.length; i < l; i++) { 4051 manager = node.childNodes[i]._domManager; 4052 if (manager) { 4053 manager.setLayer(layer); 4054 } 4055 } 4056 } 4057 }, 4058 render: function(inheritMat) { 4059 var node = this.targetNode; 4060 var matrix = enchant.Matrix.instance; 4061 var stack = matrix.stack; 4062 var dest = []; 4063 matrix.makeTransformMatrix(node, dest); 4064 matrix.multiply(stack[stack.length - 1], dest, dest); 4065 matrix.multiply(inheritMat, dest, inheritMat); 4066 node._matrix = inheritMat; 4067 var ox = (typeof node._originX === 'number') ? node._originX : node.width / 2 || 0; 4068 var oy = (typeof node._originY === 'number') ? node._originY : node.height / 2 || 0; 4069 var vec = [ ox, oy ]; 4070 matrix.multiplyVec(dest, vec, vec); 4071 4072 node._offsetX = vec[0] - ox; 4073 node._offsetY = vec[1] - oy; 4074 if(node.parentNode && !(node.parentNode instanceof enchant.Group)) { 4075 node._offsetX += node.parentNode._offsetX; 4076 node._offsetY += node.parentNode._offsetY; 4077 } 4078 if (node._dirty) { 4079 this.style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'matrix(' + 4080 dest[0].toFixed(10) + ',' + 4081 dest[1].toFixed(10) + ',' + 4082 dest[2].toFixed(10) + ',' + 4083 dest[3].toFixed(10) + ',' + 4084 dest[4].toFixed(10) + ',' + 4085 dest[5].toFixed(10) + 4086 ')'; 4087 } 4088 this.domRender(); 4089 }, 4090 domRender: function() { 4091 var node = this.targetNode; 4092 if(!node._style) { 4093 node._style = {}; 4094 } 4095 if(!node.__styleStatus) { 4096 node.__styleStatus = {}; 4097 } 4098 if (node.width !== null) { 4099 node._style.width = node.width + 'px'; 4100 } 4101 if (node.height !== null) { 4102 node._style.height = node.height + 'px'; 4103 } 4104 node._style.opacity = node._opacity; 4105 node._style['background-color'] = node._backgroundColor; 4106 if (typeof node._visible !== 'undefined') { 4107 node._style.display = node._visible ? 'block' : 'none'; 4108 } 4109 if (typeof node.domRender === 'function') { 4110 node.domRender(this.element); 4111 } 4112 var value; 4113 for (var prop in node._style) { 4114 value = node._style[prop]; 4115 if(node.__styleStatus[prop] !== value && value != null) { 4116 this.style.setProperty(prop, '' + value); 4117 node.__styleStatus[prop] = value; 4118 } 4119 } 4120 }, 4121 _attachEvent: function() { 4122 if (enchant.ENV.TOUCH_ENABLED) { 4123 this.element.addEventListener('touchstart', this._setDomTarget, true); 4124 } 4125 this.element.addEventListener('mousedown', this._setDomTarget, true); 4126 }, 4127 _detachEvent: function() { 4128 if (enchant.ENV.TOUCH_ENABLED) { 4129 this.element.removeEventListener('touchstart', this._setDomTarget, true); 4130 } 4131 this.element.removeEventListener('mousedown', this._setDomTarget, true); 4132 }, 4133 remove: function() { 4134 this._detachEvent(); 4135 this.element = this.style = this.targetNode = null; 4136 } 4137 }); 4138 4139 enchant.DomlessManager = enchant.Class.create({ 4140 initialize: function(node) { 4141 this._domRef = []; 4142 this.targetNode = node; 4143 }, 4144 _register: function(element, nextElement) { 4145 var i = this._domRef.indexOf(nextElement); 4146 if (element instanceof Array) { 4147 if (i === -1) { 4148 Array.prototype.push.apply(this._domRef, element); 4149 } else { 4150 Array.prototype.splice.apply(this._domRef, [i, 0].concat(element)); 4151 } 4152 } else { 4153 if (i === -1) { 4154 this._domRef.push(element); 4155 } else { 4156 this._domRef.splice(i, 0, element); 4157 } 4158 } 4159 }, 4160 getNextManager: function(manager) { 4161 var i = this.targetNode.parentNode.childNodes.indexOf(manager.targetNode); 4162 if (i !== this.targetNode.parentNode.childNodes.length - 1) { 4163 return this.targetNode.parentNode.childNodes[i + 1]._domManager; 4164 } else { 4165 return null; 4166 } 4167 }, 4168 getDomElement: function() { 4169 var ret = []; 4170 this.targetNode.childNodes.forEach(function(child) { 4171 ret = ret.concat(child._domManager.getDomElement()); 4172 }); 4173 return ret; 4174 }, 4175 getDomElementAsNext: function() { 4176 if (this._domRef.length) { 4177 return this._domRef[0]; 4178 } else { 4179 var nextManager = this.getNextManager(this); 4180 if (nextManager) { 4181 return nextManager.element; 4182 } else { 4183 return null; 4184 } 4185 } 4186 }, 4187 addManager: function(childManager, nextManager) { 4188 var parentNode = this.targetNode.parentNode; 4189 if (parentNode) { 4190 if (nextManager === null) { 4191 nextManager = this.getNextManager(this); 4192 } 4193 if (parentNode instanceof enchant.Scene) { 4194 parentNode._layers.Dom._domManager.addManager(childManager, nextManager); 4195 } else { 4196 parentNode._domManager.addManager(childManager, nextManager); 4197 } 4198 } 4199 var nextElement = nextManager ? nextManager.getDomElementAsNext() : null; 4200 this._register(childManager.getDomElement(), nextElement); 4201 this.setLayer(this.layer); 4202 }, 4203 removeManager: function(childManager) { 4204 var dom; 4205 var i = this._domRef.indexOf(childManager.element); 4206 if (i !== -1) { 4207 dom = this._domRef[i]; 4208 dom.parentNode.removeChild(dom); 4209 this._domRef.splice(i, 1); 4210 } 4211 this.setLayer(this.layer); 4212 }, 4213 setLayer: function(layer) { 4214 this.layer = layer; 4215 var node = this.targetNode; 4216 var manager; 4217 if (node.childNodes) { 4218 for (var i = 0, l = node.childNodes.length; i < l; i++) { 4219 manager = node.childNodes[i]._domManager; 4220 if (manager) { 4221 manager.setLayer(layer); 4222 } 4223 } 4224 } 4225 }, 4226 render: function(inheritMat) { 4227 var matrix = enchant.Matrix.instance; 4228 var stack = matrix.stack; 4229 var node = this.targetNode; 4230 var dest = []; 4231 matrix.makeTransformMatrix(node, dest); 4232 matrix.multiply(stack[stack.length - 1], dest, dest); 4233 matrix.multiply(inheritMat, dest, inheritMat); 4234 node._matrix = inheritMat; 4235 var ox = (typeof node._originX === 'number') ? node._originX : node.width / 2 || 0; 4236 var oy = (typeof node._originY === 'number') ? node._originY : node.height / 2 || 0; 4237 var vec = [ ox, oy ]; 4238 matrix.multiplyVec(dest, vec, vec); 4239 node._offsetX = vec[0] - ox; 4240 node._offsetY = vec[1] - oy; 4241 stack.push(dest); 4242 }, 4243 remove: function() { 4244 this._domRef = []; 4245 this.targetNode = null; 4246 } 4247 }); 4248 4249 enchant.DomLayer = enchant.Class.create(enchant.Group, { 4250 initialize: function() { 4251 var core = enchant.Core.instance; 4252 enchant.Group.call(this); 4253 4254 this._touchEventTarget = null; 4255 4256 this._element = document.createElement('div'); 4257 this._element.style.position = 'absolute'; 4258 4259 this._domManager = new enchant.DomManager(this, this._element); 4260 this._domManager.layer = this; 4261 4262 this.width = core.width; 4263 this.height = core.height; 4264 4265 var touch = [ 4266 enchant.Event.TOUCH_START, 4267 enchant.Event.TOUCH_MOVE, 4268 enchant.Event.TOUCH_END 4269 ]; 4270 4271 touch.forEach(function(type) { 4272 this.addEventListener(type, function(e) { 4273 if (this._scene) { 4274 this._scene.dispatchEvent(e); 4275 } 4276 }); 4277 }, this); 4278 4279 var __onchildadded = function(e) { 4280 var child = e.node; 4281 var next = e.next; 4282 var self = e.target; 4283 var nextManager = next ? next._domManager : null; 4284 enchant.DomLayer._attachDomManager(child, __onchildadded, __onchildremoved); 4285 self._domManager.addManager(child._domManager, nextManager); 4286 var render = new enchant.Event(enchant.Event.RENDER); 4287 child._dirty = true; 4288 self._domManager.layer._rendering(child, render); 4289 }; 4290 4291 var __onchildremoved = function(e) { 4292 var child = e.node; 4293 var self = e.target; 4294 self._domManager.removeManager(child._domManager); 4295 enchant.DomLayer._detachDomManager(child, __onchildadded, __onchildremoved); 4296 }; 4297 4298 this.addEventListener('childremoved', __onchildremoved); 4299 this.addEventListener('childadded', __onchildadded); 4300 4301 }, 4302 width: { 4303 get: function() { 4304 return this._width; 4305 }, 4306 set: function(width) { 4307 this._width = width; 4308 this._element.style.width = width + 'px'; 4309 } 4310 }, 4311 height: { 4312 get: function() { 4313 return this._height; 4314 }, 4315 set: function(height) { 4316 this._height = height; 4317 this._element.style.height = height + 'px'; 4318 } 4319 }, 4320 addChild: function(node) { 4321 this.childNodes.push(node); 4322 node.parentNode = this; 4323 var childAdded = new enchant.Event('childadded'); 4324 childAdded.node = node; 4325 childAdded.next = null; 4326 this.dispatchEvent(childAdded); 4327 node.dispatchEvent(new enchant.Event('added')); 4328 if (this.scene) { 4329 node.scene = this.scene; 4330 var addedToScene = new enchant.Event('addedtoscene'); 4331 node.dispatchEvent(addedToScene); 4332 } 4333 }, 4334 insertBefore: function(node, reference) { 4335 var i = this.childNodes.indexOf(reference); 4336 if (i !== -1) { 4337 this.childNodes.splice(i, 0, node); 4338 node.parentNode = this; 4339 var childAdded = new enchant.Event('childadded'); 4340 childAdded.node = node; 4341 childAdded.next = reference; 4342 this.dispatchEvent(childAdded); 4343 node.dispatchEvent(new enchant.Event('added')); 4344 if (this.scene) { 4345 node.scene = this.scene; 4346 var addedToScene = new enchant.Event('addedtoscene'); 4347 node.dispatchEvent(addedToScene); 4348 } 4349 } else { 4350 this.addChild(node); 4351 } 4352 }, 4353 _startRendering: function() { 4354 this.addEventListener('exitframe', this._onexitframe); 4355 this._onexitframe(); 4356 }, 4357 _stopRendering: function() { 4358 this.removeEventListener('exitframe', this._onexitframe); 4359 this._onexitframe(); 4360 }, 4361 _onexitframe: function() { 4362 this._rendering(this, new enchant.Event(enchant.Event.RENDER)); 4363 }, 4364 _rendering: function(node, e, inheritMat) { 4365 var child; 4366 if (!inheritMat) { 4367 inheritMat = [ 1, 0, 0, 1, 0, 0 ]; 4368 } 4369 node.dispatchEvent(e); 4370 node._domManager.render(inheritMat); 4371 if (node.childNodes) { 4372 for (var i = 0, l = node.childNodes.length; i < l; i++) { 4373 child = node.childNodes[i]; 4374 this._rendering(child, e, inheritMat.slice()); 4375 } 4376 } 4377 if (node._domManager instanceof enchant.DomlessManager) { 4378 enchant.Matrix.instance.stack.pop(); 4379 } 4380 node._dirty = false; 4381 }, 4382 _determineEventTarget: function() { 4383 var target = this._touchEventTarget; 4384 this._touchEventTarget = null; 4385 return (target === this) ? null : target; 4386 } 4387 }); 4388 4389 enchant.DomLayer._attachDomManager = function(node, onchildadded, onchildremoved) { 4390 var child; 4391 if (!node._domManager) { 4392 node.addEventListener('childadded', onchildadded); 4393 node.addEventListener('childremoved', onchildremoved); 4394 if (node instanceof enchant.Group) { 4395 node._domManager = new enchant.DomlessManager(node); 4396 } else { 4397 if (node._element) { 4398 node._domManager = new enchant.DomManager(node, node._element); 4399 } else { 4400 node._domManager = new enchant.DomManager(node, 'div'); 4401 } 4402 } 4403 } 4404 if (node.childNodes) { 4405 for (var i = 0, l = node.childNodes.length; i < l; i++) { 4406 child = node.childNodes[i]; 4407 enchant.DomLayer._attachDomManager(child, onchildadded, onchildremoved); 4408 node._domManager.addManager(child._domManager, null); 4409 } 4410 } 4411 }; 4412 4413 enchant.DomLayer._detachDomManager = function(node, onchildadded, onchildremoved) { 4414 var child; 4415 node.removeEventListener('childadded', onchildadded); 4416 node.removeEventListener('childremoved', onchildremoved); 4417 if (node.childNodes) { 4418 for (var i = 0, l = node.childNodes.length; i < l; i++) { 4419 child = node.childNodes[i]; 4420 node._domManager.removeManager(child._domManager, null); 4421 enchant.DomLayer._detachDomManager(child, onchildadded, onchildremoved); 4422 } 4423 } 4424 node._domManager.remove(); 4425 delete node._domManager; 4426 }; 4427 4428 /** 4429 * @scope enchant.CanvasLayer.prototype 4430 */ 4431 enchant.CanvasLayer = enchant.Class.create(enchant.Group, { 4432 /** 4433 * @name enchant.CanvasLayer 4434 * @class 4435 * Canvas を用いた描画を行うクラス. 4436 * 子を Canvas を用いた描画に切り替えるクラス. 4437 * @constructs 4438 * @extends enchant.Group 4439 */ 4440 initialize: function() { 4441 var core = enchant.Core.instance; 4442 4443 enchant.Group.call(this); 4444 4445 this._cvsCache = { 4446 matrix: [1, 0, 0, 1, 0, 0], 4447 detectColor: '#000000' 4448 }; 4449 this._cvsCache.layer = this; 4450 4451 this._element = document.createElement('canvas'); 4452 this._element.style.position = 'absolute'; 4453 // issue 179 4454 this._element.style.left = this._element.style.top = '0px'; 4455 4456 this._detect = document.createElement('canvas'); 4457 this._detect.style.position = 'absolute'; 4458 this._lastDetected = 0; 4459 4460 this.context = this._element.getContext('2d'); 4461 this._dctx = this._detect.getContext('2d'); 4462 this._setImageSmoothingEnable(); 4463 4464 this._colorManager = new enchant.DetectColorManager(16, 256); 4465 4466 this.width = core.width; 4467 this.height = core.height; 4468 4469 var touch = [ 4470 enchant.Event.TOUCH_START, 4471 enchant.Event.TOUCH_MOVE, 4472 enchant.Event.TOUCH_END 4473 ]; 4474 4475 touch.forEach(function(type) { 4476 this.addEventListener(type, function(e) { 4477 if (this._scene) { 4478 this._scene.dispatchEvent(e); 4479 } 4480 }); 4481 }, this); 4482 4483 var __onchildadded = function(e) { 4484 var child = e.node; 4485 var self = e.target; 4486 var layer; 4487 if (self instanceof enchant.CanvasLayer) { 4488 layer = self._scene._layers.Canvas; 4489 } else { 4490 layer = self.scene._layers.Canvas; 4491 } 4492 enchant.CanvasLayer._attachCache(child, layer, __onchildadded, __onchildremoved); 4493 var render = new enchant.Event(enchant.Event.RENDER); 4494 if (self._dirty) { 4495 self._updateCoordinate(); 4496 } 4497 child._dirty = true; 4498 enchant.Matrix.instance.stack.push(self._matrix); 4499 enchant.CanvasRenderer.instance.render(layer.context, child, render); 4500 enchant.Matrix.instance.stack.pop(self._matrix); 4501 }; 4502 4503 var __onchildremoved = function(e) { 4504 var child = e.node; 4505 var self = e.target; 4506 var layer; 4507 if (self instanceof enchant.CanvasLayer) { 4508 layer = self._scene._layers.Canvas; 4509 } else { 4510 layer = self.scene._layers.Canvas; 4511 } 4512 enchant.CanvasLayer._detachCache(child, layer, __onchildadded, __onchildremoved); 4513 }; 4514 4515 this.addEventListener('childremoved', __onchildremoved); 4516 this.addEventListener('childadded', __onchildadded); 4517 4518 }, 4519 /** 4520 * CanvasLayerの横幅. 4521 * @type Number 4522 */ 4523 width: { 4524 get: function() { 4525 return this._width; 4526 }, 4527 set: function(width) { 4528 this._width = width; 4529 this._element.width = this._detect.width = width; 4530 this._setImageSmoothingEnable(); 4531 } 4532 }, 4533 /** 4534 * CanvasLayerの高さ. 4535 * @type Number 4536 */ 4537 height: { 4538 get: function() { 4539 return this._height; 4540 }, 4541 set: function(height) { 4542 this._height = height; 4543 this._element.height = this._detect.height = height; 4544 this._setImageSmoothingEnable(); 4545 } 4546 }, 4547 addChild: function(node) { 4548 this.childNodes.push(node); 4549 node.parentNode = this; 4550 var childAdded = new enchant.Event('childadded'); 4551 childAdded.node = node; 4552 childAdded.next = null; 4553 this.dispatchEvent(childAdded); 4554 node.dispatchEvent(new enchant.Event('added')); 4555 }, 4556 insertBefore: function(node, reference) { 4557 var i = this.childNodes.indexOf(reference); 4558 if (i !== -1) { 4559 this.childNodes.splice(i, 0, node); 4560 node.parentNode = this; 4561 var childAdded = new enchant.Event('childadded'); 4562 childAdded.node = node; 4563 childAdded.next = reference; 4564 this.dispatchEvent(childAdded); 4565 node.dispatchEvent(new enchant.Event('added')); 4566 } else { 4567 this.addChild(node); 4568 } 4569 }, 4570 /** 4571 * レンダリングを開始する. 4572 * @private 4573 */ 4574 _startRendering: function() { 4575 this.addEventListener('exitframe', this._onexitframe); 4576 this._onexitframe(); 4577 }, 4578 /** 4579 * レンダリングを停止する. 4580 * @private 4581 */ 4582 _stopRendering: function() { 4583 this.removeEventListener('exitframe', this._onexitframe); 4584 this._onexitframe(); 4585 }, 4586 _onexitframe: function() { 4587 var core = enchant.Core.instance; 4588 var ctx = this.context; 4589 ctx.clearRect(0, 0, core.width, core.height); 4590 var render = new enchant.Event(enchant.Event.RENDER); 4591 enchant.CanvasRenderer.instance.render(ctx, this, render); 4592 }, 4593 _determineEventTarget: function(e) { 4594 return this._getEntityByPosition(e.x, e.y); 4595 }, 4596 _getEntityByPosition: function(x, y) { 4597 var core = enchant.Core.instance; 4598 var ctx = this._dctx; 4599 if (this._lastDetected < core.frame) { 4600 ctx.clearRect(0, 0, this.width, this.height); 4601 enchant.CanvasRenderer.instance.detectRender(ctx, this); 4602 this._lastDetected = core.frame; 4603 } 4604 var extra = enchant.ENV.COLOR_DETECTION_LEVEL - 1; 4605 var rgba = ctx.getImageData(x - extra, y - extra, 1 + extra * 2, 1 + extra * 2).data; 4606 return this._colorManager.getSpriteByColors(rgba); 4607 }, 4608 _setImageSmoothingEnable: function() { 4609 this._dctx.imageSmoothingEnabled = 4610 this._dctx.msImageSmoothingEnabled = 4611 this._dctx.mozImageSmoothingEnabled = 4612 this._dctx.webkitImageSmoothingEnabled = false; 4613 } 4614 }); 4615 4616 enchant.CanvasLayer._attachCache = function(node, layer, onchildadded, onchildremoved) { 4617 var child; 4618 if (!node._cvsCache) { 4619 node._cvsCache = {}; 4620 node._cvsCache.matrix = [ 1, 0, 0, 1, 0, 0 ]; 4621 node._cvsCache.detectColor = 'rgba(' + layer._colorManager.attachDetectColor(node) + ')'; 4622 node.addEventListener('childadded', onchildadded); 4623 node.addEventListener('childremoved', onchildremoved); 4624 } 4625 if (node.childNodes) { 4626 for (var i = 0, l = node.childNodes.length; i < l; i++) { 4627 child = node.childNodes[i]; 4628 enchant.CanvasLayer._attachCache(child, layer, onchildadded, onchildremoved); 4629 } 4630 } 4631 }; 4632 4633 enchant.CanvasLayer._detachCache = function(node, layer, onchildadded, onchildremoved) { 4634 var child; 4635 if (node._cvsCache) { 4636 layer._colorManager.detachDetectColor(node); 4637 node.removeEventListener('childadded', onchildadded); 4638 node.removeEventListener('childremoved', onchildremoved); 4639 delete node._cvsCache; 4640 } 4641 if (node.childNodes) { 4642 for (var i = 0, l = node.childNodes.length; i < l; i++) { 4643 child = node.childNodes[i]; 4644 enchant.CanvasLayer._detachCache(child, layer, onchildadded, onchildremoved); 4645 } 4646 } 4647 }; 4648 4649 enchant.CanvasRenderer = enchant.Class.create({ 4650 render: function(ctx, node, e) { 4651 var width, height, child; 4652 ctx.save(); 4653 node.dispatchEvent(e); 4654 // transform 4655 this.transform(ctx, node); 4656 if (typeof node._visible === 'undefined' || node._visible) { 4657 width = node.width; 4658 height = node.height; 4659 // composite 4660 if (node.compositeOperation) { 4661 ctx.globalCompositeOperation = node.compositeOperation; 4662 } 4663 ctx.globalAlpha = (typeof node._opacity === 'number') ? node._opacity : 1.0; 4664 // render 4665 if (node._backgroundColor) { 4666 ctx.fillStyle = node._backgroundColor; 4667 ctx.fillRect(0, 0, width, height); 4668 } 4669 4670 if (node.cvsRender) { 4671 node.cvsRender(ctx); 4672 } 4673 4674 if (enchant.Core.instance._debug && node._debugColor) { 4675 ctx.strokeStyle = node._debugColor; 4676 ctx.strokeRect(0, 0, width, height); 4677 } 4678 if (node._clipping) { 4679 ctx.beginPath(); 4680 ctx.rect(0, 0, width, height); 4681 ctx.clip(); 4682 } 4683 if (node.childNodes) { 4684 for (var i = 0, l = node.childNodes.length; i < l; i++) { 4685 child = node.childNodes[i]; 4686 this.render(ctx, child, e); 4687 } 4688 } 4689 } 4690 ctx.restore(); 4691 enchant.Matrix.instance.stack.pop(); 4692 }, 4693 detectRender: function(ctx, node) { 4694 var width, height, child; 4695 if (typeof node._visible === 'undefined' || node._visible) { 4696 width = node.width; 4697 height = node.height; 4698 ctx.save(); 4699 this.transform(ctx, node); 4700 ctx.fillStyle = node._cvsCache.detectColor; 4701 if (node._touchEnabled) { 4702 if (node.detectRender) { 4703 node.detectRender(ctx); 4704 } else { 4705 ctx.fillRect(0, 0, width, height); 4706 } 4707 } 4708 if (node._clipping) { 4709 ctx.beginPath(); 4710 ctx.rect(0, 0, width, height); 4711 ctx.clip(); 4712 } 4713 if (node.childNodes) { 4714 for (var i = 0, l = node.childNodes.length; i < l; i++) { 4715 child = node.childNodes[i]; 4716 this.detectRender(ctx, child); 4717 } 4718 } 4719 ctx.restore(); 4720 enchant.Matrix.instance.stack.pop(); 4721 } 4722 }, 4723 transform: function(ctx, node) { 4724 var matrix = enchant.Matrix.instance; 4725 var stack = matrix.stack; 4726 var newmat, ox, oy, vec; 4727 if (node._dirty) { 4728 matrix.makeTransformMatrix(node, node._cvsCache.matrix); 4729 newmat = []; 4730 matrix.multiply(stack[stack.length - 1], node._cvsCache.matrix, newmat); 4731 node._matrix = newmat; 4732 ox = (typeof node._originX === 'number') ? node._originX : node._width / 2 || 0; 4733 oy = (typeof node._originY === 'number') ? node._originY : node._height / 2 || 0; 4734 vec = [ ox, oy ]; 4735 matrix.multiplyVec(newmat, vec, vec); 4736 node._offsetX = vec[0] - ox; 4737 node._offsetY = vec[1] - oy; 4738 node._dirty = false; 4739 } else { 4740 newmat = node._matrix; 4741 } 4742 stack.push(newmat); 4743 ctx.setTransform.apply(ctx, newmat); 4744 } 4745 }); 4746 enchant.CanvasRenderer.instance = new enchant.CanvasRenderer(); 4747 4748 /** 4749 * @scope enchant.Scene.prototype 4750 */ 4751 enchant.Scene = enchant.Class.create(enchant.Group, { 4752 /** 4753 * @name enchant.Scene 4754 * @class 4755 * 表示オブジェクトツリーのルートになるクラス. 4756 * シーンはレイヤーを持っていて, 子として追加されたオブジェクト ({@link Entity}) は描画方法に応じてレイヤーに振り分けられる. 4757 * Scene クラスは最も汎用的なシーンの実装で, ({@link enchant.DOMLayer} と {@link enchant.CanvasLayer}) を持っており, 4758 * それぞれ DOM, Canvas を用いて描画される. 描画順は DOM が手前, Canvas が奥で, 4759 * 各レイヤーの間では新しく追加されたオブジェクトほど手前に表示される. 4760 * Scene クラスを継承することで, 新しい種類の Layer を持つシーンクラスを作ることができる. 4761 * 4762 * @example 4763 * var scene = new Scene(); 4764 * scene.addChild(player); 4765 * scene.addChild(enemy); 4766 * core.pushScene(scene); 4767 * 4768 * @constructs 4769 * @extends enchant.Group 4770 */ 4771 initialize: function() { 4772 var core = enchant.Core.instance; 4773 4774 // Call initialize method of enchant.Group 4775 enchant.Group.call(this); 4776 4777 // All nodes (entities, groups, scenes) have reference to the scene that it belongs to. 4778 this.scene = this; 4779 4780 this._backgroundColor = null; 4781 4782 // Create div tag which possesses its layers 4783 this._element = document.createElement('div'); 4784 this._element.style.position = 'absolute'; 4785 this._element.style.overflow = 'hidden'; 4786 this._element.style[enchant.ENV.VENDOR_PREFIX + 'TransformOrigin'] = '0 0'; 4787 4788 this._layers = {}; 4789 this._layerPriority = []; 4790 4791 this.addEventListener(enchant.Event.CHILD_ADDED, this._onchildadded); 4792 this.addEventListener(enchant.Event.CHILD_REMOVED, this._onchildremoved); 4793 this.addEventListener(enchant.Event.ENTER, this._onenter); 4794 this.addEventListener(enchant.Event.EXIT, this._onexit); 4795 4796 var that = this; 4797 this._dispatchExitframe = function() { 4798 var layer; 4799 for (var prop in that._layers) { 4800 layer = that._layers[prop]; 4801 layer.dispatchEvent(new enchant.Event(enchant.Event.EXIT_FRAME)); 4802 } 4803 }; 4804 4805 this.addEventListener(enchant.Event.CORE_RESIZE, this._oncoreresize); 4806 4807 this._oncoreresize(core); 4808 }, 4809 /**#nocode+*/ 4810 x: { 4811 get: function() { 4812 return this._x; 4813 }, 4814 set: function(x) { 4815 this._x = x; 4816 for (var type in this._layers) { 4817 this._layers[type].x = x; 4818 } 4819 } 4820 }, 4821 y: { 4822 get: function() { 4823 return this._y; 4824 }, 4825 set: function(y) { 4826 this._y = y; 4827 for (var type in this._layers) { 4828 this._layers[type].y = y; 4829 } 4830 } 4831 }, 4832 width: { 4833 get: function() { 4834 return this._width; 4835 }, 4836 set: function(width) { 4837 this._width = width; 4838 for (var type in this._layers) { 4839 this._layers[type].width = width; 4840 } 4841 } 4842 }, 4843 height: { 4844 get: function() { 4845 return this._height; 4846 }, 4847 set: function(height) { 4848 this._height = height; 4849 for (var type in this._layers) { 4850 this._layers[type].height = height; 4851 } 4852 } 4853 }, 4854 rotation: { 4855 get: function() { 4856 return this._rotation; 4857 }, 4858 set: function(rotation) { 4859 this._rotation = rotation; 4860 for (var type in this._layers) { 4861 this._layers[type].rotation = rotation; 4862 } 4863 } 4864 }, 4865 scaleX: { 4866 get: function() { 4867 return this._scaleX; 4868 }, 4869 set: function(scaleX) { 4870 this._scaleX = scaleX; 4871 for (var type in this._layers) { 4872 this._layers[type].scaleX = scaleX; 4873 } 4874 } 4875 }, 4876 scaleY: { 4877 get: function() { 4878 return this._scaleY; 4879 }, 4880 set: function(scaleY) { 4881 this._scaleY = scaleY; 4882 for (var type in this._layers) { 4883 this._layers[type].scaleY = scaleY; 4884 } 4885 } 4886 }, 4887 backgroundColor: { 4888 get: function() { 4889 return this._backgroundColor; 4890 }, 4891 set: function(color) { 4892 this._backgroundColor = this._element.style.backgroundColor = color; 4893 } 4894 }, 4895 remove: function() { 4896 this.clearEventListener(); 4897 4898 while (this.childNodes.length > 0) { 4899 this.childNodes[0].remove(); 4900 } 4901 4902 return enchant.Core.instance.removeScene(this); 4903 }, 4904 /**#nocode-*/ 4905 _oncoreresize: function(e) { 4906 this._element.style.width = e.width + 'px'; 4907 this.width = e.width; 4908 this._element.style.height = e.height + 'px'; 4909 this.height = e.height; 4910 this._element.style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'scale(' + e.scale + ')'; 4911 4912 for (var type in this._layers) { 4913 this._layers[type].dispatchEvent(e); 4914 } 4915 }, 4916 addLayer: function(type, i) { 4917 var core = enchant.Core.instance; 4918 if (this._layers[type]) { 4919 return; 4920 } 4921 var layer = new enchant[type + 'Layer'](); 4922 if (core.currentScene === this) { 4923 layer._startRendering(); 4924 } 4925 this._layers[type] = layer; 4926 var element = layer._element; 4927 if (typeof i === 'number') { 4928 var nextSibling = this._element.childNodes[i]; 4929 if (nextSibling) { 4930 this._element.insertBefore(element, nextSibling); 4931 } else { 4932 this._element.appendChild(element); 4933 } 4934 this._layerPriority.splice(i, 0, type); 4935 } else { 4936 this._element.appendChild(element); 4937 this._layerPriority.push(type); 4938 } 4939 layer._scene = this; 4940 }, 4941 _determineEventTarget: function(e) { 4942 var layer, target; 4943 for (var i = this._layerPriority.length - 1; i >= 0; i--) { 4944 layer = this._layers[this._layerPriority[i]]; 4945 target = layer._determineEventTarget(e); 4946 if (target) { 4947 break; 4948 } 4949 } 4950 if (!target) { 4951 target = this; 4952 } 4953 return target; 4954 }, 4955 _onchildadded: function(e) { 4956 var child = e.node; 4957 var next = e.next; 4958 var target, i; 4959 if (child._element) { 4960 target = 'Dom'; 4961 i = 1; 4962 } else { 4963 target = 'Canvas'; 4964 i = 0; 4965 } 4966 if (!this._layers[target]) { 4967 this.addLayer(target, i); 4968 } 4969 child._layer = this._layers[target]; 4970 this._layers[target].insertBefore(child, next); 4971 child.parentNode = this; 4972 }, 4973 _onchildremoved: function(e) { 4974 var child = e.node; 4975 child._layer.removeChild(child); 4976 child._layer = null; 4977 }, 4978 _onenter: function() { 4979 for (var type in this._layers) { 4980 this._layers[type]._startRendering(); 4981 } 4982 enchant.Core.instance.addEventListener('exitframe', this._dispatchExitframe); 4983 }, 4984 _onexit: function() { 4985 for (var type in this._layers) { 4986 this._layers[type]._stopRendering(); 4987 } 4988 enchant.Core.instance.removeEventListener('exitframe', this._dispatchExitframe); 4989 } 4990 }); 4991 4992 /** 4993 * @scope enchant.LoadingScene.prototype 4994 */ 4995 enchant.LoadingScene = enchant.Class.create(enchant.Scene, { 4996 /** 4997 * @name enchant.LoadingScene 4998 * @class 4999 * デフォルトのローディングシーン. ローディングアニメーションを書き換えたい場合は, 5000 * enchant.LoadingSceneを上書きする. 5001 * 5002 * @example 5003 * enchant.LoadingScene = enchant.Class.create(enchant.Scene, { 5004 * initialize: function() { 5005 * enchant.Scene.call(this); 5006 * this.backgroundColor = 'red'; 5007 * // ... 5008 * this.addEventListener('progress', function(e) { 5009 * progress = e.loaded / e.total; 5010 * }); 5011 * this.addEventListener('enterframe', function() { 5012 * // animation 5013 * }); 5014 * } 5015 * }); 5016 * @constructs 5017 * @extends enchant.Scene 5018 */ 5019 initialize: function() { 5020 enchant.Scene.call(this); 5021 this.backgroundColor = '#000'; 5022 var barWidth = this.width * 0.4 | 0; 5023 var barHeight = this.width * 0.05 | 0; 5024 var border = barWidth * 0.03 | 0; 5025 var bar = new enchant.Sprite(barWidth, barHeight); 5026 bar.disableCollection(); 5027 bar.x = (this.width - barWidth) / 2; 5028 bar.y = (this.height - barHeight) / 2; 5029 var image = new enchant.Surface(barWidth, barHeight); 5030 image.context.fillStyle = '#fff'; 5031 image.context.fillRect(0, 0, barWidth, barHeight); 5032 image.context.fillStyle = '#000'; 5033 image.context.fillRect(border, border, barWidth - border * 2, barHeight - border * 2); 5034 bar.image = image; 5035 var progress = 0, _progress = 0; 5036 this.addEventListener('progress', function(e) { 5037 // avoid #167 https://github.com/wise9/enchant.js/issues/177 5038 progress = e.loaded / e.total * 1.0; 5039 }); 5040 bar.addEventListener('enterframe', function() { 5041 _progress *= 0.9; 5042 _progress += progress * 0.1; 5043 image.context.fillStyle = '#fff'; 5044 image.context.fillRect(border, 0, (barWidth - border * 2) * _progress, barHeight); 5045 }); 5046 this.addChild(bar); 5047 this.addEventListener('load', function(e) { 5048 var core = enchant.Core.instance; 5049 core.removeScene(core.loadingScene); 5050 core.dispatchEvent(e); 5051 }); 5052 } 5053 }); 5054 5055 /** 5056 * @scope enchant.CanvasScene.prototype 5057 */ 5058 enchant.CanvasScene = enchant.Class.create(enchant.Scene, { 5059 /** 5060 * @name enchant.CanvasScene 5061 * @class 5062 * すべての子をCanvasに描画するScene. 5063 * @constructs 5064 * @extends enchant.Scene 5065 */ 5066 initialize: function() { 5067 enchant.Scene.call(this); 5068 this.addLayer('Canvas'); 5069 }, 5070 _determineEventTarget: function(e) { 5071 var target = this._layers.Canvas._determineEventTarget(e); 5072 if (!target) { 5073 target = this; 5074 } 5075 return target; 5076 }, 5077 _onchildadded: function(e) { 5078 var child = e.node; 5079 var next = e.next; 5080 child._layer = this._layers.Canvas; 5081 this._layers.Canvas.insertBefore(child, next); 5082 }, 5083 _onenter: function() { 5084 this._layers.Canvas._startRendering(); 5085 enchant.Core.instance.addEventListener('exitframe', this._dispatchExitframe); 5086 }, 5087 _onexit: function() { 5088 this._layers.Canvas._stopRendering(); 5089 enchant.Core.instance.removeEventListener('exitframe', this._dispatchExitframe); 5090 } 5091 }); 5092 5093 /** 5094 * @scope enchant.DOMScene.prototype 5095 */ 5096 enchant.DOMScene = enchant.Class.create(enchant.Scene, { 5097 /** 5098 * @name enchant.DOMScene 5099 * @class 5100 * すべての子をDOMで描画するScene. 5101 * @constructs 5102 * @extends enchant.Scene 5103 */ 5104 initialize: function() { 5105 enchant.Scene.call(this); 5106 this.addLayer('Dom'); 5107 }, 5108 _determineEventTarget: function(e) { 5109 var target = this._layers.Dom._determineEventTarget(e); 5110 if (!target) { 5111 target = this; 5112 } 5113 return target; 5114 }, 5115 _onchildadded: function(e) { 5116 var child = e.node; 5117 var next = e.next; 5118 child._layer = this._layers.Dom; 5119 this._layers.Dom.insertBefore(child, next); 5120 }, 5121 _onenter: function() { 5122 this._layers.Dom._startRendering(); 5123 enchant.Core.instance.addEventListener('exitframe', this._dispatchExitframe); 5124 }, 5125 _onexit: function() { 5126 this._layers.Dom._stopRendering(); 5127 enchant.Core.instance.removeEventListener('exitframe', this._dispatchExitframe); 5128 } 5129 }); 5130 5131 /** 5132 * @scope enchant.Surface.prototype 5133 */ 5134 enchant.Surface = enchant.Class.create(enchant.EventTarget, { 5135 /** 5136 * @name enchant.Surface 5137 * @class 5138 * canvas要素をラップしたクラス. 5139 * 5140 * {@link enchant.Sprite} や {@link enchant.Map} のimageプロパティに設定して表示させることができる. 5141 * Canvas APIにアクセスしたいときは {@link enchant.Surface#context} プロパティを用いる. 5142 * 5143 * @example 5144 * // 円を表示するSpriteを作成する 5145 * var ball = new Sprite(50, 50); 5146 * var surface = new Surface(50, 50); 5147 * surface.context.beginPath(); 5148 * surface.context.arc(25, 25, 25, 0, Math.PI*2, true); 5149 * surface.context.fill(); 5150 * ball.image = surface; 5151 * 5152 * @param {Number} width Surfaceの横幅. 5153 * @param {Number} height Surfaceの高さ. 5154 * @constructs 5155 * @extends enchant.EventTarget 5156 */ 5157 initialize: function(width, height) { 5158 enchant.EventTarget.call(this); 5159 5160 var core = enchant.Core.instance; 5161 5162 /** 5163 * Surfaceの横幅. 5164 * @type Number 5165 */ 5166 this.width = Math.ceil(width); 5167 /** 5168 * Surfaceの高さ. 5169 * @type Number 5170 */ 5171 this.height = Math.ceil(height); 5172 /** 5173 * Surfaceの描画コンテクスト. 5174 * @type CanvasRenderingContext2D 5175 */ 5176 this.context = null; 5177 5178 var id = 'enchant-surface' + core._surfaceID++; 5179 if (document.getCSSCanvasContext) { 5180 this.context = document.getCSSCanvasContext('2d', id, width, height); 5181 this._element = this.context.canvas; 5182 this._css = '-webkit-canvas(' + id + ')'; 5183 var context = this.context; 5184 } else if (document.mozSetImageElement) { 5185 this._element = document.createElement('canvas'); 5186 this._element.width = width; 5187 this._element.height = height; 5188 this._css = '-moz-element(#' + id + ')'; 5189 this.context = this._element.getContext('2d'); 5190 document.mozSetImageElement(id, this._element); 5191 } else { 5192 this._element = document.createElement('canvas'); 5193 this._element.width = width; 5194 this._element.height = height; 5195 this._element.style.position = 'absolute'; 5196 this.context = this._element.getContext('2d'); 5197 5198 enchant.ENV.CANVAS_DRAWING_METHODS.forEach(function(name) { 5199 var method = this.context[name]; 5200 this.context[name] = function() { 5201 method.apply(this, arguments); 5202 this._dirty = true; 5203 }; 5204 }, this); 5205 } 5206 }, 5207 /** 5208 * Surfaceから1ピクセル取得する. 5209 * @param {Number} x 取得するピクセルのx座標. 5210 * @param {Number} y 取得するピクセルのy座標. 5211 * @return {Number[]} ピクセルの情報を[r, g, b, a]の形式で持つ配列. 5212 */ 5213 getPixel: function(x, y) { 5214 return this.context.getImageData(x, y, 1, 1).data; 5215 }, 5216 /** 5217 * Surfaceに1ピクセル設定する. 5218 * @param {Number} x 設定するピクセルのx座標. 5219 * @param {Number} y 設定するピクセルのy座標. 5220 * @param {Number} r 設定するピクセルのrの値. 5221 * @param {Number} g 設定するピクセルのgの値. 5222 * @param {Number} b 設定するピクセルのbの値. 5223 * @param {Number} a 設定するピクセルの透明度. 5224 */ 5225 setPixel: function(x, y, r, g, b, a) { 5226 var pixel = this.context.createImageData(1, 1); 5227 pixel.data[0] = r; 5228 pixel.data[1] = g; 5229 pixel.data[2] = b; 5230 pixel.data[3] = a; 5231 this.context.putImageData(pixel, x, y); 5232 }, 5233 /** 5234 * Surfaceの全ピクセルをクリアし透明度0の黒に設定する. 5235 */ 5236 clear: function() { 5237 this.context.clearRect(0, 0, this.width, this.height); 5238 }, 5239 /** 5240 * Surfaceに対して引数で指定されたSurfaceを描画する. 5241 * 5242 * Canvas APIのdrawImageをラップしており, 描画する矩形を同様の形式で指定できる. 5243 * 5244 * @example 5245 * var src = core.assets['src.gif']; 5246 * var dst = new Surface(100, 100); 5247 * dst.draw(src); // ソースを(0, 0)に描画 5248 * dst.draw(src, 50, 50); // ソースを(50, 50)に描画 5249 * // ソースを(50, 50)に縦横30ピクセル分だけ描画 5250 * dst.draw(src, 50, 50, 30, 30); 5251 * // ソースの(10, 10)から縦横40ピクセルの領域を(50, 50)に縦横30ピクセルに縮小して描画 5252 * dst.draw(src, 10, 10, 40, 40, 50, 50, 30, 30); 5253 * 5254 * @param {enchant.Surface} image 描画に用いるSurface. 5255 */ 5256 draw: function(image) { 5257 image = image._element; 5258 if (arguments.length === 1) { 5259 this.context.drawImage(image, 0, 0); 5260 } else { 5261 var args = arguments; 5262 args[0] = image; 5263 this.context.drawImage.apply(this.context, args); 5264 } 5265 }, 5266 /** 5267 * Surfaceを複製する. 5268 * @return {enchant.Surface} 複製されたSurface. 5269 */ 5270 clone: function() { 5271 var clone = new enchant.Surface(this.width, this.height); 5272 clone.draw(this); 5273 return clone; 5274 }, 5275 /** 5276 * SurfaceからdataスキームのURLを生成する. 5277 * @return {String} Surfaceを表すdataスキームのURL. 5278 */ 5279 toDataURL: function() { 5280 var src = this._element.src; 5281 if (src) { 5282 if (src.slice(0, 5) === 'data:') { 5283 return src; 5284 } else { 5285 return this.clone().toDataURL(); 5286 } 5287 } else { 5288 return this._element.toDataURL(); 5289 } 5290 } 5291 }); 5292 5293 /** 5294 * 画像ファイルを読み込んでSurfaceオブジェクトを作成する. 5295 * 5296 * このメソッドによって作成されたSurfaceはimg要素をラップしており {@link enchant.Surface#context} プロパティに 5297 * アクセスしたり {@link enchant.Surface#draw}, {@link enchant.Surface#clear}, {@link enchant.Surface#getPixel}, 5298 * {@link enchant.Surface#setPixel} メソッドなどの呼び出しでCanvas APIを使った画像操作を行うことはできない. 5299 * ただし{@link enchant.Surface#draw} メソッドの引数とすることはでき, 5300 * ほかのSurfaceに描画した上で画像操作を行うことはできる(クロスドメインでロードした 5301 * 場合はピクセルを取得するなど画像操作の一部が制限される). 5302 * 5303 * @param {String} src ロードする画像ファイルのパス. 5304 * @param {Function} callback ロード完了時のコールバック. 5305 * @param {Function} [onerror] ロード失敗時のコールバック. 5306 * @static 5307 * @return {enchant.Surface} Surface 5308 */ 5309 enchant.Surface.load = function(src, callback, onerror) { 5310 var image = new Image(); 5311 var surface = Object.create(enchant.Surface.prototype, { 5312 context: { value: null }, 5313 _css: { value: 'url(' + src + ')' }, 5314 _element: { value: image } 5315 }); 5316 enchant.EventTarget.call(surface); 5317 onerror = onerror || function() {}; 5318 surface.addEventListener('load', callback); 5319 surface.addEventListener('error', onerror); 5320 image.onerror = function() { 5321 var e = new enchant.Event(enchant.Event.ERROR); 5322 e.message = 'Cannot load an asset: ' + image.src; 5323 enchant.Core.instance.dispatchEvent(e); 5324 surface.dispatchEvent(e); 5325 }; 5326 image.onload = function() { 5327 surface.width = image.width; 5328 surface.height = image.height; 5329 surface.dispatchEvent(new enchant.Event('load')); 5330 }; 5331 image.src = src; 5332 return surface; 5333 }; 5334 enchant.Surface._staticCanvas2DContext = document.createElement('canvas').getContext('2d'); 5335 5336 enchant.Surface._getPattern = function(surface, force) { 5337 if (!surface._pattern || force) { 5338 surface._pattern = this._staticCanvas2DContext.createPattern(surface._element, 'repeat'); 5339 } 5340 return surface._pattern; 5341 }; 5342 5343 if (window.Deferred) { 5344 enchant.Deferred = window.Deferred; 5345 } else { 5346 /** 5347 * @scope enchant.Deferred.prototype 5348 */ 5349 enchant.Deferred = enchant.Class.create({ 5350 /** 5351 * @name enchant.Deferred 5352 * @class 5353 * 非同期処理を扱うためのクラス. 5354 * jsdeferredのAPIを模倣している. 5355 * jQuery.Deferredとの互換性はない. 5356 * <br/> 5357 * See: <a href="http://cho45.stfuawsc.com/jsdeferred/"> 5358 * http://cho45.stfuawsc.com/jsdeferred/</a> 5359 * 5360 * @example 5361 * enchant.Deferred 5362 * .next(function() { 5363 * return 42; 5364 * }) 5365 * .next(function(n) { 5366 * console.log(n); // 42 5367 * }) 5368 * .next(function() { 5369 * return core.load('img.png'); // wait loading 5370 * }) 5371 * .next(function() { 5372 * var img = core.assets['img.png']; 5373 * console.log(img instanceof enchant.Surface); // true 5374 * throw new Error('!!!'); 5375 * }) 5376 * .next(function() { 5377 * // skip 5378 * }) 5379 * .error(function(err) { 5380 * console.log(err.message); // !!! 5381 * }); 5382 * 5383 * @constructs 5384 */ 5385 initialize: function() { 5386 this._succ = this._fail = this._next = this._id = null; 5387 this._tail = this; 5388 }, 5389 /** 5390 * 後続の処理を追加する. 5391 * @param {Function} func 追加する処理. 5392 */ 5393 next: function(func) { 5394 var q = new enchant.Deferred(); 5395 q._succ = func; 5396 return this._add(q); 5397 }, 5398 /** 5399 * エラー処理を追加する. 5400 * @param {Function} func 追加するエラー処理. 5401 */ 5402 error: function(func) { 5403 var q = new enchant.Deferred(); 5404 q._fail = func; 5405 return this._add(q); 5406 }, 5407 _add: function(queue) { 5408 this._tail._next = queue; 5409 this._tail = queue; 5410 return this; 5411 }, 5412 /** 5413 * 値を伝播させる. 5414 * @param {*} arg 次の処理に渡す値. 5415 */ 5416 call: function(arg) { 5417 var received; 5418 var queue = this; 5419 while (queue && !queue._succ) { 5420 queue = queue._next; 5421 } 5422 if (!(queue instanceof enchant.Deferred)) { 5423 return; 5424 } 5425 try { 5426 received = queue._succ(arg); 5427 } catch (e) { 5428 return queue.fail(e); 5429 } 5430 if (received instanceof enchant.Deferred) { 5431 enchant.Deferred._insert(queue, received); 5432 } else if (queue._next instanceof enchant.Deferred) { 5433 queue._next.call(received); 5434 } 5435 }, 5436 /** 5437 * エラーを伝播させる. 5438 * @param {*} arg エラーとして伝播させる値. 5439 */ 5440 fail: function(arg) { 5441 var result, err, 5442 queue = this; 5443 while (queue && !queue._fail) { 5444 queue = queue._next; 5445 } 5446 if (queue instanceof enchant.Deferred) { 5447 result = queue._fail(arg); 5448 queue.call(result); 5449 } else if (arg instanceof Error) { 5450 throw arg; 5451 } else { 5452 err = new Error('failed in Deferred'); 5453 err.arg = arg; 5454 throw err; 5455 } 5456 } 5457 }); 5458 enchant.Deferred._insert = function(queue, ins) { 5459 if (queue._next instanceof enchant.Deferred) { 5460 ins._tail._next = queue._next; 5461 } 5462 queue._next = ins; 5463 }; 5464 /** 5465 * タイマーで起動するDeferredオブジェクトを生成する. 5466 * @param {Function} func 5467 * @return {enchant.Deferred} 生成されたDeferredオブジェクト. 5468 * @static 5469 */ 5470 enchant.Deferred.next = function(func) { 5471 var q = new enchant.Deferred().next(func); 5472 q._id = setTimeout(function() { q.call(); }, 0); 5473 return q; 5474 }; 5475 /** 5476 * 複数のDeferredオブジェクトを待つDeferredオブジェクトを生成する. 5477 * @param {Object|enchant.Deferred[]} arg 5478 * @return {enchant.Deferred} 生成されたDeferredオブジェクト. 5479 * 5480 * @example 5481 * // array 5482 * enchant.Deferred 5483 * .parallel([ 5484 * enchant.Deferred.next(function() { 5485 * return 24; 5486 * }), 5487 * enchant.Deferred.next(function() { 5488 * return 42; 5489 * }) 5490 * ]) 5491 * .next(function(arg) { 5492 * console.log(arg); // [ 24, 42 ] 5493 * }); 5494 * // object 5495 * enchant.Deferred 5496 * .parallel({ 5497 * foo: enchant.Deferred.next(function() { 5498 * return 24; 5499 * }), 5500 * bar: enchant.Deferred.next(function() { 5501 * return 42; 5502 * }) 5503 * }) 5504 * .next(function(arg) { 5505 * console.log(arg.foo); // 24 5506 * console.log(arg.bar); // 42 5507 * }); 5508 * 5509 * @static 5510 */ 5511 enchant.Deferred.parallel = function(arg) { 5512 var q = new enchant.Deferred(); 5513 q._id = setTimeout(function() { q.call(); }, 0); 5514 var progress = 0; 5515 var ret = (arg instanceof Array) ? [] : {}; 5516 var p = new enchant.Deferred(); 5517 for (var prop in arg) { 5518 if (arg.hasOwnProperty(prop)) { 5519 progress++; 5520 /*jshint loopfunc:true */ 5521 (function(queue, name) { 5522 queue.next(function(arg) { 5523 progress--; 5524 ret[name] = arg; 5525 if (progress <= 0) { 5526 p.call(ret); 5527 } 5528 }) 5529 .error(function(err) { p.fail(err); }); 5530 if (typeof queue._id === 'number') { 5531 clearTimeout(queue._id); 5532 } 5533 queue._id = setTimeout(function() { queue.call(); }, 0); 5534 }(arg[prop], prop)); 5535 } 5536 } 5537 if (!progress) { 5538 p._id = setTimeout(function() { p.call(ret); }, 0); 5539 } 5540 return q.next(function() { return p; }); 5541 }; 5542 } 5543 5544 /** 5545 * @scope enchant.DOMSound.prototype 5546 */ 5547 enchant.DOMSound = enchant.Class.create(enchant.EventTarget, { 5548 /** 5549 * @name enchant.DOMSound 5550 * @class 5551 * audio要素をラップしたクラス. 5552 * 5553 * MP3ファイルの再生はSafari, Chrome, Firefox, Opera, IEが対応 5554 * (Firefox, OperaではFlashを経由して再生). WAVEファイルの再生は 5555 * Safari, Chrome, Firefox, Operaが対応している. ブラウザが音声ファイル 5556 * のコーデックに対応していない場合は再生されない. 5557 * 5558 * コンストラクタではなく {@link enchant.DOMSound.load} を通じてインスタンスを作成する. 5559 * @constructs 5560 * @extends enchant.EventTarget 5561 */ 5562 initialize: function() { 5563 enchant.EventTarget.call(this); 5564 /** 5565 * Soundの再生時間 (秒). 5566 * @type Number 5567 */ 5568 this.duration = 0; 5569 throw new Error("Illegal Constructor"); 5570 }, 5571 /** 5572 * 再生を開始する. 5573 */ 5574 play: function() { 5575 if (this._element) { 5576 this._element.play(); 5577 } 5578 }, 5579 /** 5580 * 再生を中断する. 5581 */ 5582 pause: function() { 5583 if (this._element) { 5584 this._element.pause(); 5585 } 5586 }, 5587 /** 5588 * 再生を停止する. 5589 */ 5590 stop: function() { 5591 this.pause(); 5592 this.currentTime = 0; 5593 }, 5594 /** 5595 * Soundを複製する. 5596 * @return {enchant.DOMSound} 複製されたSound. 5597 */ 5598 clone: function() { 5599 var clone; 5600 if (this._element instanceof Audio) { 5601 clone = Object.create(enchant.DOMSound.prototype, { 5602 _element: { value: this._element.cloneNode(false) }, 5603 duration: { value: this.duration } 5604 }); 5605 } else if (enchant.ENV.USE_FLASH_SOUND) { 5606 return this; 5607 } else { 5608 clone = Object.create(enchant.DOMSound.prototype); 5609 } 5610 enchant.EventTarget.call(clone); 5611 return clone; 5612 }, 5613 /** 5614 * 現在の再生位置 (秒). 5615 * @type Number 5616 */ 5617 currentTime: { 5618 get: function() { 5619 return this._element ? this._element.currentTime : 0; 5620 }, 5621 set: function(time) { 5622 if (this._element) { 5623 this._element.currentTime = time; 5624 } 5625 } 5626 }, 5627 /** 5628 * ボリューム. 0 (無音) ~ 1 (フルボリューム). 5629 * @type Number 5630 */ 5631 volume: { 5632 get: function() { 5633 return this._element ? this._element.volume : 1; 5634 }, 5635 set: function(volume) { 5636 if (this._element) { 5637 this._element.volume = volume; 5638 } 5639 } 5640 } 5641 }); 5642 5643 /** 5644 * 音声ファイルを読み込んでDOMSoundオブジェクトを作成する. 5645 * @param {String} src ロードする音声ファイルのパス. 5646 * @param {String} [type] 音声ファイルのMIME Type. 5647 * @param {Function} [callback] ロード完了時のコールバック. 5648 * @param {Function} [onerror] ロード失敗時のコールバック. 5649 * @return {enchant.DOMSound} DOMSound 5650 * @static 5651 */ 5652 enchant.DOMSound.load = function(src, type, callback, onerror) { 5653 if (type == null) { 5654 var ext = enchant.Core.findExt(src); 5655 if (ext) { 5656 type = 'audio/' + ext; 5657 } else { 5658 type = ''; 5659 } 5660 } 5661 type = type.replace('mp3', 'mpeg').replace('m4a', 'mp4'); 5662 callback = callback || function() {}; 5663 onerror = onerror || function() {}; 5664 5665 var sound = Object.create(enchant.DOMSound.prototype); 5666 enchant.EventTarget.call(sound); 5667 sound.addEventListener('load', callback); 5668 sound.addEventListener('error', onerror); 5669 var audio = new Audio(); 5670 if (!enchant.ENV.SOUND_ENABLED_ON_MOBILE_SAFARI && 5671 enchant.ENV.VENDOR_PREFIX === 'webkit' && enchant.ENV.TOUCH_ENABLED) { 5672 window.setTimeout(function() { 5673 sound.dispatchEvent(new enchant.Event('load')); 5674 }, 0); 5675 } else { 5676 if (!enchant.ENV.USE_FLASH_SOUND && audio.canPlayType(type)) { 5677 audio.addEventListener('canplaythrough', function canplay() { 5678 sound.duration = audio.duration; 5679 sound.dispatchEvent(new enchant.Event('load')); 5680 audio.removeEventListener('canplaythrough', canplay); 5681 }, false); 5682 audio.src = src; 5683 audio.load(); 5684 audio.autoplay = false; 5685 audio.onerror = function() { 5686 var e = new enchant.Event(enchant.Event.ERROR); 5687 e.message = 'Cannot load an asset: ' + audio.src; 5688 enchant.Core.instance.dispatchEvent(e); 5689 sound.dispatchEvent(e); 5690 }; 5691 sound._element = audio; 5692 } else if (type === 'audio/mpeg') { 5693 var embed = document.createElement('embed'); 5694 var id = 'enchant-audio' + enchant.Core.instance._soundID++; 5695 embed.width = embed.height = 1; 5696 embed.name = id; 5697 embed.src = 'sound.swf?id=' + id + '&src=' + src; 5698 embed.allowscriptaccess = 'always'; 5699 embed.style.position = 'absolute'; 5700 embed.style.left = '-1px'; 5701 sound.addEventListener('load', function() { 5702 Object.defineProperties(embed, { 5703 currentTime: { 5704 get: function() { 5705 return embed.getCurrentTime(); 5706 }, 5707 set: function(time) { 5708 embed.setCurrentTime(time); 5709 } 5710 }, 5711 volume: { 5712 get: function() { 5713 return embed.getVolume(); 5714 }, 5715 set: function(volume) { 5716 embed.setVolume(volume); 5717 } 5718 } 5719 }); 5720 sound._element = embed; 5721 sound.duration = embed.getDuration(); 5722 }); 5723 enchant.Core.instance._element.appendChild(embed); 5724 enchant.DOMSound[id] = sound; 5725 } else { 5726 window.setTimeout(function() { 5727 sound.dispatchEvent(new enchant.Event('load')); 5728 }, 0); 5729 } 5730 } 5731 return sound; 5732 }; 5733 5734 window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext || window.oAudioContext; 5735 5736 /** 5737 * @scope enchant.WebAudioSound.prototype 5738 */ 5739 enchant.WebAudioSound = enchant.Class.create(enchant.EventTarget, { 5740 /** 5741 * @name enchant.WebAudioSound 5742 * @class 5743 * WebAudioをラップしたクラス. 5744 * @constructs 5745 * @extends enchant.EventTarget 5746 */ 5747 initialize: function() { 5748 if (!window.AudioContext) { 5749 throw new Error("This browser does not support WebAudio API."); 5750 } 5751 enchant.EventTarget.call(this); 5752 if (!enchant.WebAudioSound.audioContext) { 5753 enchant.WebAudioSound.audioContext = new window.AudioContext(); 5754 enchant.WebAudioSound.destination = enchant.WebAudioSound.audioContext.destination; 5755 } 5756 this.context = enchant.WebAudioSound.audioContext; 5757 this.src = this.context.createBufferSource(); 5758 this.buffer = null; 5759 this._volume = 1; 5760 this._currentTime = 0; 5761 this._state = 0; 5762 this.connectTarget = enchant.WebAudioSound.destination; 5763 }, 5764 /** 5765 * 再生を開始する. 5766 * @param {Boolean} [dup=false] trueならオブジェクトの現在の再生を残したまま新しく音声を再生する. 5767 */ 5768 play: function(dup) { 5769 if (this._state === 1 && !dup) { 5770 this.src.disconnect(); 5771 } 5772 if (this._state !== 2) { 5773 this._currentTime = 0; 5774 } 5775 var offset = this._currentTime; 5776 var actx = this.context; 5777 this.src = actx.createBufferSource(); 5778 if (actx.createGain != null) { 5779 this._gain = actx.createGain(); 5780 } else { 5781 this._gain = actx.createGainNode(); 5782 } 5783 this.src.buffer = this.buffer; 5784 this._gain.gain.value = this._volume; 5785 5786 this.src.connect(this._gain); 5787 this._gain.connect(this.connectTarget); 5788 if (this.src.start != null) { 5789 this.src.start(0, offset, this.buffer.duration - offset - 1.192e-7); 5790 } else { 5791 this.src.noteGrainOn(0, offset, this.buffer.duration - offset - 1.192e-7); 5792 } 5793 this._startTime = actx.currentTime - this._currentTime; 5794 this._state = 1; 5795 }, 5796 /** 5797 * 再生を中断する. 5798 */ 5799 pause: function() { 5800 var currentTime = this.currentTime; 5801 if (currentTime === this.duration) { 5802 return; 5803 } 5804 if (this.src.stop != null) { 5805 this.src.stop(0); 5806 } else { 5807 this.src.noteOff(0); 5808 } 5809 this._currentTime = currentTime; 5810 this._state = 2; 5811 }, 5812 /** 5813 * 再生を停止する. 5814 */ 5815 stop: function() { 5816 if (this.src.stop != null) { 5817 this.src.stop(0); 5818 } else { 5819 this.src.noteOff(0); 5820 } 5821 this._state = 0; 5822 }, 5823 /** 5824 * Soundを複製する. 5825 * @return {enchant.WebAudioSound} 複製されたSound. 5826 */ 5827 clone: function() { 5828 var sound = new enchant.WebAudioSound(); 5829 sound.buffer = this.buffer; 5830 return sound; 5831 }, 5832 /** 5833 * Soundの再生時間 (秒). 5834 * @type Number 5835 */ 5836 duration: { 5837 get: function() { 5838 if (this.buffer) { 5839 return this.buffer.duration; 5840 } else { 5841 return 0; 5842 } 5843 } 5844 }, 5845 /** 5846 * ボリューム. 0 (無音) ~ 1 (フルボリューム). 5847 * @type Number 5848 */ 5849 volume: { 5850 get: function() { 5851 return this._volume; 5852 }, 5853 set: function(volume) { 5854 volume = Math.max(0, Math.min(1, volume)); 5855 this._volume = volume; 5856 if (this.src) { 5857 this._gain.gain.value = volume; 5858 } 5859 } 5860 }, 5861 /** 5862 * 現在の再生位置 (秒). 5863 * @type Number 5864 */ 5865 currentTime: { 5866 get: function() { 5867 return Math.max(0, Math.min(this.duration, this.src.context.currentTime - this._startTime)); 5868 }, 5869 set: function(time) { 5870 this._currentTime = time; 5871 if (this._state !== 2) { 5872 this.play(false); 5873 } 5874 } 5875 } 5876 }); 5877 5878 /** 5879 * 音声ファイルを読み込んでWebAudioSoundオブジェクトを作成する. 5880 * @param {String} src ロードする音声ファイルのパス. 5881 * @param {String} [type] 音声ファイルのMIME Type. 5882 * @param {Function} [callback] ロード完了時のコールバック. 5883 * @param {Function} [onerror] ロード失敗時のコールバック. 5884 * @return {enchant.WebAudioSound} WebAudioSound 5885 * @static 5886 */ 5887 enchant.WebAudioSound.load = function(src, type, callback, onerror) { 5888 var canPlay = (new Audio()).canPlayType(type); 5889 var sound = new enchant.WebAudioSound(); 5890 callback = callback || function() {}; 5891 onerror = onerror || function() {}; 5892 sound.addEventListener(enchant.Event.LOAD, callback); 5893 sound.addEventListener(enchant.Event.ERROR, onerror); 5894 function dispatchErrorEvent() { 5895 var e = new enchant.Event(enchant.Event.ERROR); 5896 e.message = 'Cannot load an asset: ' + src; 5897 enchant.Core.instance.dispatchEvent(e); 5898 sound.dispatchEvent(e); 5899 } 5900 var actx, xhr; 5901 if (canPlay === 'maybe' || canPlay === 'probably') { 5902 actx = enchant.WebAudioSound.audioContext; 5903 xhr = new XMLHttpRequest(); 5904 xhr.open('GET', src, true); 5905 xhr.responseType = 'arraybuffer'; 5906 xhr.onload = function() { 5907 actx.decodeAudioData(xhr.response, function(buffer) { 5908 sound.buffer = buffer; 5909 sound.dispatchEvent(new enchant.Event(enchant.Event.LOAD)); 5910 }, dispatchErrorEvent); 5911 }; 5912 xhr.onerror = dispatchErrorEvent; 5913 xhr.send(null); 5914 } else { 5915 setTimeout(dispatchErrorEvent, 50); 5916 } 5917 return sound; 5918 }; 5919 5920 enchant.Sound = window.AudioContext && enchant.ENV.USE_WEBAUDIO ? enchant.WebAudioSound : enchant.DOMSound; 5921 5922 /* 5923 * ============================================================================================ 5924 * Easing Equations v2.0 5925 * September 1, 2003 5926 * (c) 2003 Robert Penner, all rights reserved. 5927 * This work is subject to the terms in http://www.robertpenner.com/easing_terms_of_use.html. 5928 * ============================================================================================ 5929 */ 5930 5931 /** 5932 * @namespace 5933 * イージング関数ライブラリ. 5934 * {@link enchant.Easing} 以下にある関数は全て t(現在の時刻), b(初期値), c(変化後の値), d(値の変化にかける時間) の引数を取り, 指定した時刻に取る値を返す. 5935 * ActionScript で広く使われている Robert Penner による Easing Equations を JavaScript に移植した. 5936 * <br/> 5937 * See: <a href="http://www.robertpenner.com/easing/"> 5938 * http://www.robertpenner.com/easing/</a> 5939 * <br/> 5940 * See: <a href="http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf"> 5941 * http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf</a> 5942 */ 5943 enchant.Easing = { 5944 LINEAR: function(t, b, c, d) { 5945 return c * t / d + b; 5946 }, 5947 5948 SWING: function(t, b, c, d) { 5949 return c * (0.5 - Math.cos(((t / d) * Math.PI)) / 2) + b; 5950 }, 5951 5952 // *** quad 5953 QUAD_EASEIN: function(t, b, c, d) { 5954 return c * (t /= d) * t + b; 5955 }, 5956 5957 QUAD_EASEOUT: function(t, b, c, d) { 5958 return -c * (t /= d) * (t - 2) + b; 5959 }, 5960 5961 QUAD_EASEINOUT: function(t, b, c, d) { 5962 if ((t /= d / 2) < 1) { 5963 return c / 2 * t * t + b; 5964 } 5965 return -c / 2 * ((--t) * (t - 2) - 1) + b; 5966 }, 5967 5968 // *** cubic 5969 CUBIC_EASEIN: function(t, b, c, d) { 5970 return c * (t /= d) * t * t + b; 5971 }, 5972 5973 CUBIC_EASEOUT: function(t, b, c, d) { 5974 return c * ((t = t / d - 1) * t * t + 1) + b; 5975 }, 5976 5977 CUBIC_EASEINOUT: function(t, b, c, d) { 5978 if ((t /= d / 2) < 1) { 5979 return c / 2 * t * t * t + b; 5980 } 5981 return c / 2 * ((t -= 2) * t * t + 2) + b; 5982 }, 5983 5984 // *** quart 5985 QUART_EASEIN: function(t, b, c, d) { 5986 return c * (t /= d) * t * t * t + b; 5987 }, 5988 5989 QUART_EASEOUT: function(t, b, c, d) { 5990 return -c * ((t = t / d - 1) * t * t * t - 1) + b; 5991 }, 5992 5993 QUART_EASEINOUT: function(t, b, c, d) { 5994 if ((t /= d / 2) < 1) { 5995 return c / 2 * t * t * t * t + b; 5996 } 5997 return -c / 2 * ((t -= 2) * t * t * t - 2) + b; 5998 }, 5999 6000 // *** quint 6001 QUINT_EASEIN: function(t, b, c, d) { 6002 return c * (t /= d) * t * t * t * t + b; 6003 }, 6004 6005 QUINT_EASEOUT: function(t, b, c, d) { 6006 return c * ((t = t / d - 1) * t * t * t * t + 1) + b; 6007 }, 6008 6009 QUINT_EASEINOUT: function(t, b, c, d) { 6010 if ((t /= d / 2) < 1) { 6011 return c / 2 * t * t * t * t * t + b; 6012 } 6013 return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; 6014 }, 6015 6016 // *** sin 6017 SIN_EASEIN: function(t, b, c, d) { 6018 return -c * Math.cos(t / d * (Math.PI / 2)) + c + b; 6019 }, 6020 6021 SIN_EASEOUT: function(t, b, c, d) { 6022 return c * Math.sin(t / d * (Math.PI / 2)) + b; 6023 }, 6024 6025 SIN_EASEINOUT: function(t, b, c, d) { 6026 return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; 6027 }, 6028 6029 // *** circ 6030 CIRC_EASEIN: function(t, b, c, d) { 6031 return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b; 6032 }, 6033 6034 CIRC_EASEOUT: function(t, b, c, d) { 6035 return c * Math.sqrt(1 - (t = t / d - 1) * t) + b; 6036 }, 6037 6038 CIRC_EASEINOUT: function(t, b, c, d) { 6039 if ((t /= d / 2) < 1) { 6040 return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b; 6041 } 6042 return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b; 6043 }, 6044 6045 // *** elastic 6046 ELASTIC_EASEIN: function(t, b, c, d, a, p) { 6047 if (t === 0) { 6048 return b; 6049 } 6050 if ((t /= d) === 1) { 6051 return b + c; 6052 } 6053 6054 if (!p) { 6055 p = d * 0.3; 6056 } 6057 6058 var s; 6059 if (!a || a < Math.abs(c)) { 6060 a = c; 6061 s = p / 4; 6062 } else { 6063 s = p / (2 * Math.PI) * Math.asin(c / a); 6064 } 6065 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; 6066 }, 6067 6068 ELASTIC_EASEOUT: function(t, b, c, d, a, p) { 6069 if (t === 0) { 6070 return b; 6071 } 6072 if ((t /= d) === 1) { 6073 return b + c; 6074 } 6075 if (!p) { 6076 p = d * 0.3; 6077 } 6078 var s; 6079 if (!a || a < Math.abs(c)) { 6080 a = c; 6081 s = p / 4; 6082 } else { 6083 s = p / (2 * Math.PI) * Math.asin(c / a); 6084 } 6085 return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b); 6086 }, 6087 6088 ELASTIC_EASEINOUT: function(t, b, c, d, a, p) { 6089 if (t === 0) { 6090 return b; 6091 } 6092 if ((t /= d / 2) === 2) { 6093 return b + c; 6094 } 6095 if (!p) { 6096 p = d * (0.3 * 1.5); 6097 } 6098 var s; 6099 if (!a || a < Math.abs(c)) { 6100 a = c; 6101 s = p / 4; 6102 } else { 6103 s = p / (2 * Math.PI) * Math.asin(c / a); 6104 } 6105 if (t < 1) { 6106 return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; 6107 } 6108 return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b; 6109 }, 6110 6111 // *** bounce 6112 BOUNCE_EASEOUT: function(t, b, c, d) { 6113 if ((t /= d) < (1 / 2.75)) { 6114 return c * (7.5625 * t * t) + b; 6115 } else if (t < (2 / 2.75)) { 6116 return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b; 6117 } else if (t < (2.5 / 2.75)) { 6118 return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b; 6119 } else { 6120 return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b; 6121 } 6122 }, 6123 6124 BOUNCE_EASEIN: function(t, b, c, d) { 6125 return c - enchant.Easing.BOUNCE_EASEOUT(d - t, 0, c, d) + b; 6126 }, 6127 6128 BOUNCE_EASEINOUT: function(t, b, c, d) { 6129 if (t < d / 2) { 6130 return enchant.Easing.BOUNCE_EASEIN(t * 2, 0, c, d) * 0.5 + b; 6131 } else { 6132 return enchant.Easing.BOUNCE_EASEOUT(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; 6133 } 6134 6135 }, 6136 6137 // *** back 6138 BACK_EASEIN: function(t, b, c, d, s) { 6139 if (s === undefined) { 6140 s = 1.70158; 6141 } 6142 return c * (t /= d) * t * ((s + 1) * t - s) + b; 6143 }, 6144 6145 BACK_EASEOUT: function(t, b, c, d, s) { 6146 if (s === undefined) { 6147 s = 1.70158; 6148 } 6149 return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; 6150 }, 6151 6152 BACK_EASEINOUT: function(t, b, c, d, s) { 6153 if (s === undefined) { 6154 s = 1.70158; 6155 } 6156 if ((t /= d / 2) < 1) { 6157 return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b; 6158 } 6159 return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b; 6160 }, 6161 6162 // *** expo 6163 EXPO_EASEIN: function(t, b, c, d) { 6164 return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; 6165 }, 6166 6167 EXPO_EASEOUT: function(t, b, c, d) { 6168 return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; 6169 }, 6170 6171 EXPO_EASEINOUT: function(t, b, c, d) { 6172 if (t === 0) { 6173 return b; 6174 } 6175 if (t === d) { 6176 return b + c; 6177 } 6178 if ((t /= d / 2) < 1) { 6179 return c / 2 * Math.pow(2, 10 * (t - 1)) + b; 6180 } 6181 return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b; 6182 } 6183 }; 6184 6185 /** 6186 * @scope enchant.ActionEventTarget.prototype 6187 */ 6188 enchant.ActionEventTarget = enchant.Class.create(enchant.EventTarget, { 6189 /** 6190 * @name enchant.ActionEventTarget 6191 * @class 6192 * timelineの {@link enchant.Action} クラス向けに拡張された {@link enchant.EventTarget} クラス. 6193 * @constructs 6194 * @extends enchant.EventTarget 6195 */ 6196 initialize: function() { 6197 enchant.EventTarget.apply(this, arguments); 6198 }, 6199 dispatchEvent: function(e) { 6200 var target = this.node ? this.node : this; 6201 6202 e.target = target; 6203 e.localX = e.x - target._offsetX; 6204 e.localY = e.y - target._offsetY; 6205 6206 if (this['on' + e.type] != null) { 6207 this['on' + e.type].call(target, e); 6208 } 6209 var listeners = this._listeners[e.type]; 6210 if (listeners != null) { 6211 listeners = listeners.slice(); 6212 for (var i = 0, len = listeners.length; i < len; i++) { 6213 listeners[i].call(target, e); 6214 } 6215 } 6216 } 6217 }); 6218 6219 /** 6220 * @scope enchant.Timeline.prototype 6221 */ 6222 enchant.Timeline = enchant.Class.create(enchant.EventTarget, { 6223 /** 6224 * @name enchant.Timeline 6225 * @class 6226 * アニメーションを管理するためのクラス. 6227 * 6228 * 操作するノードひとつに対して, 必ずひとつのタイムラインが対応する. 6229 * タイムラインクラスは, 自身に様々なアクションを追加するメソッドを持っており, 6230 * これらを使うことで簡潔にアニメーションや様々な操作をすることができる. 6231 * タイムラインクラスはフレームとタイムのアニメーションができる. 6232 * @param {enchant.Node} node 操作の対象となるノード. 6233 * @constructs 6234 * @extends enchant.EventTarget 6235 */ 6236 initialize: function(node) { 6237 enchant.EventTarget.call(this); 6238 this.node = node; 6239 this.queue = []; 6240 this.paused = false; 6241 this.looped = false; 6242 this.isFrameBased = true; 6243 this._parallel = null; 6244 this._activated = false; 6245 this.addEventListener(enchant.Event.ENTER_FRAME, this._onenterframe); 6246 6247 var tl = this; 6248 this._nodeEventListener = function(e) { 6249 tl.dispatchEvent(e); 6250 }; 6251 }, 6252 /** 6253 * @private 6254 */ 6255 _deactivateTimeline: function() { 6256 if (this._activated) { 6257 this._activated = false; 6258 this.node.removeEventListener('enterframe', this._nodeEventListener); 6259 } 6260 }, 6261 /** 6262 * @private 6263 */ 6264 _activateTimeline: function() { 6265 if (!this._activated && !this.paused) { 6266 this.node.addEventListener("enterframe", this._nodeEventListener); 6267 this._activated = true; 6268 } 6269 }, 6270 /** 6271 * @private 6272 */ 6273 _onenterframe: function(evt) { 6274 if (this.paused) { 6275 return; 6276 } 6277 6278 this.tick(this.isFrameBased ? 1 : evt.elapsed); 6279 }, 6280 /** 6281 * 一つのenchant.Event.ENTER_FRAMEイベントはアニメーションに一つの時間単位になる. (デフォルト) 6282 */ 6283 setFrameBased: function() { 6284 this.isFrameBased = true; 6285 }, 6286 /** 6287 * 一つのenchant.Event.ENTER_FRAMEイベントはアニメーションに前のフレームから経過した時間になる. 6288 */ 6289 setTimeBased: function() { 6290 this.isFrameBased = false; 6291 }, 6292 /** 6293 * キューの先頭にあるアクションを終了し, 次のアクションへ移行する. 6294 * アクションの中から呼び出されるが, 外から呼び出すこともできる. 6295 * 6296 * アクション実行中に, アクションが終了した場合, 6297 * もう一度 tick() 関数が呼ばれるため, 1フレームに複数のアクションが処理される場合もある. 6298 * 6299 * @example 6300 * sprite.tl.then(function A(){ .. }).then(function B(){ .. }); 6301 * // 最初のフレームで A・B の関数どちらも実行される. 6302 */ 6303 next: function(remainingTime) { 6304 var e, action = this.queue.shift(); 6305 6306 if (action) { 6307 e = new enchant.Event("actionend"); 6308 e.timeline = this; 6309 action.dispatchEvent(e); 6310 6311 e = new enchant.Event("removedfromtimeline"); 6312 e.timeline = this; 6313 action.dispatchEvent(e); 6314 6315 if (this.looped) { 6316 this.add(action); 6317 } 6318 } 6319 6320 if (this.queue.length === 0) { 6321 this._deactivateTimeline(); 6322 return; 6323 } 6324 6325 if (remainingTime > 0 || (this.queue[0] && this.queue[0].time === 0)) { 6326 var event = new enchant.Event("actiontick"); 6327 event.elapsed = remainingTime; 6328 event.timeline = this; 6329 this.queue[0].dispatchEvent(event); 6330 } 6331 }, 6332 /** 6333 * Timelineの時間を進める. 6334 * (キューの先頭にあるアクションに対して, actionstart/actiontickイベントを発行する) 6335 * @param {Number} elapsed 経過させる時間. 6336 */ 6337 tick: function(elapsed) { 6338 if (this.queue.length > 0) { 6339 var action = this.queue[0]; 6340 if (action.frame === 0) { 6341 var f; 6342 f = new enchant.Event("actionstart"); 6343 f.timeline = this; 6344 action.dispatchEvent(f); 6345 } 6346 6347 var e = new enchant.Event("actiontick"); 6348 e.timeline = this; 6349 e.elapsed = elapsed; 6350 action.dispatchEvent(e); 6351 } 6352 }, 6353 /** 6354 * タイムラインにアクションを追加する. 6355 * @param {enchant.Action} action 追加するアクション. 6356 * @return {enchant.Timeline} 自身. 6357 */ 6358 add: function(action) { 6359 this._activateTimeline(); 6360 if (this._parallel) { 6361 this._parallel.actions.push(action); 6362 this._parallel = null; 6363 } else { 6364 this.queue.push(action); 6365 } 6366 action.frame = 0; 6367 6368 var e = new enchant.Event("addedtotimeline"); 6369 e.timeline = this; 6370 action.dispatchEvent(e); 6371 6372 e = new enchant.Event("actionadded"); 6373 e.action = action; 6374 this.dispatchEvent(e); 6375 6376 return this; 6377 }, 6378 /** 6379 * アクションを簡単に追加するためのメソッド. 6380 * 実体は {@link enchant.Timeline#add} のラッパ. 6381 * @param {Object} params アクションの設定オブジェクト. 6382 * @return {enchant.Timeline} 自身. 6383 */ 6384 action: function(params) { 6385 return this.add(new enchant.Action(params)); 6386 }, 6387 /** 6388 * トゥイーンを簡単に追加するためのメソッド. 6389 * 実体は {@link enchant.Timeline#add} のラッパ. 6390 * @param {Object} params トゥイーンの設定オブジェクト. 6391 * @return {enchant.Timeline} 自身. 6392 */ 6393 tween: function(params) { 6394 return this.add(new enchant.Tween(params)); 6395 }, 6396 /** 6397 * タイムラインのキューをすべて破棄する. 終了イベントは発行されない. 6398 * @return {enchant.Timeline} 自身. 6399 */ 6400 clear: function() { 6401 var e = new enchant.Event("removedfromtimeline"); 6402 e.timeline = this; 6403 6404 for (var i = 0, len = this.queue.length; i < len; i++) { 6405 this.queue[i].dispatchEvent(e); 6406 } 6407 this.queue = []; 6408 this._deactivateTimeline(); 6409 return this; 6410 }, 6411 /** 6412 * タイムラインを早送りする. 6413 * 指定したフレーム数が経過したのと同様の処理を, 瞬時に実行する. 6414 * 巻き戻しはできない. 6415 * @param {Number} frames スキップするフレーム数. 6416 * @return {enchant.Timeline} 自身. 6417 */ 6418 skip: function(frames) { 6419 var event = new enchant.Event("enterframe"); 6420 if (this.isFrameBased) { 6421 event.elapsed = 1; 6422 } else { 6423 event.elapsed = frames; 6424 frames = 1; 6425 } 6426 while (frames--) { 6427 this.dispatchEvent(event); 6428 } 6429 return this; 6430 }, 6431 /** 6432 * タイムラインの実行を一時停止する. 6433 * @return {enchant.Timeline} 自身. 6434 */ 6435 pause: function() { 6436 if (!this.paused) { 6437 this.paused = true; 6438 this._deactivateTimeline(); 6439 } 6440 return this; 6441 }, 6442 /** 6443 * タイムラインの実行を再開する. 6444 * @return {enchant.Timeline} 自身. 6445 */ 6446 resume: function() { 6447 if (this.paused) { 6448 this.paused = false; 6449 this._activateTimeline(); 6450 } 6451 return this; 6452 }, 6453 /** 6454 * タイムラインをループさせる. 6455 * ループしているときに終了したアクションは, タイムラインから取り除かれた後, 6456 * 再度タイムラインに追加される. このアクションは, ループが解除されても残る. 6457 * @return {enchant.Timeline} 自身. 6458 */ 6459 loop: function() { 6460 this.looped = true; 6461 return this; 6462 }, 6463 /** 6464 * タイムラインのループを解除する. 6465 * @return {enchant.Timeline} 自身. 6466 */ 6467 unloop: function() { 6468 this.looped = false; 6469 return this; 6470 }, 6471 /** 6472 * 指定したフレーム数だけ待ち, 何もしないアクションを追加する. 6473 * @param {Number} time 待機するフレーム数. 6474 * @return {enchant.Timeline} 自身. 6475 */ 6476 delay: function(time) { 6477 return this.action({ 6478 time: time 6479 }); 6480 }, 6481 /** 6482 * @ignore 6483 * @param {Number} time 6484 */ 6485 wait: function(time) { 6486 // reserved 6487 return this; 6488 }, 6489 /** 6490 * 関数を実行し, 即時に次のアクションに移るアクションを追加する. 6491 * @param {Function} func 実行する関数. 6492 * @return {enchant.Timeline} 自身. 6493 */ 6494 then: function(func) { 6495 return this.action({ 6496 onactiontick: function(evt) { 6497 func.call(this); 6498 }, 6499 // if time is 0, next action will be immediately executed 6500 time: 0 6501 }); 6502 }, 6503 /** 6504 * 関数を実行し, 即時に次のアクションに移るアクションを追加する. 6505 * {@link enchant.Timeline#then} のシノニム. 6506 * @param {Function} func 実行する関数. 6507 * @return {enchant.Timeline} 自身. 6508 */ 6509 exec: function(func) { 6510 return this.then(func); 6511 }, 6512 /** 6513 * 実行したい関数を, フレーム数をキーとした連想配列(オブジェクト)で複数指定し追加する. 6514 * 内部的には {@link enchant.Timeline#delay}, {@link enchant.Timeline#then} を用いている. 6515 * 6516 * @example 6517 * sprite.tl.cue({ 6518 * 10: function() {}, // 10フレーム経過した後に実行される関数 6519 * 20: function() {}, // 20フレーム経過した後に実行される関数 6520 * 30: function() {} // 30フレーム経過した後に実行される関数 6521 * }); 6522 * 6523 * @param {Object} cue キューオブジェクト. 6524 * @return {enchant.Timeline} 自身. 6525 */ 6526 cue: function(cue) { 6527 var ptr = 0; 6528 for (var frame in cue) { 6529 if (cue.hasOwnProperty(frame)) { 6530 this.delay(frame - ptr); 6531 this.then(cue[frame]); 6532 ptr = frame; 6533 } 6534 } 6535 return this; 6536 }, 6537 /** 6538 * ある関数を指定したフレーム数繰り返し実行するアクションを追加する. 6539 * @param {Function} func 実行したい関数. 6540 * @param {Number} time 持続フレーム数. 6541 * @return {enchant.Timeline} 自身. 6542 */ 6543 repeat: function(func, time) { 6544 return this.action({ 6545 onactiontick: function(evt) { 6546 func.call(this); 6547 }, 6548 time: time 6549 }); 6550 }, 6551 /** 6552 * 複数のアクションを並列で実行したいときに指定する. 6553 * and で結ばれたすべてのアクションが終了するまで次のアクションには移行しない. 6554 * 6555 * @example 6556 * sprite.tl.fadeIn(30).and().rotateBy(360, 30); 6557 * 6558 * // 30フレームでフェードインしながら360度回転する. 6559 * @return {enchant.Timeline} 自身. 6560 */ 6561 and: function() { 6562 var last = this.queue.pop(); 6563 if (last instanceof enchant.ParallelAction) { 6564 this._parallel = last; 6565 this.queue.push(last); 6566 } else { 6567 var parallel = new enchant.ParallelAction(); 6568 parallel.actions.push(last); 6569 this.queue.push(parallel); 6570 this._parallel = parallel; 6571 } 6572 return this; 6573 }, 6574 /** 6575 * @ignore 6576 */ 6577 or: function() { 6578 return this; 6579 }, 6580 /** 6581 * @ignore 6582 */ 6583 doAll: function(children) { 6584 return this; 6585 }, 6586 /** 6587 * @ignore 6588 */ 6589 waitAll: function() { 6590 return this; 6591 }, 6592 /** 6593 * trueが返るまで, 関数を毎フレーム実行するアクションを追加する. 6594 * 6595 * @example 6596 * sprite.tl.waitUntil(function() { 6597 * return --this.x < 0; 6598 * }).then(function(){ .. }); 6599 * // x座標が負になるまで毎フレームx座標を減算し続ける. 6600 * 6601 * @param {Function} func 条件とする関数. 6602 * @return {enchant.Timeline} 自身. 6603 */ 6604 waitUntil: function(func) { 6605 return this.action({ 6606 onactiontick: function(evt) { 6607 if (func.call(this)) { 6608 evt.timeline.next(); 6609 } 6610 } 6611 }); 6612 }, 6613 /** 6614 * Entityの不透明度をなめらかに変えるアクションを追加する. 6615 * @param {Number} opacity 目標の不透明度. 6616 * @param {Number} time フレーム数. 6617 * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数. 6618 * @return {enchant.Timeline} 自身. 6619 */ 6620 fadeTo: function(opacity, time, easing) { 6621 return this.tween({ 6622 opacity: opacity, 6623 time: time, 6624 easing: easing 6625 }); 6626 }, 6627 /** 6628 * Entityをフェードインするアクションを追加する. 6629 * fadeTo(1, time, easing) のエイリアス. 6630 * @param {Number} time フレーム数. 6631 * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数. 6632 * @return {enchant.Timeline} 自身. 6633 */ 6634 fadeIn: function(time, easing) { 6635 return this.fadeTo(1, time, easing); 6636 }, 6637 /** 6638 * Entityをフェードアウトするアクションを追加する. 6639 * fadeTo(1, time, easing) のエイリアス. 6640 * @param {Number} time フレーム数. 6641 * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数. 6642 * @return {enchant.Timeline} 自身. 6643 */ 6644 fadeOut: function(time, easing) { 6645 return this.fadeTo(0, time, easing); 6646 }, 6647 /** 6648 * Entityの位置をなめらかに移動させるアクションを追加する. 6649 * @param {Number} x 目標のx座標. 6650 * @param {Number} y 目標のy座標. 6651 * @param {Number} time フレーム数. 6652 * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数. 6653 * @return {enchant.Timeline} 自身. 6654 */ 6655 moveTo: function(x, y, time, easing) { 6656 return this.tween({ 6657 x: x, 6658 y: y, 6659 time: time, 6660 easing: easing 6661 }); 6662 }, 6663 /** 6664 * Entityのx座標をなめらかに移動させるアクションを追加する. 6665 * @param {Number} x 目標のx座標. 6666 * @param {Number} time フレーム数. 6667 * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数. 6668 * @return {enchant.Timeline} 自身. 6669 */ 6670 moveX: function(x, time, easing) { 6671 return this.tween({ 6672 x: x, 6673 time: time, 6674 easing: easing 6675 }); 6676 }, 6677 /** 6678 * Entityのy座標をなめらかに移動させるアクションを追加する. 6679 * @param {Number} y 目標のy座標. 6680 * @param {Number} time フレーム数. 6681 * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数. 6682 * @return {enchant.Timeline} 自身. 6683 */ 6684 moveY: function(y, time, easing) { 6685 return this.tween({ 6686 y: y, 6687 time: time, 6688 easing: easing 6689 }); 6690 }, 6691 /** 6692 * Entityの位置をなめらかに変化させるアクションを追加する. 6693 * 座標は, アクション開始時からの相対座標で指定する. 6694 * @param {Number} x x軸方向の移動量. 6695 * @param {Number} y y軸方向の移動量. 6696 * @param {Number} time フレーム数. 6697 * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数. 6698 * @return {enchant.Timeline} 自身. 6699 */ 6700 moveBy: function(x, y, time, easing) { 6701 return this.tween({ 6702 x: function() { 6703 return this.x + x; 6704 }, 6705 y: function() { 6706 return this.y + y; 6707 }, 6708 time: time, 6709 easing: easing 6710 }); 6711 }, 6712 /** 6713 * Entityの不透明度を0にする. (即時) 6714 * @return {enchant.Timeline} 自身. 6715 */ 6716 hide: function() { 6717 return this.then(function() { 6718 this.opacity = 0; 6719 }); 6720 }, 6721 /** 6722 * Entityの不透明度を1にする. (即時) 6723 * @return {enchant.Timeline} 自身. 6724 */ 6725 show: function() { 6726 return this.then(function() { 6727 this.opacity = 1; 6728 }); 6729 }, 6730 /** 6731 * Entityをシーンから削除する. 6732 * シーンから削除された場合, enterframe イベントは呼ばれなくなるので, 6733 * タイムラインも止まることに注意. 6734 * これ以降のアクションは, 再度シーンに追加されるまで実行されない. 6735 * @return {enchant.Timeline} 自身. 6736 */ 6737 removeFromScene: function() { 6738 return this.then(function() { 6739 this.parentNode.removeChild(this); 6740 }); 6741 }, 6742 /** 6743 * Entityをなめらかに拡大・縮小するアクションを追加する. 6744 * @param {Number} scaleX x軸方向の縮尺. 6745 * @param {Number} [scaleY] y軸方向の縮尺. 省略した場合 scaleX と同じ. 6746 * @param {Number} time フレーム数. 6747 * @param {Function} [easing=enchant.Easing.LINEAR] 6748 * @return {enchant.Timeline} 自身. 6749 */ 6750 scaleTo: function(scale, time, easing) { 6751 var scaleX, scaleY; 6752 6753 if (typeof easing === "number") { 6754 scaleX = arguments[0]; 6755 scaleY = arguments[1]; 6756 time = arguments[2]; 6757 easing = arguments[3]; 6758 } else { 6759 scaleX = scaleY = scale; 6760 } 6761 6762 return this.tween({ 6763 scaleX: scaleX, 6764 scaleY: scaleY, 6765 time: time, 6766 easing: easing 6767 }); 6768 }, 6769 /** 6770 * Entityをなめらかに拡大・縮小させるアクションを追加する. 6771 * 相対縮尺 (アクション開始時の縮尺のn倍) で指定する. 6772 * @param {Number} scaleX x軸方向の相対縮尺. 6773 * @param {Number} [scaleY] y軸方向の相対縮尺. 省略した場合 scaleX と同じ. 6774 * @param {Number} time フレーム数. 6775 * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数. 6776 * @return {enchant.Timeline} 自身. 6777 */ 6778 scaleBy: function(scale, time, easing) { 6779 var scaleX, scaleY; 6780 6781 if (typeof easing === "number") { 6782 scaleX = arguments[0]; 6783 scaleY = arguments[1]; 6784 time = arguments[2]; 6785 easing = arguments[3]; 6786 } else { 6787 scaleX = scaleY = scale; 6788 } 6789 6790 return this.tween({ 6791 scaleX: function() { 6792 return this.scaleX * scaleX; 6793 }, 6794 scaleY: function() { 6795 return this.scaleY * scaleY; 6796 }, 6797 time: time, 6798 easing: easing 6799 }); 6800 }, 6801 /** 6802 * Entityをなめらかに回転させるアクションを追加する. 6803 * @param {Number} deg 目標の回転角度. (度数法) 6804 * @param {Number} time フレーム数. 6805 * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数. 6806 * @return {enchant.Timeline} 自身. 6807 */ 6808 rotateTo: function(deg, time, easing) { 6809 return this.tween({ 6810 rotation: deg, 6811 time: time, 6812 easing: easing 6813 }); 6814 }, 6815 /** 6816 * Entityをなめらかに回転させるアクションを追加する. 6817 * 角度は相対角度 (アクション開始時の角度から更にn度) で指定する. 6818 * @param {Number} deg 目標の相対角度. (度数法) 6819 * @param {Number} time フレーム数. 6820 * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数. 6821 * @return {enchant.Timeline} 自身. 6822 */ 6823 rotateBy: function(deg, time, easing) { 6824 return this.tween({ 6825 rotation: function() { 6826 return this.rotation + deg; 6827 }, 6828 time: time, 6829 easing: easing 6830 }); 6831 } 6832 }); 6833 6834 /** 6835 * @scope enchant.Action.prototype 6836 */ 6837 enchant.Action = enchant.Class.create(enchant.ActionEventTarget, { 6838 /** 6839 * @name enchant.Action 6840 * @class 6841 * アニメーションタイムラインを構成する, 実行したい処理を指定するためのクラス. 6842 * 6843 * タイムラインに追加されたアクションは順に実行される. 6844 * アクションが開始・終了された時に actionstart, actionend イベントが発行され, 6845 * また1フレーム経過した時には actiontick イベントが発行される. 6846 * これらのイベントのリスナとして実行したい処理を指定する. 6847 * 6848 * time で指定されたフレーム数が経過すると自動的に次のアクションに移行するが, 6849 * null が指定されると, タイムラインの next メソッドが呼ばれるまで移行しない. 6850 * @param {Object} param 6851 * @param {Number} [param.time] アクションが持続するフレーム数. null が指定されると無限長. 6852 * @param {Function} [param.onactionstart] アクションが開始される時のイベントリスナ. 6853 * @param {Function} [param.onactiontick] アクションが1フレーム経過するときのイベントリスナ. 6854 * @param {Function} [param.onactionend] アクションがが終了する時のイベントリスナ. 6855 * @constructs 6856 * @extends enchant.ActionEventTarget 6857 */ 6858 initialize: function(param) { 6859 enchant.ActionEventTarget.call(this); 6860 this.time = null; 6861 this.frame = 0; 6862 for (var key in param) { 6863 if (param.hasOwnProperty(key)) { 6864 if (param[key] != null) { 6865 this[key] = param[key]; 6866 } 6867 } 6868 } 6869 var action = this; 6870 6871 this.timeline = null; 6872 this.node = null; 6873 6874 this.addEventListener(enchant.Event.ADDED_TO_TIMELINE, function(evt) { 6875 action.timeline = evt.timeline; 6876 action.node = evt.timeline.node; 6877 action.frame = 0; 6878 }); 6879 6880 this.addEventListener(enchant.Event.REMOVED_FROM_TIMELINE, function() { 6881 action.timeline = null; 6882 action.node = null; 6883 action.frame = 0; 6884 }); 6885 6886 this.addEventListener(enchant.Event.ACTION_TICK, function(evt) { 6887 var remaining = action.time - (action.frame + evt.elapsed); 6888 if (action.time != null && remaining <= 0) { 6889 action.frame = action.time; 6890 evt.timeline.next(-remaining); 6891 } else { 6892 action.frame += evt.elapsed; 6893 } 6894 }); 6895 6896 } 6897 }); 6898 6899 /** 6900 * @scope enchant.ParallelAction.prototype 6901 */ 6902 enchant.ParallelAction = enchant.Class.create(enchant.Action, { 6903 /** 6904 * @name enchant.ParallelAction 6905 * @class 6906 * アクションを並列で実行するためのアクション. 6907 * 子アクションを複数持つことができる. 6908 * @constructs 6909 * @extends enchant.Action 6910 */ 6911 initialize: function(param) { 6912 enchant.Action.call(this, param); 6913 /** 6914 * 子アクション. 6915 * @type enchant.Action[] 6916 */ 6917 this.actions = []; 6918 /** 6919 * 実行が終了したアクション. 6920 * @type enchant.Action[] 6921 */ 6922 this.endedActions = []; 6923 var that = this; 6924 6925 this.addEventListener(enchant.Event.ACTION_START, function(evt) { 6926 for (var i = 0, len = that.actions.length; i < len; i++) { 6927 that.actions[i].dispatchEvent(evt); 6928 } 6929 }); 6930 6931 this.addEventListener(enchant.Event.ACTION_TICK, function(evt) { 6932 var i, len, timeline = { 6933 next: function(remaining) { 6934 var action = that.actions[i]; 6935 that.actions.splice(i--, 1); 6936 len = that.actions.length; 6937 that.endedActions.push(action); 6938 6939 var e = new enchant.Event("actionend"); 6940 e.timeline = this; 6941 action.dispatchEvent(e); 6942 6943 e = new enchant.Event("removedfromtimeline"); 6944 e.timeline = this; 6945 action.dispatchEvent(e); 6946 } 6947 }; 6948 6949 var e = new enchant.Event("actiontick"); 6950 e.timeline = timeline; 6951 e.elapsed = evt.elapsed; 6952 for (i = 0, len = that.actions.length; i < len; i++) { 6953 that.actions[i].dispatchEvent(e); 6954 } 6955 6956 if (that.actions.length === 0) { 6957 evt.timeline.next(); 6958 } 6959 }); 6960 6961 this.addEventListener(enchant.Event.ADDED_TO_TIMELINE, function(evt) { 6962 for (var i = 0, len = that.actions.length; i < len; i++) { 6963 that.actions[i].dispatchEvent(evt); 6964 } 6965 }); 6966 6967 this.addEventListener(enchant.Event.REMOVED_FROM_TIMELINE, function() { 6968 that.actions = that.endedActions; 6969 that.endedActions = []; 6970 }); 6971 6972 } 6973 }); 6974 6975 /** 6976 * @scope enchant.Tween.prototype 6977 */ 6978 enchant.Tween = enchant.Class.create(enchant.Action, { 6979 /** 6980 * @name enchant.Tween 6981 * @class 6982 * オブジェクトの特定のプロパティを, なめらかに変更したい時に用いるためのアクションクラス. 6983 * アクションを扱いやすく拡張したクラス. 6984 * 6985 * コンストラクタに渡す設定オブジェクトに, プロパティの目標値を指定すると, 6986 * アクションが実行された時に, 目標値までなめらかに値を変更するようなアクションを生成する. 6987 * 6988 * トゥイーンのイージングも, easing プロパティで指定できる. 6989 * 6990 * @param {Object} params 6991 * @param {Number} params.time アニメーションにかける時間. 6992 * @param {Function} [params.easing=enchant.Easing.LINEAR] イージング関数. 6993 * @constructs 6994 * @extends enchant.Action 6995 */ 6996 initialize: function(params) { 6997 var origin = {}; 6998 var target = {}; 6999 enchant.Action.call(this, params); 7000 7001 if (this.easing == null) { 7002 this.easing = enchant.Easing.LINEAR; 7003 } 7004 7005 var tween = this; 7006 this.addEventListener(enchant.Event.ACTION_START, function() { 7007 // excepted property 7008 var excepted = ["frame", "time", "callback", "onactiontick", "onactionstart", "onactionend"]; 7009 for (var prop in params) { 7010 if (params.hasOwnProperty(prop)) { 7011 // if function is used instead of numerical value, evaluate it 7012 var target_val; 7013 if (typeof params[prop] === "function") { 7014 target_val = params[prop].call(tween.node); 7015 } else { 7016 target_val = params[prop]; 7017 } 7018 7019 if (excepted.indexOf(prop) === -1) { 7020 origin[prop] = tween.node[prop]; 7021 target[prop] = target_val; 7022 } 7023 } 7024 } 7025 }); 7026 7027 this.addEventListener(enchant.Event.ACTION_TICK, function(evt) { 7028 // if time is 0, set property to target value immediately 7029 var ratio = tween.time === 0 ? 1 : tween.easing(Math.min(tween.time,tween.frame + evt.elapsed), 0, 1, tween.time) - tween.easing(tween.frame, 0, 1, tween.time); 7030 7031 for (var prop in target){ 7032 if (target.hasOwnProperty(prop)) { 7033 if (typeof this[prop] === "undefined"){ 7034 continue; 7035 } 7036 tween.node[prop] += (target[prop] - origin[prop]) * ratio; 7037 if (Math.abs(tween.node[prop]) < 10e-8){ 7038 tween.node[prop] = 0; 7039 } 7040 } 7041 } 7042 }); 7043 } 7044 }); 7045 7046 }(window)); 7047