From 2777c2a46347ac2c632549a18e19fae0a4169c4d Mon Sep 17 00:00:00 2001 From: photonstorm Date: Sat, 8 Nov 2014 13:24:38 +0000 Subject: [PATCH] ScaleManager.elementBounds is a cross-browser element.getBoundingClientRect method with optional cushion. ScaleManager.calibrate is a private method that calibrates element coordinates for viewport checks. ScaleManager.aspect gets the viewport aspect ratio (or the aspect ratio of an object or element) ScaleManager.inViewport tests if the given DOM element is within the viewport, with an optional cushion parameter that allows you to specify a distance. ScaleManager.scaleSprite takes a Sprite or Image object and scales it to fit the given dimensions. Scaling happens proportionally without distortion to the sprites texture. The letterBox parameter controls if scaling will produce a letter-box effect or zoom the sprite until it fills the given values. ScaleManager.viewportWidth returns the viewport width in pixels. ScaleManager.viewportHeight returns the viewport height in pixels. ScaleManager.documentWidth returns the document width in pixels. ScaleManager.documentHeight returns the document height in pixels. --- README.md | 9 ++ src/core/ScaleManager.js | 281 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 290 insertions(+) diff --git a/README.md b/README.md index 7144d8e7f..362bfcf22 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,15 @@ Version 2.1.4 - "Bethal" - in development * Pointer.addClickTrampoline now adds in support for click trampolines. These raise pointer events into click events, which are required internally for a few edge cases like IE11 full screen mode support, but are also useful if you know you specifically need a DOM click event from a pointer (thanks @pnstickne #1282) * Point.floor will Math.floor both the `x` and `y` values of the Point. * Point.ceil will Math.ceil both the `x` and `y` values of the Point. +* ScaleManager.elementBounds is a cross-browser element.getBoundingClientRect method with optional cushion. +* ScaleManager.calibrate is a private method that calibrates element coordinates for viewport checks. +* ScaleManager.aspect gets the viewport aspect ratio (or the aspect ratio of an object or element) +* ScaleManager.inViewport tests if the given DOM element is within the viewport, with an optional cushion parameter that allows you to specify a distance. +* ScaleManager.scaleSprite takes a Sprite or Image object and scales it to fit the given dimensions. Scaling happens proportionally without distortion to the sprites texture. The letterBox parameter controls if scaling will produce a letter-box effect or zoom the sprite until it fills the given values. +* ScaleManager.viewportWidth returns the viewport width in pixels. +* ScaleManager.viewportHeight returns the viewport height in pixels. +* ScaleManager.documentWidth returns the document width in pixels. +* ScaleManager.documentHeight returns the document height in pixels. ### Updates diff --git a/src/core/ScaleManager.js b/src/core/ScaleManager.js index 501f0ad30..694f7dac6 100644 --- a/src/core/ScaleManager.js +++ b/src/core/ScaleManager.js @@ -9,6 +9,8 @@ * * The `width` and `height` constructor parameters can either be a number which represents pixels or a string that represents a percentage: e.g. `800` (for 800 pixels) or `"80%"` for 80%. * +* Some parts of ScaleManager were inspired by the research of Ryan Van Etten, released under MIT License 2013. +* * @class Phaser.ScaleManager * @constructor * @param {Phaser.Game} game - A reference to the currently running game. @@ -1443,10 +1445,183 @@ Phaser.ScaleManager.prototype = { this.event = event; console.warn("Phaser.ScaleManager: requestFullscreen failed or device does not support the Fullscreen API"); + this.fullScreenFailed.dispatch(); }, + /** + * A cross-browser element.getBoundingClientRect method with optional cushion. + * + * Returns a plain object containing the properties `top/bottom/left/right/width/height` with respect to the top-left corner of the current viewport. + * Its properties match the native rectangle. + * The cushion parameter is an amount of pixels (+/-) to cushion the element. + * It adjusts the measurements such that it is possible to detect when an element is near the viewport. + * + * @method Phaser.ScaleManager#elementBounds + * @public + * @param {Element|Object} element - The element or stack (uses first item) to get the bounds for. If none given it defaults to the Phaser game canvas. + * @param {number} [cushion] - A +/- pixel adjustment amount. + * @return {Object|boolean} A plain object containing the properties `top/bottom/left/right/width/height` or `false` if a non-valid element is given. + */ + elementBounds: function (element, cushion) { + + if (typeof element === 'undefined') { element = this.game.canvas; } + if (typeof cushion === 'undefined') { cushion = 0; } + + element = element && !element.nodeType ? element[0] : element; + + if (!element || element.nodeType !== 1) + { + return false; + } + else + { + return this.calibrate(element.getBoundingClientRect(), cushion); + } + + }, + + /** + * Calibrates element coordinates for `inViewport` checks. + * + * @method Phaser.ScaleManager#calibrate + * @private + * @param {Object} coords -An object containing the following properties: `{top: number, right: number, bottom: number, left: number}` + * @param {number} [cushion] - A value to adjust the coordinates by. + * @return {Object} The calibrated element coordinates + */ + calibrate: function (coords, cushion) { + + cushion = +cushion || 0; + + var output = { width: 0, height: 0, left: 0, right: 0, top: 0, bottom: 0 }; + + output.width = (output.right = coords.right + cushion) - (output.left = coords.left - cushion); + output.height = (output.bottom = coords.bottom + cushion) - (output.top = coords.top - cushion); + + return output; + + }, + + /** + * Get the viewport aspect ratio (or the aspect ratio of an object or element) + * @link http://w3.org/TR/css3-mediaqueries/#orientation + * + * @method Phaser.ScaleManager#aspect + * @public + * @param {(Element|Object)=} [object] - Optional object. Must have public `width` and `height` properties or methods. + * @return {number} The aspect ratio. + */ + aspect: function (object) { + + object = null == object ? this.viewport() : 1 === object.nodeType ? this.elementBounds(object) : object; + + var w = object['width']; + var h = object['height']; + + if (typeof w === 'function') + { + w = w.call(object); + } + + if (typeof h === 'function') + { + h = h.call(object); + } + + return w / h; + + }, + + /** + * Tests if the given DOM element is within the viewport. + * + * The optional cushion parameter allows you to specify a distance. + * + * inViewport(element, 100) is `true` if the element is in the viewport or 100px near it. + * inViewport(element, -100) is `true` if the element is in the viewport or at least 100px near it. + * + * @method Phaser.ScaleManager#inViewport + * @public + * @param {Element|Object} [element] - The DOM element to check. If no element is given it defaults to the Phaser game canvas. + * @param {number} [cushion] - The cushion allows you to specify a distance within which the element must be within the viewport. + * @return {boolean} True if the element is within the viewport, or within `cushion` distance from it. + */ + inViewport: function (element, cushion) { + + if (typeof element === 'undefined') { element = this.game.canvas; } + + var r = this.elementBounds(element, cushion); + + return !!r && r.bottom >= 0 && r.right >= 0 && r.top <= this.viewportH() && r.left <= this.viewportW(); + + }, + + /** + * Takes a Sprite or Image object and scales it to fit the given dimensions. + * Scaling happens proportionally without distortion to the sprites texture. + * The letterBox parameter controls if scaling will produce a letter-box effect or zoom the + * sprite until it fills the given values. Note that with letterBox set to false the scaled sprite may spill out over either + * the horizontal or vertical sides of the target dimensions. If you wish to stop this you can crop the Sprite. + * + * @method Phaser.ScaleManager#scaleSprite + * @public + * @param {Phaser.Sprite|Phaser.Image} sprite - The sprite we want to scale. + * @param {integer} [width] - The target width that we want to fit the sprite in to. If not given it defaults to ScaleManager.width. + * @param {integer} [height] - The target height that we want to fit the sprite in to. If not given it defaults to ScaleManager.height. + * @param {boolean} [letterBox=false] - True if we want the `fitted` mode. Otherwise, the function uses the `zoom` mode. + * @return {Phaser.Sprite|Phaser.Image} The scaled sprite. + */ + scaleSprite: function (sprite, width, height, letterBox) { + + if (typeof width === 'undefined') { width = this.width; } + if (typeof height === 'undefined') { height = this.height; } + if (typeof letterbox === 'undefined') { letterbox = false; } + + sprite.scale.set(1); + + if ((sprite.width <= 0) || (sprite.height <= 0) || (width <= 0) || (height <= 0)) + { + return sprite; + } + + var scaleX1 = width; + var scaleY1 = (sprite.height * width) / sprite.width; + + var scaleX2 = (sprite.width * height) / sprite.height; + var scaleY2 = height; + + var scaleOnWidth = (scaleX2 > width); + + if (scaleOnWidth) + { + scaleOnWidth = letterBox; + } + else + { + scaleOnWidth = !letterBox; + } + + if (scaleOnWidth) + { + sprite.width = Math.floor(scaleX1); + sprite.height = Math.floor(scaleY1); + } + else + { + sprite.width = Math.floor(scaleX2); + sprite.height = Math.floor(scaleY2); + } + + // Enable at some point? + // sprite.x = Math.floor((width - sprite.width) / 2); + // sprite.y = Math.floor((height - sprite.height) / 2); + + return sprite; + + }, + /** * Destroys the ScaleManager and removes any event listeners. * This should probably only be called when the game is destroyed. @@ -1729,3 +1904,109 @@ Object.defineProperty(Phaser.ScaleManager.prototype, "isLandscape", { } }); + +/** +* A cross-browser window.scrollX. +* +* @name Phaser.ScaleManager#scrollX +* @property {number} scrollX +* @readOnly +*/ +Object.defineProperty(Phaser.ScaleManager.prototype, "scrollX", { + + get: function () { + return window.pageXOffset || document.documentElement.scrollLeft; + } + +}); + +/** +* A cross-browser window.scrollY. +* +* @name Phaser.ScaleManager#scrollY +* @property {number} scrollY +* @readOnly +*/ +Object.defineProperty(Phaser.ScaleManager.prototype, "scrollY", { + + get: function () { + return window.pageYOffset || document.documentElement.scrollTop; + } + +}); + +/** +* Gets the viewport width in pixels. +* +* @name Phaser.ScaleManager#viewportWidth +* @property {number} viewportWidth +* @readOnly +*/ +Object.defineProperty(Phaser.ScaleManager.prototype, "viewportWidth", { + + get: function () { + + var a = document.documentElement.clientWidth; + var b = window.innerWidth; + + return a < b ? b : a; + + } + +}); + +/** +* Gets the viewport height in pixels. +* +* @name Phaser.ScaleManager#viewportHeight +* @property {number} viewportHeight +* @readOnly +*/ +Object.defineProperty(Phaser.ScaleManager.prototype, "viewportHeight", { + + get: function () { + + var a = document.documentElement.clientHeight; + var b = window.innerHeight; + + return a < b ? b : a; + + } + +}); + +/** +* Gets the document width in pixels. +* +* @name Phaser.ScaleManager#documentWidth +* @property {number} documentWidth +* @readOnly +*/ +Object.defineProperty(Phaser.ScaleManager.prototype, "documentWidth", { + + get: function () { + + var d = document.documentElement; + return Math.max(d.clientWidth, d.offsetWidth, d.scrollWidth); + + } + +}); + +/** +* Gets the document height in pixels. +* +* @name Phaser.ScaleManager#documentHeight +* @property {number} documentHeight +* @readOnly +*/ +Object.defineProperty(Phaser.ScaleManager.prototype, "documentHeight", { + + get: function () { + + var d = document.documentElement; + return Math.max(d.clientHeight, d.offsetHeight, d.scrollHeight); + + } + +});