1 /**************************************************************************** 2 Copyright (c) 2008-2010 Ricardo Quesada 3 Copyright (c) 2011-2012 cocos2d-x.org 4 Copyright (c) 2013-2014 Chukong Technologies Inc. 5 6 http://www.cocos2d-x.org 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy 9 of this software and associated documentation files (the "Software"), to deal 10 in the Software without restriction, including without limitation the rights 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 copies of the Software, and to permit persons to whom the Software is 13 furnished to do so, subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be included in 16 all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 THE SOFTWARE. 25 ****************************************************************************/ 26 27 /** 28 * @ignore 29 */ 30 cc.Touches = []; 31 cc.TouchesIntergerDict = {}; 32 33 cc.DENSITYDPI_DEVICE = "device-dpi"; 34 cc.DENSITYDPI_HIGH = "high-dpi"; 35 cc.DENSITYDPI_MEDIUM = "medium-dpi"; 36 cc.DENSITYDPI_LOW = "low-dpi"; 37 38 cc.__BrowserGetter = { 39 init: function(){ 40 this.html = document.getElementsByTagName("html")[0]; 41 }, 42 availWidth: function(frame){ 43 if(!frame || frame === this.html) 44 return window.innerWidth; 45 else 46 return frame.clientWidth; 47 }, 48 availHeight: function(frame){ 49 if(!frame || frame === this.html) 50 return window.innerHeight; 51 else 52 return frame.clientHeight; 53 }, 54 meta: { 55 "width": "device-width", 56 "user-scalable": "no" 57 }, 58 adaptationType: cc.sys.browserType 59 }; 60 61 if(window.navigator.userAgent.indexOf("OS 8_1_") > -1) //this mistake like MIUI, so use of MIUI treatment method 62 cc.__BrowserGetter.adaptationType = cc.sys.BROWSER_TYPE_MIUI; 63 64 if(cc.sys.os === cc.sys.OS_IOS) // All browsers are WebView 65 cc.__BrowserGetter.adaptationType = cc.sys.BROWSER_TYPE_SAFARI; 66 67 switch(cc.__BrowserGetter.adaptationType){ 68 case cc.sys.BROWSER_TYPE_SAFARI: 69 cc.__BrowserGetter.meta["minimal-ui"] = "true"; 70 cc.__BrowserGetter.availWidth = function(frame){ 71 return frame.clientWidth; 72 }; 73 cc.__BrowserGetter.availHeight = function(frame){ 74 return frame.clientHeight; 75 }; 76 break; 77 case cc.sys.BROWSER_TYPE_CHROME: 78 cc.__BrowserGetter.__defineGetter__("target-densitydpi", function(){ 79 return cc.view._targetDensityDPI; 80 }); 81 case cc.sys.BROWSER_TYPE_SOUGOU: 82 case cc.sys.BROWSER_TYPE_UC: 83 cc.__BrowserGetter.availWidth = function(frame){ 84 return frame.clientWidth; 85 }; 86 cc.__BrowserGetter.availHeight = function(frame){ 87 return frame.clientHeight; 88 }; 89 break; 90 case cc.sys.BROWSER_TYPE_MIUI: 91 cc.__BrowserGetter.init = function(view){ 92 if(view.__resizeWithBrowserSize) return; 93 var resize = function(){ 94 view.setDesignResolutionSize( 95 view._designResolutionSize.width, 96 view._designResolutionSize.height, 97 view._resolutionPolicy 98 ); 99 window.removeEventListener("resize", resize, false); 100 }; 101 window.addEventListener("resize", resize, false); 102 }; 103 break; 104 } 105 106 /** 107 * cc.view is the singleton object which represents the game window.<br/> 108 * It's main task include: <br/> 109 * - Apply the design resolution policy<br/> 110 * - Provide interaction with the window, like resize event on web, retina display support, etc...<br/> 111 * - Manage the game view port which can be different with the window<br/> 112 * - Manage the content scale and translation<br/> 113 * <br/> 114 * Since the cc.view is a singleton, you don't need to call any constructor or create functions,<br/> 115 * the standard way to use it is by calling:<br/> 116 * - cc.view.methodName(); <br/> 117 * @class 118 * @name cc.view 119 * @extend cc.Class 120 */ 121 cc.EGLView = cc.Class.extend(/** @lends cc.view# */{ 122 _delegate: null, 123 // Size of parent node that contains cc.container and cc._canvas 124 _frameSize: null, 125 // resolution size, it is the size appropriate for the app resources. 126 _designResolutionSize: null, 127 _originalDesignResolutionSize: null, 128 // Viewport is the container's rect related to content's coordinates in pixel 129 _viewPortRect: null, 130 // The visible rect in content's coordinate in point 131 _visibleRect: null, 132 _retinaEnabled: false, 133 _autoFullScreen: true, 134 // The device's pixel ratio (for retina displays) 135 _devicePixelRatio: 1, 136 // the view name 137 _viewName: "", 138 // Custom callback for resize event 139 _resizeCallback: null, 140 _scaleX: 1, 141 _originalScaleX: 1, 142 _scaleY: 1, 143 _originalScaleY: 1, 144 _indexBitsUsed: 0, 145 _maxTouches: 5, 146 _resolutionPolicy: null, 147 _rpExactFit: null, 148 _rpShowAll: null, 149 _rpNoBorder: null, 150 _rpFixedHeight: null, 151 _rpFixedWidth: null, 152 _initialized: false, 153 154 _captured: false, 155 _wnd: null, 156 _hDC: null, 157 _hRC: null, 158 _supportTouch: false, 159 _contentTranslateLeftTop: null, 160 161 // Parent node that contains cc.container and cc._canvas 162 _frame: null, 163 _frameZoomFactor: 1.0, 164 __resizeWithBrowserSize: false, 165 _isAdjustViewPort: true, 166 _targetDensityDPI: null, 167 168 /** 169 * Constructor of cc.EGLView 170 */ 171 ctor: function () { 172 var _t = this, d = document, _strategyer = cc.ContainerStrategy, _strategy = cc.ContentStrategy; 173 174 cc.__BrowserGetter.init(this); 175 176 _t._frame = (cc.container.parentNode === d.body) ? d.documentElement : cc.container.parentNode; 177 _t._frameSize = cc.size(0, 0); 178 _t._initFrameSize(); 179 180 var w = cc._canvas.width, h = cc._canvas.height; 181 _t._designResolutionSize = cc.size(w, h); 182 _t._originalDesignResolutionSize = cc.size(w, h); 183 _t._viewPortRect = cc.rect(0, 0, w, h); 184 _t._visibleRect = cc.rect(0, 0, w, h); 185 _t._contentTranslateLeftTop = {left: 0, top: 0}; 186 _t._viewName = "Cocos2dHTML5"; 187 188 var sys = cc.sys; 189 _t.enableRetina(sys.os === sys.OS_IOS || sys.os === sys.OS_OSX); 190 cc.visibleRect && cc.visibleRect.init(_t._visibleRect); 191 192 // Setup system default resolution policies 193 _t._rpExactFit = new cc.ResolutionPolicy(_strategyer.EQUAL_TO_FRAME, _strategy.EXACT_FIT); 194 _t._rpShowAll = new cc.ResolutionPolicy(_strategyer.PROPORTION_TO_FRAME, _strategy.SHOW_ALL); 195 _t._rpNoBorder = new cc.ResolutionPolicy(_strategyer.EQUAL_TO_FRAME, _strategy.NO_BORDER); 196 _t._rpFixedHeight = new cc.ResolutionPolicy(_strategyer.EQUAL_TO_FRAME, _strategy.FIXED_HEIGHT); 197 _t._rpFixedWidth = new cc.ResolutionPolicy(_strategyer.EQUAL_TO_FRAME, _strategy.FIXED_WIDTH); 198 199 _t._hDC = cc._canvas; 200 _t._hRC = cc._renderContext; 201 _t._targetDensityDPI = cc.DENSITYDPI_HIGH; 202 }, 203 204 // Resize helper functions 205 _resizeEvent: function () { 206 var view; 207 if(this.setDesignResolutionSize){ 208 view = this; 209 }else{ 210 view = cc.view; 211 } 212 213 // Check frame size changed or not 214 var prevFrameW = view._frameSize.width, prevFrameH = view._frameSize.height; 215 view._initFrameSize(); 216 if (view._frameSize.width === prevFrameW && view._frameSize.height === prevFrameH) 217 return; 218 219 // Frame size changed, do resize works 220 if (view._resizeCallback) { 221 view._resizeCallback.call(); 222 } 223 var width = view._originalDesignResolutionSize.width; 224 var height = view._originalDesignResolutionSize.height; 225 if (width > 0) 226 view.setDesignResolutionSize(width, height, view._resolutionPolicy); 227 }, 228 229 /** 230 * <p> 231 * Sets view's target-densitydpi for android mobile browser. it can be set to: <br/> 232 * 1. cc.DENSITYDPI_DEVICE, value is "device-dpi" <br/> 233 * 2. cc.DENSITYDPI_HIGH, value is "high-dpi" (default value) <br/> 234 * 3. cc.DENSITYDPI_MEDIUM, value is "medium-dpi" (browser's default value) <br/> 235 * 4. cc.DENSITYDPI_LOW, value is "low-dpi" <br/> 236 * 5. Custom value, e.g: "480" <br/> 237 * </p> 238 * @param {String} densityDPI 239 */ 240 setTargetDensityDPI: function(densityDPI){ 241 this._targetDensityDPI = densityDPI; 242 this._adjustViewportMeta(); 243 }, 244 245 /** 246 * Returns the current target-densitydpi value of cc.view. 247 * @returns {String} 248 */ 249 getTargetDensityDPI: function(){ 250 return this._targetDensityDPI; 251 }, 252 253 /** 254 * Sets whether resize canvas automatically when browser's size changed.<br/> 255 * Useful only on web. 256 * @param {Boolean} enabled Whether enable automatic resize with browser's resize event 257 */ 258 resizeWithBrowserSize: function (enabled) { 259 if (enabled) { 260 //enable 261 if (!this.__resizeWithBrowserSize) { 262 this.__resizeWithBrowserSize = true; 263 cc._addEventListener(window, 'resize', this._resizeEvent); 264 cc._addEventListener(window, 'orientationchange', this._resizeEvent); 265 } 266 } else { 267 //disable 268 if (this.__resizeWithBrowserSize) { 269 this.__resizeWithBrowserSize = false; 270 window.removeEventListener('resize', this._resizeEvent); 271 window.removeEventListener('orientationchange', this._resizeEvent); 272 } 273 } 274 }, 275 276 /** 277 * Sets the callback function for cc.view's resize action,<br/> 278 * this callback will be invoked before applying resolution policy, <br/> 279 * so you can do any additional modifications within the callback.<br/> 280 * Useful only on web. 281 * @param {Function|null} callback The callback function 282 */ 283 setResizeCallback: function (callback) { 284 if (cc.isFunction(callback) || callback == null) { 285 this._resizeCallback = callback; 286 } 287 }, 288 289 _initFrameSize: function () { 290 var locFrameSize = this._frameSize; 291 locFrameSize.width = cc.__BrowserGetter.availWidth(this._frame); 292 locFrameSize.height = cc.__BrowserGetter.availHeight(this._frame); 293 }, 294 295 // hack 296 _adjustSizeKeepCanvasSize: function () { 297 var designWidth = this._originalDesignResolutionSize.width; 298 var designHeight = this._originalDesignResolutionSize.height; 299 if (designWidth > 0) 300 this.setDesignResolutionSize(designWidth, designHeight, this._resolutionPolicy); 301 }, 302 303 _setViewportMeta: function (metas, overwrite) { 304 var vp = document.getElementById("cocosMetaElement"); 305 if(vp){ 306 document.head.removeChild(vp); 307 } 308 309 var elems = document.getElementsByName("viewport"), 310 currentVP = elems ? elems[0] : null, 311 content, key, pattern; 312 313 vp = cc.newElement("meta"); 314 vp.id = "cocosMetaElement"; 315 vp.name = "viewport"; 316 vp.content = ""; 317 318 content = currentVP ? currentVP.content : ""; 319 for (key in metas) { 320 if (content.indexOf(key) == -1) { 321 content += "," + key + "=" + metas[key]; 322 } 323 else if (overwrite) { 324 pattern = new RegExp(key+"\s*=\s*[^,]+"); 325 content.replace(pattern, key + "=" + metas[key]); 326 } 327 } 328 if(/^,/.test(content)) 329 content = content.substr(1); 330 331 vp.content = content; 332 // For adopting certain android devices which don't support second viewport 333 if (currentVP) 334 currentVP.content = content; 335 336 document.head.appendChild(vp); 337 }, 338 339 _adjustViewportMeta: function () { 340 if (this._isAdjustViewPort) { 341 this._setViewportMeta(cc.__BrowserGetter.meta, false); 342 } 343 }, 344 345 // RenderTexture hacker 346 _setScaleXYForRenderTexture: function () { 347 //hack for RenderTexture on canvas mode when adapting multiple resolution resources 348 var scaleFactor = cc.contentScaleFactor(); 349 this._scaleX = scaleFactor; 350 this._scaleY = scaleFactor; 351 }, 352 353 // Other helper functions 354 _resetScale: function () { 355 this._scaleX = this._originalScaleX; 356 this._scaleY = this._originalScaleY; 357 }, 358 359 // Useless, just make sure the compatibility temporarily, should be removed 360 _adjustSizeToBrowser: function () { 361 }, 362 363 initialize: function () { 364 this._initialized = true; 365 }, 366 367 /** 368 * Sets whether the engine modify the "viewport" meta in your web page.<br/> 369 * It's enabled by default, we strongly suggest you not to disable it.<br/> 370 * And even when it's enabled, you can still set your own "viewport" meta, it won't be overridden<br/> 371 * Only useful on web 372 * @param {Boolean} enabled Enable automatic modification to "viewport" meta 373 */ 374 adjustViewPort: function (enabled) { 375 this._isAdjustViewPort = enabled; 376 }, 377 378 /** 379 * Retina support is enabled by default for Apple device but disabled for other devices,<br/> 380 * it takes effect only when you called setDesignResolutionPolicy<br/> 381 * Only useful on web 382 * @param {Boolean} enabled Enable or disable retina display 383 */ 384 enableRetina: function(enabled) { 385 this._retinaEnabled = enabled ? true : false; 386 }, 387 388 /** 389 * Check whether retina display is enabled.<br/> 390 * Only useful on web 391 * @return {Boolean} 392 */ 393 isRetinaEnabled: function() { 394 return this._retinaEnabled; 395 }, 396 397 /** 398 * If enabled, the application will try automatically to enter full screen mode on mobile devices<br/> 399 * You can pass true as parameter to enable it and disable it by passing false.<br/> 400 * Only useful on web 401 * @param {Boolean} enabled Enable or disable auto full screen on mobile devices 402 */ 403 enableAutoFullScreen: function(enabled) { 404 this._autoFullScreen = enabled ? true : false; 405 }, 406 407 /** 408 * Check whether auto full screen is enabled.<br/> 409 * Only useful on web 410 * @return {Boolean} Auto full screen enabled or not 411 */ 412 isAutoFullScreenEnabled: function() { 413 return this._autoFullScreen; 414 }, 415 416 /** 417 * Force destroying EGL view, subclass must implement this method. 418 */ 419 end: function () { 420 }, 421 422 /** 423 * Get whether render system is ready(no matter opengl or canvas),<br/> 424 * this name is for the compatibility with cocos2d-x, subclass must implement this method. 425 * @return {Boolean} 426 */ 427 isOpenGLReady: function () { 428 return (this._hDC !== null && this._hRC !== null); 429 }, 430 431 /* 432 * Set zoom factor for frame. This method is for debugging big resolution (e.g.new ipad) app on desktop. 433 * @param {Number} zoomFactor 434 */ 435 setFrameZoomFactor: function (zoomFactor) { 436 this._frameZoomFactor = zoomFactor; 437 this.centerWindow(); 438 cc.director.setProjection(cc.director.getProjection()); 439 }, 440 441 /** 442 * Exchanges the front and back buffers, subclass must implement this method. 443 */ 444 swapBuffers: function () { 445 }, 446 447 /** 448 * Open or close IME keyboard , subclass must implement this method. 449 * @param {Boolean} isOpen 450 */ 451 setIMEKeyboardState: function (isOpen) { 452 }, 453 454 /** 455 * Sets the resolution translate on EGLView 456 * @param {Number} offsetLeft 457 * @param {Number} offsetTop 458 */ 459 setContentTranslateLeftTop: function (offsetLeft, offsetTop) { 460 this._contentTranslateLeftTop = {left: offsetLeft, top: offsetTop}; 461 }, 462 463 /** 464 * Returns the resolution translate on EGLView 465 * @return {cc.Size|Object} 466 */ 467 getContentTranslateLeftTop: function () { 468 return this._contentTranslateLeftTop; 469 }, 470 471 /** 472 * Returns the frame size of the view.<br/> 473 * On native platforms, it returns the screen size since the view is a fullscreen view.<br/> 474 * On web, it returns the size of the canvas's outer DOM element. 475 * @return {cc.Size} 476 */ 477 getFrameSize: function () { 478 return cc.size(this._frameSize.width, this._frameSize.height); 479 }, 480 481 /** 482 * On native, it sets the frame size of view.<br/> 483 * On web, it sets the size of the canvas's outer DOM element. 484 * @param {Number} width 485 * @param {Number} height 486 */ 487 setFrameSize: function (width, height) { 488 this._frameSize.width = width; 489 this._frameSize.height = height; 490 this._frame.style.width = width + "px"; 491 this._frame.style.height = height + "px"; 492 //this.centerWindow(); 493 this._resizeEvent(); 494 cc.director.setProjection(cc.director.getProjection()); 495 }, 496 497 /** 498 * Empty function 499 */ 500 centerWindow: function () { 501 }, 502 503 /** 504 * Returns the visible area size of the view port. 505 * @return {cc.Size} 506 */ 507 getVisibleSize: function () { 508 return cc.size(this._visibleRect.width,this._visibleRect.height); 509 }, 510 511 /** 512 * Returns the visible origin of the view port. 513 * @return {cc.Point} 514 */ 515 getVisibleOrigin: function () { 516 return cc.p(this._visibleRect.x,this._visibleRect.y); 517 }, 518 519 /** 520 * Returns whether developer can set content's scale factor. 521 * @return {Boolean} 522 */ 523 canSetContentScaleFactor: function () { 524 return true; 525 }, 526 527 /** 528 * Returns the current resolution policy 529 * @see cc.ResolutionPolicy 530 * @return {cc.ResolutionPolicy} 531 */ 532 getResolutionPolicy: function () { 533 return this._resolutionPolicy; 534 }, 535 536 /** 537 * Sets the current resolution policy 538 * @see cc.ResolutionPolicy 539 * @param {cc.ResolutionPolicy|Number} resolutionPolicy 540 */ 541 setResolutionPolicy: function (resolutionPolicy) { 542 var _t = this; 543 if (resolutionPolicy instanceof cc.ResolutionPolicy) { 544 _t._resolutionPolicy = resolutionPolicy; 545 } 546 // Ensure compatibility with JSB 547 else { 548 var _locPolicy = cc.ResolutionPolicy; 549 if(resolutionPolicy === _locPolicy.EXACT_FIT) 550 _t._resolutionPolicy = _t._rpExactFit; 551 if(resolutionPolicy === _locPolicy.SHOW_ALL) 552 _t._resolutionPolicy = _t._rpShowAll; 553 if(resolutionPolicy === _locPolicy.NO_BORDER) 554 _t._resolutionPolicy = _t._rpNoBorder; 555 if(resolutionPolicy === _locPolicy.FIXED_HEIGHT) 556 _t._resolutionPolicy = _t._rpFixedHeight; 557 if(resolutionPolicy === _locPolicy.FIXED_WIDTH) 558 _t._resolutionPolicy = _t._rpFixedWidth; 559 } 560 }, 561 562 /** 563 * Sets the resolution policy with designed view size in points.<br/> 564 * The resolution policy include: <br/> 565 * [1] ResolutionExactFit Fill screen by stretch-to-fit: if the design resolution ratio of width to height is different from the screen resolution ratio, your game view will be stretched.<br/> 566 * [2] ResolutionNoBorder Full screen without black border: if the design resolution ratio of width to height is different from the screen resolution ratio, two areas of your game view will be cut.<br/> 567 * [3] ResolutionShowAll Full screen with black border: if the design resolution ratio of width to height is different from the screen resolution ratio, two black borders will be shown.<br/> 568 * [4] ResolutionFixedHeight Scale the content's height to screen's height and proportionally scale its width<br/> 569 * [5] ResolutionFixedWidth Scale the content's width to screen's width and proportionally scale its height<br/> 570 * [cc.ResolutionPolicy] [Web only feature] Custom resolution policy, constructed by cc.ResolutionPolicy<br/> 571 * @param {Number} width Design resolution width. 572 * @param {Number} height Design resolution height. 573 * @param {cc.ResolutionPolicy|Number} resolutionPolicy The resolution policy desired 574 */ 575 setDesignResolutionSize: function (width, height, resolutionPolicy) { 576 // Defensive code 577 if( !(width > 0 || height > 0) ){ 578 cc.log(cc._LogInfos.EGLView_setDesignResolutionSize); 579 return; 580 } 581 582 this.setResolutionPolicy(resolutionPolicy); 583 var policy = this._resolutionPolicy; 584 if (!policy){ 585 cc.log(cc._LogInfos.EGLView_setDesignResolutionSize_2); 586 return; 587 } 588 policy.preApply(this); 589 590 // Reinit frame size 591 if(cc.sys.isMobile) 592 this._adjustViewportMeta(); 593 594 this._initFrameSize(); 595 596 this._originalDesignResolutionSize.width = this._designResolutionSize.width = width; 597 this._originalDesignResolutionSize.height = this._designResolutionSize.height = height; 598 599 var result = policy.apply(this, this._designResolutionSize); 600 601 if(result.scale && result.scale.length === 2){ 602 this._scaleX = result.scale[0]; 603 this._scaleY = result.scale[1]; 604 } 605 606 if(result.viewport){ 607 var vp = this._viewPortRect, 608 vb = this._visibleRect, 609 rv = result.viewport; 610 611 vp.x = rv.x; 612 vp.y = rv.y; 613 vp.width = rv.width; 614 vp.height = rv.height; 615 616 vb.x = -vp.x / this._scaleX; 617 vb.y = -vp.y / this._scaleY; 618 vb.width = cc._canvas.width / this._scaleX; 619 vb.height = cc._canvas.height / this._scaleY; 620 cc._renderContext.setOffset && cc._renderContext.setOffset(vp.x, -vp.y) 621 } 622 623 // reset director's member variables to fit visible rect 624 var director = cc.director; 625 director._winSizeInPoints.width = this._designResolutionSize.width; 626 director._winSizeInPoints.height = this._designResolutionSize.height; 627 policy.postApply(this); 628 cc.winSize.width = director._winSizeInPoints.width; 629 cc.winSize.height = director._winSizeInPoints.height; 630 631 if (cc._renderType === cc._RENDER_TYPE_WEBGL) { 632 // reset director's member variables to fit visible rect 633 director._createStatsLabel(); 634 director.setGLDefaultValues(); 635 } 636 637 this._originalScaleX = this._scaleX; 638 this._originalScaleY = this._scaleY; 639 // For editbox 640 if (cc.DOM) 641 cc.DOM._resetEGLViewDiv(); 642 cc.visibleRect && cc.visibleRect.init(this._visibleRect); 643 }, 644 645 /** 646 * Returns the designed size for the view. 647 * Default resolution size is the same as 'getFrameSize'. 648 * @return {cc.Size} 649 */ 650 getDesignResolutionSize: function () { 651 return cc.size(this._designResolutionSize.width, this._designResolutionSize.height); 652 }, 653 654 /** 655 * Sets the document body to desired pixel resolution and fit the game content to it. 656 * This function is very useful for adaptation in mobile browsers. 657 * In some HD android devices, the resolution is very high, but its browser performance may not be very good. 658 * In this case, enabling retina display is very costy and not suggested, and if retina is disabled, the image may be blurry. 659 * But this API can be helpful to set a desired pixel resolution which is in between. 660 * This API will do the following: 661 * 1. Set viewport's width to the desired width in pixel 662 * 2. Set body width to the exact pixel resolution 663 * 3. The resolution policy will be reset with designed view size in points. 664 * @param {Number} width Design resolution width. 665 * @param {Number} height Design resolution height. 666 * @param {cc.ResolutionPolicy|Number} resolutionPolicy The resolution policy desired 667 */ 668 setRealPixelResolution: function (width, height, resolutionPolicy) { 669 // Set viewport's width 670 this._setViewportMeta({"width": width, "user-scalable": "no"}, true); 671 672 // Set body width to the exact pixel resolution 673 document.body.style.width = width + "px"; 674 document.body.style.height = "100%"; 675 document.body.style.left = "0px"; 676 document.body.style.top = "0px"; 677 678 // Reset the resolution size and policy 679 this.setDesignResolutionSize(width, height, resolutionPolicy); 680 }, 681 682 /** 683 * Sets view port rectangle with points. 684 * @param {Number} x 685 * @param {Number} y 686 * @param {Number} w width 687 * @param {Number} h height 688 */ 689 setViewPortInPoints: function (x, y, w, h) { 690 var locFrameZoomFactor = this._frameZoomFactor, locScaleX = this._scaleX, locScaleY = this._scaleY; 691 cc._renderContext.viewport((x * locScaleX * locFrameZoomFactor + this._viewPortRect.x * locFrameZoomFactor), 692 (y * locScaleY * locFrameZoomFactor + this._viewPortRect.y * locFrameZoomFactor), 693 (w * locScaleX * locFrameZoomFactor), 694 (h * locScaleY * locFrameZoomFactor)); 695 }, 696 697 /** 698 * Sets Scissor rectangle with points. 699 * @param {Number} x 700 * @param {Number} y 701 * @param {Number} w 702 * @param {Number} h 703 */ 704 setScissorInPoints: function (x, y, w, h) { 705 var locFrameZoomFactor = this._frameZoomFactor, locScaleX = this._scaleX, locScaleY = this._scaleY; 706 cc._renderContext.scissor((x * locScaleX * locFrameZoomFactor + this._viewPortRect.x * locFrameZoomFactor), 707 (y * locScaleY * locFrameZoomFactor + this._viewPortRect.y * locFrameZoomFactor), 708 (w * locScaleX * locFrameZoomFactor), 709 (h * locScaleY * locFrameZoomFactor)); 710 }, 711 712 /** 713 * Returns whether GL_SCISSOR_TEST is enable 714 * @return {Boolean} 715 */ 716 isScissorEnabled: function () { 717 var gl = cc._renderContext; 718 return gl.isEnabled(gl.SCISSOR_TEST); 719 }, 720 721 /** 722 * Returns the current scissor rectangle 723 * @return {cc.Rect} 724 */ 725 getScissorRect: function () { 726 var gl = cc._renderContext, scaleX = this._scaleX, scaleY = this._scaleY; 727 var boxArr = gl.getParameter(gl.SCISSOR_BOX); 728 return cc.rect((boxArr[0] - this._viewPortRect.x) / scaleX, (boxArr[1] - this._viewPortRect.y) / scaleY, 729 boxArr[2] / scaleX, boxArr[3] / scaleY); 730 }, 731 732 /** 733 * Sets the name of the view 734 * @param {String} viewName 735 */ 736 setViewName: function (viewName) { 737 if (viewName != null && viewName.length > 0) { 738 this._viewName = viewName; 739 } 740 }, 741 742 /** 743 * Returns the name of the view 744 * @return {String} 745 */ 746 getViewName: function () { 747 return this._viewName; 748 }, 749 750 /** 751 * Returns the view port rectangle. 752 * @return {cc.Rect} 753 */ 754 getViewPortRect: function () { 755 return this._viewPortRect; 756 }, 757 758 /** 759 * Returns scale factor of the horizontal direction (X axis). 760 * @return {Number} 761 */ 762 getScaleX: function () { 763 return this._scaleX; 764 }, 765 766 /** 767 * Returns scale factor of the vertical direction (Y axis). 768 * @return {Number} 769 */ 770 getScaleY: function () { 771 return this._scaleY; 772 }, 773 774 /** 775 * Returns device pixel ratio for retina display. 776 * @return {Number} 777 */ 778 getDevicePixelRatio: function() { 779 return this._devicePixelRatio; 780 }, 781 782 /** 783 * Returns the real location in view for a translation based on a related position 784 * @param {Number} tx The X axis translation 785 * @param {Number} ty The Y axis translation 786 * @param {Object} relatedPos The related position object including "left", "top", "width", "height" informations 787 * @return {cc.Point} 788 */ 789 convertToLocationInView: function (tx, ty, relatedPos) { 790 return {x: this._devicePixelRatio * (tx - relatedPos.left), y: this._devicePixelRatio * (relatedPos.top + relatedPos.height - ty)}; 791 }, 792 793 _convertMouseToLocationInView: function(point, relatedPos) { 794 var locViewPortRect = this._viewPortRect, _t = this; 795 point.x = ((_t._devicePixelRatio * (point.x - relatedPos.left)) - locViewPortRect.x) / _t._scaleX; 796 point.y = (_t._devicePixelRatio * (relatedPos.top + relatedPos.height - point.y) - locViewPortRect.y) / _t._scaleY; 797 }, 798 799 _convertTouchesWithScale: function(touches){ 800 var locViewPortRect = this._viewPortRect, locScaleX = this._scaleX, locScaleY = this._scaleY, selTouch, selPoint, selPrePoint; 801 for( var i = 0; i < touches.length; i ++){ 802 selTouch = touches[i]; 803 selPoint = selTouch._point; 804 selPrePoint = selTouch._prevPoint; 805 selTouch._setPoint((selPoint.x - locViewPortRect.x) / locScaleX, 806 (selPoint.y - locViewPortRect.y) / locScaleY); 807 selTouch._setPrevPoint((selPrePoint.x - locViewPortRect.x) / locScaleX, 808 (selPrePoint.y - locViewPortRect.y) / locScaleY); 809 } 810 } 811 }); 812 813 /** 814 * @function 815 * @return {cc.EGLView} 816 * @private 817 */ 818 cc.EGLView._getInstance = function () { 819 if (!this._instance) { 820 this._instance = this._instance || new cc.EGLView(); 821 this._instance.initialize(); 822 } 823 return this._instance; 824 }; 825 826 /** 827 * <p>cc.ContainerStrategy class is the root strategy class of container's scale strategy, 828 * it controls the behavior of how to scale the cc.container and cc._canvas object</p> 829 * 830 * @class 831 * @extends cc.Class 832 */ 833 cc.ContainerStrategy = cc.Class.extend(/** @lends cc.ContainerStrategy# */{ 834 /** 835 * Manipulation before appling the strategy 836 * @param {cc.view} The target view 837 */ 838 preApply: function (view) { 839 }, 840 841 /** 842 * Function to apply this strategy 843 * @param {cc.view} view 844 * @param {cc.Size} designedResolution 845 */ 846 apply: function (view, designedResolution) { 847 }, 848 849 /** 850 * Manipulation after applying the strategy 851 * @param {cc.view} view The target view 852 */ 853 postApply: function (view) { 854 855 }, 856 857 _setupContainer: function (view, w, h) { 858 var frame = view._frame; 859 if (cc.view._autoFullScreen && cc.sys.isMobile && frame === document.documentElement) { 860 // Automatically full screen when user touches on mobile version 861 cc.screen.autoFullScreen(frame); 862 } 863 864 var locCanvasElement = cc._canvas, locContainer = cc.container; 865 // Setup container 866 locContainer.style.width = locCanvasElement.style.width = w + "px"; 867 locContainer.style.height = locCanvasElement.style.height = h + "px"; 868 // Setup pixel ratio for retina display 869 var devicePixelRatio = view._devicePixelRatio = 1; 870 if (view.isRetinaEnabled()) 871 devicePixelRatio = view._devicePixelRatio = window.devicePixelRatio || 1; 872 // Setup canvas 873 locCanvasElement.width = w * devicePixelRatio; 874 locCanvasElement.height = h * devicePixelRatio; 875 cc._renderContext.resetCache && cc._renderContext.resetCache(); 876 877 var body = document.body, style; 878 if (body && (style = body.style)) { 879 style.paddingTop = style.paddingTop || "0px"; 880 style.paddingRight = style.paddingRight || "0px"; 881 style.paddingBottom = style.paddingBottom || "0px"; 882 style.paddingLeft = style.paddingLeft || "0px"; 883 style.borderTop = style.borderTop || "0px"; 884 style.borderRight = style.borderRight || "0px"; 885 style.borderBottom = style.borderBottom || "0px"; 886 style.borderLeft = style.borderLeft || "0px"; 887 style.marginTop = style.marginTop || "0px"; 888 style.marginRight = style.marginRight || "0px"; 889 style.marginBottom = style.marginBottom || "0px"; 890 style.marginLeft = style.marginLeft || "0px"; 891 } 892 }, 893 894 _fixContainer: function () { 895 // Add container to document body 896 document.body.insertBefore(cc.container, document.body.firstChild); 897 // Set body's width height to window's size, and forbid overflow, so that game will be centered 898 var bs = document.body.style; 899 bs.width = window.innerWidth + "px"; 900 bs.height = window.innerHeight + "px"; 901 bs.overflow = "hidden"; 902 // Body size solution doesn't work on all mobile browser so this is the aleternative: fixed container 903 var contStyle = cc.container.style; 904 contStyle.position = "fixed"; 905 contStyle.left = contStyle.top = "0px"; 906 // Reposition body 907 document.body.scrollTop = 0; 908 } 909 }); 910 911 /** 912 * <p>cc.ContentStrategy class is the root strategy class of content's scale strategy, 913 * it controls the behavior of how to scale the scene and setup the viewport for the game</p> 914 * 915 * @class 916 * @extends cc.Class 917 */ 918 cc.ContentStrategy = cc.Class.extend(/** @lends cc.ContentStrategy# */{ 919 920 _result: { 921 scale: [1, 1], 922 viewport: null 923 }, 924 925 _buildResult: function (containerW, containerH, contentW, contentH, scaleX, scaleY) { 926 // Makes content fit better the canvas 927 Math.abs(containerW - contentW) < 2 && (contentW = containerW); 928 Math.abs(containerH - contentH) < 2 && (contentH = containerH); 929 930 var viewport = cc.rect(Math.round((containerW - contentW) / 2), 931 Math.round((containerH - contentH) / 2), 932 contentW, contentH); 933 934 // Translate the content 935 if (cc._renderType === cc._RENDER_TYPE_CANVAS){ 936 //TODO: modify something for setTransform 937 //cc._renderContext.translate(viewport.x, viewport.y + contentH); 938 } 939 940 this._result.scale = [scaleX, scaleY]; 941 this._result.viewport = viewport; 942 return this._result; 943 }, 944 945 /** 946 * Manipulation before applying the strategy 947 * @param {cc.view} view The target view 948 */ 949 preApply: function (view) { 950 }, 951 952 /** 953 * Function to apply this strategy 954 * The return value is {scale: [scaleX, scaleY], viewport: {cc.Rect}}, 955 * The target view can then apply these value to itself, it's preferred not to modify directly its private variables 956 * @param {cc.view} view 957 * @param {cc.Size} designedResolution 958 * @return {object} scaleAndViewportRect 959 */ 960 apply: function (view, designedResolution) { 961 return {"scale": [1, 1]}; 962 }, 963 964 /** 965 * Manipulation after applying the strategy 966 * @param {cc.view} view The target view 967 */ 968 postApply: function (view) { 969 } 970 }); 971 972 (function () { 973 974 // Container scale strategys 975 /** 976 * @class 977 * @extends cc.ContainerStrategy 978 */ 979 var EqualToFrame = cc.ContainerStrategy.extend({ 980 apply: function (view) { 981 this._setupContainer(view, view._frameSize.width, view._frameSize.height); 982 } 983 }); 984 985 /** 986 * @class 987 * @extends cc.ContainerStrategy 988 */ 989 var ProportionalToFrame = cc.ContainerStrategy.extend({ 990 apply: function (view, designedResolution) { 991 var frameW = view._frameSize.width, frameH = view._frameSize.height, containerStyle = cc.container.style, 992 designW = designedResolution.width, designH = designedResolution.height, 993 scaleX = frameW / designW, scaleY = frameH / designH, 994 containerW, containerH; 995 996 scaleX < scaleY ? (containerW = frameW, containerH = designH * scaleX) : (containerW = designW * scaleY, containerH = frameH); 997 998 // Adjust container size with integer value 999 var offx = Math.round((frameW - containerW) / 2); 1000 var offy = Math.round((frameH - containerH) / 2); 1001 containerW = frameW - 2 * offx; 1002 containerH = frameH - 2 * offy; 1003 1004 this._setupContainer(view, containerW, containerH); 1005 // Setup container's margin 1006 containerStyle.marginLeft = offx + "px"; 1007 containerStyle.marginRight = offx + "px"; 1008 containerStyle.marginTop = offy + "px"; 1009 containerStyle.marginBottom = offy + "px"; 1010 } 1011 }); 1012 1013 /** 1014 * @class 1015 * @extends EqualToFrame 1016 */ 1017 var EqualToWindow = EqualToFrame.extend({ 1018 preApply: function (view) { 1019 this._super(view); 1020 view._frame = document.documentElement; 1021 }, 1022 1023 apply: function (view) { 1024 this._super(view); 1025 this._fixContainer(); 1026 } 1027 }); 1028 1029 /** 1030 * @class 1031 * @extends ProportionalToFrame 1032 */ 1033 var ProportionalToWindow = ProportionalToFrame.extend({ 1034 preApply: function (view) { 1035 this._super(view); 1036 view._frame = document.documentElement; 1037 }, 1038 1039 apply: function (view, designedResolution) { 1040 this._super(view, designedResolution); 1041 this._fixContainer(); 1042 } 1043 }); 1044 1045 /** 1046 * @class 1047 * @extends cc.ContainerStrategy 1048 */ 1049 var OriginalContainer = cc.ContainerStrategy.extend({ 1050 apply: function (view) { 1051 this._setupContainer(view, cc._canvas.width, cc._canvas.height); 1052 } 1053 }); 1054 1055 // #NOT STABLE on Android# Alias: Strategy that makes the container's size equals to the window's size 1056 // cc.ContainerStrategy.EQUAL_TO_WINDOW = new EqualToWindow(); 1057 // #NOT STABLE on Android# Alias: Strategy that scale proportionally the container's size to window's size 1058 // cc.ContainerStrategy.PROPORTION_TO_WINDOW = new ProportionalToWindow(); 1059 // Alias: Strategy that makes the container's size equals to the frame's size 1060 cc.ContainerStrategy.EQUAL_TO_FRAME = new EqualToFrame(); 1061 // Alias: Strategy that scale proportionally the container's size to frame's size 1062 cc.ContainerStrategy.PROPORTION_TO_FRAME = new ProportionalToFrame(); 1063 // Alias: Strategy that keeps the original container's size 1064 cc.ContainerStrategy.ORIGINAL_CONTAINER = new OriginalContainer(); 1065 1066 // Content scale strategys 1067 var ExactFit = cc.ContentStrategy.extend({ 1068 apply: function (view, designedResolution) { 1069 var containerW = cc._canvas.width, containerH = cc._canvas.height, 1070 scaleX = containerW / designedResolution.width, scaleY = containerH / designedResolution.height; 1071 1072 return this._buildResult(containerW, containerH, containerW, containerH, scaleX, scaleY); 1073 } 1074 }); 1075 1076 var ShowAll = cc.ContentStrategy.extend({ 1077 apply: function (view, designedResolution) { 1078 var containerW = cc._canvas.width, containerH = cc._canvas.height, 1079 designW = designedResolution.width, designH = designedResolution.height, 1080 scaleX = containerW / designW, scaleY = containerH / designH, scale = 0, 1081 contentW, contentH; 1082 1083 scaleX < scaleY ? (scale = scaleX, contentW = containerW, contentH = designH * scale) 1084 : (scale = scaleY, contentW = designW * scale, contentH = containerH); 1085 1086 return this._buildResult(containerW, containerH, contentW, contentH, scale, scale); 1087 } 1088 }); 1089 1090 var NoBorder = cc.ContentStrategy.extend({ 1091 apply: function (view, designedResolution) { 1092 var containerW = cc._canvas.width, containerH = cc._canvas.height, 1093 designW = designedResolution.width, designH = designedResolution.height, 1094 scaleX = containerW / designW, scaleY = containerH / designH, scale, 1095 contentW, contentH; 1096 1097 scaleX < scaleY ? (scale = scaleY, contentW = designW * scale, contentH = containerH) 1098 : (scale = scaleX, contentW = containerW, contentH = designH * scale); 1099 1100 return this._buildResult(containerW, containerH, contentW, contentH, scale, scale); 1101 } 1102 }); 1103 1104 var FixedHeight = cc.ContentStrategy.extend({ 1105 apply: function (view, designedResolution) { 1106 var containerW = cc._canvas.width, containerH = cc._canvas.height, 1107 designH = designedResolution.height, scale = containerH / designH, 1108 contentW = containerW, contentH = containerH; 1109 1110 return this._buildResult(containerW, containerH, contentW, contentH, scale, scale); 1111 }, 1112 1113 postApply: function (view) { 1114 cc.director._winSizeInPoints = view.getVisibleSize(); 1115 } 1116 }); 1117 1118 var FixedWidth = cc.ContentStrategy.extend({ 1119 apply: function (view, designedResolution) { 1120 var containerW = cc._canvas.width, containerH = cc._canvas.height, 1121 designW = designedResolution.width, scale = containerW / designW, 1122 contentW = containerW, contentH = containerH; 1123 1124 return this._buildResult(containerW, containerH, contentW, contentH, scale, scale); 1125 }, 1126 1127 postApply: function (view) { 1128 cc.director._winSizeInPoints = view.getVisibleSize(); 1129 } 1130 }); 1131 1132 // Alias: Strategy to scale the content's size to container's size, non proportional 1133 cc.ContentStrategy.EXACT_FIT = new ExactFit(); 1134 // Alias: Strategy to scale the content's size proportionally to maximum size and keeps the whole content area to be visible 1135 cc.ContentStrategy.SHOW_ALL = new ShowAll(); 1136 // Alias: Strategy to scale the content's size proportionally to fill the whole container area 1137 cc.ContentStrategy.NO_BORDER = new NoBorder(); 1138 // Alias: Strategy to scale the content's height to container's height and proportionally scale its width 1139 cc.ContentStrategy.FIXED_HEIGHT = new FixedHeight(); 1140 // Alias: Strategy to scale the content's width to container's width and proportionally scale its height 1141 cc.ContentStrategy.FIXED_WIDTH = new FixedWidth(); 1142 1143 })(); 1144 1145 /** 1146 * <p>cc.ResolutionPolicy class is the root strategy class of scale strategy, 1147 * its main task is to maintain the compatibility with Cocos2d-x</p> 1148 * 1149 * @class 1150 * @extends cc.Class 1151 * @param {cc.ContainerStrategy} containerStg The container strategy 1152 * @param {cc.ContentStrategy} contentStg The content strategy 1153 */ 1154 cc.ResolutionPolicy = cc.Class.extend(/** @lends cc.ResolutionPolicy# */{ 1155 _containerStrategy: null, 1156 _contentStrategy: null, 1157 1158 /** 1159 * Constructor of cc.ResolutionPolicy 1160 * @param {cc.ContainerStrategy} containerStg 1161 * @param {cc.ContentStrategy} contentStg 1162 */ 1163 ctor: function (containerStg, contentStg) { 1164 this.setContainerStrategy(containerStg); 1165 this.setContentStrategy(contentStg); 1166 }, 1167 1168 /** 1169 * Manipulation before applying the resolution policy 1170 * @param {cc.view} view The target view 1171 */ 1172 preApply: function (view) { 1173 this._containerStrategy.preApply(view); 1174 this._contentStrategy.preApply(view); 1175 }, 1176 1177 /** 1178 * Function to apply this resolution policy 1179 * The return value is {scale: [scaleX, scaleY], viewport: {cc.Rect}}, 1180 * The target view can then apply these value to itself, it's preferred not to modify directly its private variables 1181 * @param {cc.view} view The target view 1182 * @param {cc.Size} designedResolution The user defined design resolution 1183 * @return {object} An object contains the scale X/Y values and the viewport rect 1184 */ 1185 apply: function (view, designedResolution) { 1186 this._containerStrategy.apply(view, designedResolution); 1187 return this._contentStrategy.apply(view, designedResolution); 1188 }, 1189 1190 /** 1191 * Manipulation after appyling the strategy 1192 * @param {cc.view} view The target view 1193 */ 1194 postApply: function (view) { 1195 this._containerStrategy.postApply(view); 1196 this._contentStrategy.postApply(view); 1197 }, 1198 1199 /** 1200 * Setup the container's scale strategy 1201 * @param {cc.ContainerStrategy} containerStg 1202 */ 1203 setContainerStrategy: function (containerStg) { 1204 if (containerStg instanceof cc.ContainerStrategy) 1205 this._containerStrategy = containerStg; 1206 }, 1207 1208 /** 1209 * Setup the content's scale strategy 1210 * @param {cc.ContentStrategy} contentStg 1211 */ 1212 setContentStrategy: function (contentStg) { 1213 if (contentStg instanceof cc.ContentStrategy) 1214 this._contentStrategy = contentStg; 1215 } 1216 }); 1217 1218 /** 1219 * @memberOf cc.ResolutionPolicy# 1220 * @name EXACT_FIT 1221 * @constant 1222 * @type Number 1223 * @static 1224 * The entire application is visible in the specified area without trying to preserve the original aspect ratio.<br/> 1225 * Distortion can occur, and the application may appear stretched or compressed. 1226 */ 1227 cc.ResolutionPolicy.EXACT_FIT = 0; 1228 1229 /** 1230 * @memberOf cc.ResolutionPolicy# 1231 * @name NO_BORDER 1232 * @constant 1233 * @type Number 1234 * @static 1235 * The entire application fills the specified area, without distortion but possibly with some cropping,<br/> 1236 * while maintaining the original aspect ratio of the application. 1237 */ 1238 cc.ResolutionPolicy.NO_BORDER = 1; 1239 1240 /** 1241 * @memberOf cc.ResolutionPolicy# 1242 * @name SHOW_ALL 1243 * @constant 1244 * @type Number 1245 * @static 1246 * The entire application is visible in the specified area without distortion while maintaining the original<br/> 1247 * aspect ratio of the application. Borders can appear on two sides of the application. 1248 */ 1249 cc.ResolutionPolicy.SHOW_ALL = 2; 1250 1251 /** 1252 * @memberOf cc.ResolutionPolicy# 1253 * @name FIXED_HEIGHT 1254 * @constant 1255 * @type Number 1256 * @static 1257 * The application takes the height of the design resolution size and modifies the width of the internal<br/> 1258 * canvas so that it fits the aspect ratio of the device<br/> 1259 * no distortion will occur however you must make sure your application works on different<br/> 1260 * aspect ratios 1261 */ 1262 cc.ResolutionPolicy.FIXED_HEIGHT = 3; 1263 1264 /** 1265 * @memberOf cc.ResolutionPolicy# 1266 * @name FIXED_WIDTH 1267 * @constant 1268 * @type Number 1269 * @static 1270 * The application takes the width of the design resolution size and modifies the height of the internal<br/> 1271 * canvas so that it fits the aspect ratio of the device<br/> 1272 * no distortion will occur however you must make sure your application works on different<br/> 1273 * aspect ratios 1274 */ 1275 cc.ResolutionPolicy.FIXED_WIDTH = 4; 1276 1277 /** 1278 * @memberOf cc.ResolutionPolicy# 1279 * @name UNKNOWN 1280 * @constant 1281 * @type Number 1282 * @static 1283 * Unknow policy 1284 */ 1285 cc.ResolutionPolicy.UNKNOWN = 5;