/**
* The MIT License (MIT)
*
* Copyright (c) 2004 Spencer Evans, NAUT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @version 1.0
* @copyright Copyright © 2015 Spencer Evans
* @author Spencer Evans evans.spencer@gmail.com
* @license http://opensource.org/licenses/MIT
* @website http://spencer-evans.com/
*/
var swevans;
(function (swevans) {
/**
* A helper class for to dynamically resize canvas elements to fill parent bounds and
* resize the canvas pixels to match device pixels.
*
* Resizing Behavior:
* - The CanvasResizer class updates the width and height properties of a canvas to
* match dimensions of the parent container.
* - If autoSize is set to true (it is by default), then the CanvasResizer will automatically
* resize the canvas to fit the bounds of it's parent container. A resize check is
* triggered any time the window resizes.
*
* High Definition Behavior:
* - The CanvasResizer will attempt to render canvas graphics in 'high definition' if the
* highDefinition flag is set to true (it is by default).
* - High Definition rendering (as it is defined here :) means that the canvas is resized
* such that each canvas pixel will match the size of a single device pixel.
* - As an example.. if the canvas is running on an iPad 3, the logical webpage resolution
* will be 1024x768, but the actual device resolution is 2048x1536. This results in
* each canvas pixel taking up 4 (2x2) device pixels. This will lead to blurry strokes.
* CanvasResizer high def rendering reconciles this by enlarging the canvas to twice the
* desired size then scales the graphics up to match. This creates clean, sharp strokes.
*
* RECOMMENDATIONS:
* - Specify a single viewport meta tag at the top of your html files, it should include
* the property initial-scale=1. EX:
*
* - Canvas sizes (width and height) must be whole numbers. When the canvas's parent element
* is a subpixel size (say... 800.5 by 600.5) the canvas will be 1/2 pixel too small / large.
* You should set the canvas style width and height to 100% to make it a perfect fit. EX:
*
*
* BROWSWER SUPPORT:
* - The CanvasResizer class should work in all modern browsers (IE9+).
*
* FUTURE CHANGES:
* - It would be nice to have the canvas be able to resize based on css styling without
* simply filling the parent element.
*/
var CanvasResizer = (function () {
//}
//{ Constructor
/**
* Creates a new canvas resizer. A resize is immediately triggered autoResize is true and a resize is needed.
* @param canvas HTMLCanvasElement; The canvas that should be resized.
* @param highDefinition boolean (optional, default=true); Indicates if the canvas should be rendered at high definition.
* @param autoResize boolean (optional, default=true); Indicates if the canvas should resize when parent element dimensions change.
*/
function CanvasResizer(canvas, highDefinition, autoResize) {
if (highDefinition === void 0) { highDefinition = true; }
if (autoResize === void 0) { autoResize = true; }
/** @private The current width of the canvas. */
this._width = undefined;
/** @private The current height of the canvas. */
this._height = undefined;
/** @private Indicates if the canvas resizer is disposed. */
this._isDisposed = false;
/** @private The current scale being applied to the canvas to achieve high definition rendering. */
this._contentScaleFactor = 1;
/** @private Indicates if the canvas should be rendered at high definition. */
this._highDefinition = true;
/** @private Indicates if the canvas should automatically resize when the parent element dimension changes. */
this._autoResize = true;
// store params
this._canvas = canvas;
this._highDefinition = highDefinition;
this._autoResize = autoResize;
// bind event handlers
this.window_resize = this.window_resize.bind(this);
// listen for events
window.addEventListener("resize", this.window_resize, false);
// do an initial resize
if (this._autoResize)
this.resize();
}
Object.defineProperty(CanvasResizer.prototype, "contentScaleFactor", {
/** number: Gets the current scale being applied to the canvas to achieve high definition rendering. */
get: function () { return this._contentScaleFactor; },
enumerable: true,
configurable: true
});
Object.defineProperty(CanvasResizer.prototype, "highDefinition", {
/** boolean: Gets whether the canvas should be rendered at high definition. True by default. */
get: function () { return this._highDefinition; },
/** boolean: Sets whether the canvas should be rendered at high definition. True by default. */
set: function (value) { if (this._highDefinition === value) {
return;
} this._highDefinition = value; this.resize(); },
enumerable: true,
configurable: true
});
Object.defineProperty(CanvasResizer.prototype, "autoResize", {
/** boolean: Gets whether the canvas should automatically resize when the parent element dimension changes. True by default. */
get: function () { return this._autoResize; },
/** boolean: Sets whether the canvas should automatically resize when the parent element dimension changes. True by default. */
set: function (value) { if (this._autoResize === value) {
return;
} this._autoResize = value; this.resize(); },
enumerable: true,
configurable: true
});
/**
* Disposes of the canvas resizer by removing event listeners. No parameters, no return value.
*/
CanvasResizer.prototype.dispose = function () {
if (this._isDisposed)
return;
this._isDisposed = true;
// stop listening for events
window.removeEventListener("resize", this.window_resize, false);
};
//}
//{ Resizing
/**
* Triggers a resize check. No parameters. No return.
*/
CanvasResizer.prototype.resize = function () {
// Grab the canvas's holder
var parent = this._canvas.parentNode;
// If the parent is null, the element is not in the DOM and we cannot respond
if (parent === null || typeof (parent) === "undefined")
return;
// Hide the canvas so we can get the parent's responsive bounds
var displayBackup = this._canvas.style.display;
this._canvas.style.display = "none";
// Measure parent without the canvas
var w = parent.clientWidth;
var h = parent.clientHeight;
// Scale the canvas to match device pixels if high definition is requested and a viewport is defined
var scale = 1;
if (this._highDefinition) {
var currentScale = CanvasResizer.readViewportScale();
if (currentScale != null) {
var pixelRatio = (window.devicePixelRatio ? window.devicePixelRatio : 1);
if (pixelRatio != 1)
scale = pixelRatio * currentScale;
}
}
this._contentScaleFactor = scale;
w *= scale;
h *= scale;
// Check to see if a resize is even necessary
var resizeNeeded = true;
if (this._width === w && this._height === h)
resizeNeeded = false;
// Set the new canvas size
if (resizeNeeded) {
this._canvas.width = w; // * scale;
this._canvas.height = h; // * scale;
this._width = w;
this._height = h;
// Reset and scale the rendering transform
var ctx = this._canvas.getContext('2d');
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.scale(this._contentScaleFactor, this._contentScaleFactor);
}
// Restore the canvas display property
this._canvas.style.display = displayBackup;
// dispatch the resize event if we actually resized
if (resizeNeeded)
this._canvas.dispatchEvent(new Event("resize"));
};
//}
//{ Event Handlers
/**
* Handles window resize events.
*/
CanvasResizer.prototype.window_resize = function (evt) {
// make the canvas respond in size
if (this._autoResize)
this.resize();
};
//}
//{ Utilities
/**
* Reads the initial scale property of the window.
* @return The initial scale value of the viewport tag, null if not defined.
* @private Do not call
*/
CanvasResizer.readViewportScale = function () {
// Get the viewport tag
var viewports = document.querySelectorAll("meta[name=viewport]");
var viewport = null;
if (viewports.length > 0)
viewport = viewports[viewports.length - 1];
// If there is no viewport tag, return null
if (viewport === null)
return null;
// Get the content property of the viewport tag
var contentValue = viewport.content;
// If there is no content property, return null
if (contentValue === "" || contentValue === null)
return null;
// Split the content property into a set of values
var contentValues = contentValue.split(",");
// Look for the initial scale property
for (var i = 0; i < contentValues.length; ++i) {
var val = contentValues[i];
// Remove white space
while (val.indexOf(" ") >= 0)
val = val.replace(" ", "");
// Split prop and value
var parts = val.split("=");
var prop = parts[0];
var value = parts[1];
// return the initial scale if it is found, round it to 4 decimal places to avoid float rounding issues
if (prop === "initial-scale")
return Math.round(parseFloat(value) * 1000) / 1000;
}
// No initial scale value was found, return null
return null;
};
return CanvasResizer;
})();
swevans.CanvasResizer = CanvasResizer;
})(swevans || (swevans = {}));