UNPKG

node-easel

Version:

node wrapper for EaselJS, utilizing node-canvas.

1,243 lines (1,154 loc) 53.2 kB
/* * Graphics * Visit http://createjs.com/ for documentation, updates and examples. * * Copyright (c) 2010 gskinner.com, inc. * * 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. */ /** * @module EaselJS */ // namespace: this.createjs = this.createjs||{}; (function() { "use strict"; /** * Inner class used by the {{#crossLink "Graphics"}}{{/crossLink}} class. Used to create the instruction lists used in Graphics: * @class Command * @protected * @constructor **/ function Command(f, params, path) { this.f = f; this.params = params; this.path = path==null ? true : path; } /** * @method exec * @protected * @param {Object} scope **/ Command.prototype.exec = function(scope) { this.f.apply(scope, this.params); }; /** * The Graphics class exposes an easy to use API for generating vector drawing instructions and drawing them to a * specified context. Note that you can use Graphics without any dependency on the Easel framework by calling {{#crossLink "DisplayObject/draw"}}{{/crossLink}} * directly, or it can be used with the {{#crossLink "Shape"}}{{/crossLink}} object to draw vector graphics within the * context of an Easel display list. * * <h4>Example</h4> * var g = new createjs.Graphics(); * g.setStrokeStyle(1); * g.beginStroke(createjs.Graphics.getRGB(0,0,0)); * g.beginFill(createjs.Graphics.getRGB(255,0,0)); * g.drawCircle(0,0,3); * * var s = new createjs.Shape(g); * s.x = 100; * s.y = 100; * * stage.addChild(s); * stage.update(); * * Note that all drawing methods in Graphics return the Graphics instance, so they can be chained together. For example, * the following line of code would generate the instructions to draw a rectangle with a red stroke and blue fill, then * render it to the specified context2D: * * myGraphics.beginStroke("#F00").beginFill("#00F").drawRect(20, 20, 100, 50).draw(myContext2D); * * <h4>Tiny API</h4> * The Graphics class also includes a "tiny API", which is one or two-letter methods that are shortcuts for all of the * Graphics methods. These methods are great for creating compact instructions, and is used by the Toolkit for CreateJS * to generate readable code. All tiny methods are marked as protected, so you can view them by enabling protected * descriptions in the docs. * * <table> * <tr><td><b>Tiny</b></td><td><b>Method</b></td><td><b>Tiny</b></td><td><b>Method</b></td></tr> * <tr><td>mt</td><td>{{#crossLink "Graphics/moveTo"}}{{/crossLink}} </td> * <td>lt</td> <td>{{#crossLink "Graphics/lineTo"}}{{/crossLink}}</td></tr> * <tr><td>a/at</td><td>{{#crossLink "Graphics/arc"}}{{/crossLink}} / {{#crossLink "Graphics/arcTo"}}{{/crossLink}} </td> * <td>bt</td><td>{{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}} </td></tr> * <tr><td>qt</td><td>{{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} (also curveTo)</td> * <td>r</td><td>{{#crossLink "Graphics/rect"}}{{/crossLink}} </td></tr> * <tr><td>cp</td><td>{{#crossLink "Graphics/closePath"}}{{/crossLink}} </td> * <td>c</td><td>{{#crossLink "Graphics/clear"}}{{/crossLink}} </td></tr> * <tr><td>f</td><td>{{#crossLink "Graphics/beginFill"}}{{/crossLink}} </td> * <td>lf</td><td>{{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}} </td></tr> * <tr><td>rf</td><td>{{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} </td> * <td>bf</td><td>{{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}} </td></tr> * <tr><td>ef</td><td>{{#crossLink "Graphics/endFill"}}{{/crossLink}} </td> * <td>ss</td><td>{{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} </td></tr> * <tr><td>s</td><td>{{#crossLink "Graphics/beginStroke"}}{{/crossLink}} </td> * <td>ls</td><td>{{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} </td></tr> * <tr><td>rs</td><td>{{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} </td> * <td>bs</td><td>{{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} </td></tr> * <tr><td>es</td><td>{{#crossLink "Graphics/endStroke"}}{{/crossLink}} </td> * <td>dr</td><td>{{#crossLink "Graphics/drawRect"}}{{/crossLink}} </td></tr> * <tr><td>rr</td><td>{{#crossLink "Graphics/drawRoundRect"}}{{/crossLink}} </td> * <td>rc</td><td>{{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} </td></tr> * <tr><td>dc</td><td>{{#crossLink "Graphics/drawCircle"}}{{/crossLink}} </td> * <td>de</td><td>{{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} </td></tr> * <tr><td>dp</td><td>{{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} </td> * <td>p</td><td>{{#crossLink "Graphics/decodePath"}}{{/crossLink}} </td></tr> * </table> * * Here is the above example, using the tiny API instead. * * myGraphics.s("#F00").f("#00F").r(20, 20, 100, 50).draw(myContext2D); * * @class Graphics * @constructor * @for Graphics **/ var Graphics = function() { this.initialize(); }; var p = Graphics.prototype; // static public methods: /** * Returns a CSS compatible color string based on the specified RGB numeric color values in the format * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". For example, * * createjs.Graphics.getRGB(50, 100, 150, 0.5); * // Returns "rgba(50,100,150,0.5)" * * It also supports passing a single hex color value as the first param, and an optional alpha value as the second * param. For example, * * createjs.Graphics.getRGB(0xFF00FF, 0.2); * // Returns "rgba(255,0,255,0.2)" * * @method getRGB * @static * @param {Number} r The red component for the color, between 0 and 0xFF (255). * @param {Number} g The green component for the color, between 0 and 0xFF (255). * @param {Number} b The blue component for the color, between 0 and 0xFF (255). * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque. * @return {String} A CSS compatible color string based on the specified RGB numeric color values in the format * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". **/ Graphics.getRGB = function(r, g, b, alpha) { if (r != null && b == null) { alpha = g; b = r&0xFF; g = r>>8&0xFF; r = r>>16&0xFF; } if (alpha == null) { return "rgb("+r+","+g+","+b+")"; } else { return "rgba("+r+","+g+","+b+","+alpha+")"; } }; /** * Returns a CSS compatible color string based on the specified HSL numeric color values in the format "hsla(360,100,100,1.0)", * or if alpha is null then in the format "hsl(360,100,100)". * * createjs.Graphics.getHSL(150, 100, 70); * // Returns "hsl(150,100,70)" * * @method getHSL * @static * @param {Number} hue The hue component for the color, between 0 and 360. * @param {Number} saturation The saturation component for the color, between 0 and 100. * @param {Number} lightness The lightness component for the color, between 0 and 100. * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque. * @return {String} A CSS compatible color string based on the specified HSL numeric color values in the format * "hsla(360,100,100,1.0)", or if alpha is null then in the format "hsl(360,100,100)". **/ Graphics.getHSL = function(hue, saturation, lightness, alpha) { if (alpha == null) { return "hsl("+(hue%360)+","+saturation+"%,"+lightness+"%)"; } else { return "hsla("+(hue%360)+","+saturation+"%,"+lightness+"%,"+alpha+")"; } }; // static properties: /** * Exposes the Command class used internally by Graphics. Useful for extending the Graphics class or injecting * functionality. * @property Command * @static * @type {Function} **/ Graphics.Command = Command; /** * Map of Base64 characters to values. Used by {{#crossLink "Graphics/decodePath"}}{{/crossLink}}. * @property BASE_64 * @static * @final * @readonly * @type {Object} **/ Graphics.BASE_64 = {"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"+":62,"/":63}; /** * Maps numeric values for the caps parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to * "butt", 1 to "round", and 2 to "square". * For example, to set the line caps to "square": * * myGraphics.ss(16, 2); * * @property STROKE_CAPS_MAP * @static * @final * @readonly * @type {Array} **/ Graphics.STROKE_CAPS_MAP = ["butt", "round", "square"]; /** * Maps numeric values for the joints parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to * "miter", 1 to "round", and 2 to "bevel". * For example, to set the line joints to "bevel": * * myGraphics.ss(16, 0, 2); * * @property STROKE_JOINTS_MAP * @static * @final * @readonly * @type {Array} **/ Graphics.STROKE_JOINTS_MAP = ["miter", "round", "bevel"]; /** * @property _ctx * @static * @protected * @type {CanvasRenderingContext2D} **/ /** * @property beginCmd * @static * @protected * @type {Command} **/ /** * @property fillCmd * @static * @protected * @type {Command} **/ /** * @property strokeCmd * @static * @protected * @type {Command} **/ var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")); if (canvas.getContext) { var ctx = Graphics._ctx = canvas.getContext("2d"); Graphics.beginCmd = new Command(ctx.beginPath, [], false); Graphics.fillCmd = new Command(ctx.fill, [], false); Graphics.strokeCmd = new Command(ctx.stroke, [], false); canvas.width = canvas.height = 1; } // public properties // private properties /** * @property _strokeInstructions * @protected * @type {Array} **/ p._strokeInstructions = null; /** * @property _strokeStyleInstructions * @protected * @type {Array} **/ p._strokeStyleInstructions = null; /** * @property _strokeIgnoreScale * @protected * @type Boolean **/ p._strokeIgnoreScale = false; /** * @property _fillInstructions * @protected * @type {Array} **/ p._fillInstructions = null; /** * @property _strokeMatrix * @protected * @type {Array} **/ p._fillMatrix = null; /** * @property _instructions * @protected * @type {Array} **/ p._instructions = null; /** * @property _oldInstructions * @protected * @type {Array} **/ p._oldInstructions = null; /** * @property _activeInstructions * @protected * @type {Array} **/ p._activeInstructions = null; /** * @property _active * @protected * @type {Boolean} * @default false **/ p._active = false; /** * @property _dirty * @protected * @type {Boolean} * @default false **/ p._dirty = false; /** * Initialization method. * @method initialize * @protected **/ p.initialize = function() { this.clear(); this._ctx = Graphics._ctx; }; /** * Returns true if this Graphics instance has no drawing commands. * @method isEmpty * @return {Boolean} Returns true if this Graphics instance has no drawing commands. **/ p.isEmpty = function() { return !(this._instructions.length || this._oldInstructions.length || this._activeInstructions.length); }; /** * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform. * Returns true if the draw was handled (useful for overriding functionality). * * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. * @method draw * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into. **/ p.draw = function(ctx) { if (this._dirty) { this._updateInstructions(); } var instr = this._instructions; for (var i=0, l=instr.length; i<l; i++) { instr[i].exec(ctx); } }; /** * Draws only the path described for this Graphics instance, skipping any non-path instructions, including fill and * stroke descriptions. Used by <code>DisplayObject.clippingPath</code> to draw the clipping path, for example. * @method drawAsPath * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into. **/ p.drawAsPath = function(ctx) { if (this._dirty) { this._updateInstructions(); } var instr, instrs = this._instructions; for (var i=0, l=instrs.length; i<l; i++) { // the first command is always a beginPath command. if ((instr = instrs[i]).path || i==0) { instr.exec(ctx); } } }; // public methods that map directly to context 2D calls: /** * Moves the drawing point to the specified position. A tiny API method "mt" also exists. * @method moveTo * @param {Number} x The x coordinate the drawing point should move to. * @param {Number} y The y coordinate the drawing point should move to. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls). **/ p.moveTo = function(x, y) { this._activeInstructions.push(new Command(this._ctx.moveTo, [x, y])); return this; }; /** * Draws a line from the current drawing point to the specified position, which become the new current drawing * point. A tiny API method "lt" also exists. * * For detailed information, read the * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#complex-shapes-(paths)"> * whatwg spec</a>. * @method lineTo * @param {Number} x The x coordinate the drawing point should draw to. * @param {Number} y The y coordinate the drawing point should draw to. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.lineTo = function(x, y) { this._dirty = this._active = true; this._activeInstructions.push(new Command(this._ctx.lineTo, [x, y])); return this; }; /** * Draws an arc with the specified control points and radius. For detailed information, read the * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arcto"> * whatwg spec</a>. A tiny API method "at" also exists. * @method arcTo * @param {Number} x1 * @param {Number} y1 * @param {Number} x2 * @param {Number} y2 * @param {Number} radius * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.arcTo = function(x1, y1, x2, y2, radius) { this._dirty = this._active = true; this._activeInstructions.push(new Command(this._ctx.arcTo, [x1, y1, x2, y2, radius])); return this; }; /** * Draws an arc defined by the radius, startAngle and endAngle arguments, centered at the position (x, y). For * example, to draw a full circle with a radius of 20 centered at (100, 100): * * arc(100, 100, 20, 0, Math.PI*2); * * For detailed information, read the * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arc">whatwg spec</a>. * A tiny API method "a" also exists. * @method arc * @param {Number} x * @param {Number} y * @param {Number} radius * @param {Number} startAngle Measured in radians. * @param {Number} endAngle Measured in radians. * @param {Boolean} anticlockwise * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.arc = function(x, y, radius, startAngle, endAngle, anticlockwise) { this._dirty = this._active = true; if (anticlockwise == null) { anticlockwise = false; } this._activeInstructions.push(new Command(this._ctx.arc, [x, y, radius, startAngle, endAngle, anticlockwise])); return this; }; /** * Draws a quadratic curve from the current drawing point to (x, y) using the control point (cpx, cpy). For detailed * information, read the <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-quadraticcurveto"> * whatwg spec</a>. A tiny API method "qt" also exists. * @method quadraticCurveTo * @param {Number} cpx * @param {Number} cpy * @param {Number} x * @param {Number} y * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.quadraticCurveTo = function(cpx, cpy, x, y) { this._dirty = this._active = true; this._activeInstructions.push(new Command(this._ctx.quadraticCurveTo, [cpx, cpy, x, y])); return this; }; /** * Draws a bezier curve from the current drawing point to (x, y) using the control points (cp1x, cp1y) and (cp2x, * cp2y). For detailed information, read the * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-beziercurveto"> * whatwg spec</a>. A tiny API method "bt" also exists. * @method bezierCurveTo * @param {Number} cp1x * @param {Number} cp1y * @param {Number} cp2x * @param {Number} cp2y * @param {Number} x * @param {Number} y * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { this._dirty = this._active = true; this._activeInstructions.push(new Command(this._ctx.bezierCurveTo, [cp1x, cp1y, cp2x, cp2y, x, y])); return this; }; /** * Draws a rectangle at (x, y) with the specified width and height using the current fill and/or stroke. * For detailed information, read the * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-rect"> * whatwg spec</a>. A tiny API method "r" also exists. * @method rect * @param {Number} x * @param {Number} y * @param {Number} w Width of the rectangle * @param {Number} h Height of the rectangle * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.rect = function(x, y, w, h) { this._dirty = this._active = true; this._activeInstructions.push(new Command(this._ctx.rect, [x, y, w, h])); return this; }; /** * Closes the current path, effectively drawing a line from the current drawing point to the first drawing point specified * since the fill or stroke was last set. A tiny API method "cp" also exists. * @method closePath * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.closePath = function() { if (this._active) { this._dirty = true; this._activeInstructions.push(new Command(this._ctx.closePath, [])); } return this; }; // public methods that roughly map to Flash graphics APIs: /** * Clears all drawing instructions, effectively resetting this Graphics instance. Any line and fill styles will need * to be redefined to draw shapes following a clear call. A tiny API method "c" also exists. * @method clear * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.clear = function() { this._instructions = []; this._oldInstructions = []; this._activeInstructions = []; this._strokeStyleInstructions = this._strokeInstructions = this._fillInstructions = this._fillMatrix = null; this._active = this._dirty = this._strokeIgnoreScale = false; return this; }; /** * Begins a fill with the specified color. This ends the current sub-path. A tiny API method "f" also exists. * @method beginFill * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to * null will result in no fill. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.beginFill = function(color) { if (this._active) { this._newPath(); } this._fillInstructions = color ? [new Command(this._setProp, ["fillStyle", color], false)] : null; this._fillMatrix = null; return this; }; /** * Begins a linear gradient fill defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a * square to display it: * * myGraphics.beginLinearGradientFill(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120); * * A tiny API method "lf" also exists. * @method beginLinearGradientFill * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient * drawing from red to blue. * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw * the first color to 10% then interpolating to the second color at 90%. * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size. * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size. * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size. * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.beginLinearGradientFill = function(colors, ratios, x0, y0, x1, y1) { if (this._active) { this._newPath(); } var o = this._ctx.createLinearGradient(x0, y0, x1, y1); for (var i=0, l=colors.length; i<l; i++) { o.addColorStop(ratios[i], colors[i]); } this._fillInstructions = [new Command(this._setProp, ["fillStyle", o], false)]; this._fillMatrix = null; return this; }; /** * Begins a radial gradient fill. This ends the current sub-path. For example, the following code defines a red to * blue radial gradient centered at (100, 100), with a radius of 50, and draws a circle to display it: * * myGraphics.beginRadialGradientFill(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50).drawCircle(100, 100, 50); * * A tiny API method "rf" also exists. * @method beginRadialGradientFill * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define * a gradient drawing from red to blue. * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, * 0.9] would draw the first color to 10% then interpolating to the second color at 90%. * @param {Number} x0 Center position of the inner circle that defines the gradient. * @param {Number} y0 Center position of the inner circle that defines the gradient. * @param {Number} r0 Radius of the inner circle that defines the gradient. * @param {Number} x1 Center position of the outer circle that defines the gradient. * @param {Number} y1 Center position of the outer circle that defines the gradient. * @param {Number} r1 Radius of the outer circle that defines the gradient. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.beginRadialGradientFill = function(colors, ratios, x0, y0, r0, x1, y1, r1) { if (this._active) { this._newPath(); } var o = this._ctx.createRadialGradient(x0, y0, r0, x1, y1, r1); for (var i=0, l=colors.length; i<l; i++) { o.addColorStop(ratios[i], colors[i]); } this._fillInstructions = [new Command(this._setProp, ["fillStyle", o], false)]; this._fillMatrix = null; return this; }; /** * Begins a pattern fill using the specified image. This ends the current sub-path. A tiny API method "bf" also * exists. * @method beginBitmapFill * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use * as the pattern. * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat", * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat". * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation * will be applied relative to the parent transform. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.beginBitmapFill = function(image, repetition, matrix) { if (this._active) { this._newPath(); } repetition = repetition || ""; var o = this._ctx.createPattern(image, repetition); this._fillInstructions = [new Command(this._setProp, ["fillStyle", o], false)]; this._fillMatrix = matrix ? [matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty] : null; return this; }; /** * Ends the current sub-path, and begins a new one with no fill. Functionally identical to <code>beginFill(null)</code>. * A tiny API method "ef" also exists. * @method endFill * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.endFill = function() { return this.beginFill(); }; /** * Sets the stroke style for the current sub-path. Like all drawing methods, this can be chained, so you can define * the stroke style and color in a single line of code like so: * * myGraphics.setStrokeStyle(8,"round").beginStroke("#F00"); * * A tiny API method "ss" also exists. * @method setStrokeStyle * @param {Number} thickness The width of the stroke. * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt, * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with * the tiny API. * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet. * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel) * for use with the tiny API. * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which * controls at what point a mitered joint will be clipped. * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless * of active transformations. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.setStrokeStyle = function(thickness, caps, joints, miterLimit, ignoreScale) { if (this._active) { this._newPath(); } this._strokeStyleInstructions = [ new Command(this._setProp, ["lineWidth", (thickness == null ? "1" : thickness)], false), new Command(this._setProp, ["lineCap", (caps == null ? "butt" : (isNaN(caps) ? caps : Graphics.STROKE_CAPS_MAP[caps]))], false), new Command(this._setProp, ["lineJoin", (joints == null ? "miter" : (isNaN(joints) ? joints : Graphics.STROKE_JOINTS_MAP[joints]))], false), new Command(this._setProp, ["miterLimit", (miterLimit == null ? "10" : miterLimit)], false) ]; this._strokeIgnoreScale = ignoreScale; return this; }; /** * Begins a stroke with the specified color. This ends the current sub-path. A tiny API method "s" also exists. * @method beginStroke * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to * null will result in no stroke. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.beginStroke = function(color) { if (this._active) { this._newPath(); } this._strokeInstructions = color ? [new Command(this._setProp, ["strokeStyle", color], false)] : null; return this; }; /** * Begins a linear gradient stroke defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a * square to display it: * * myGraphics.setStrokeStyle(10). * beginLinearGradientStroke(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120); * * A tiny API method "ls" also exists. * @method beginLinearGradientStroke * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define * a gradient drawing from red to blue. * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, * 0.9] would draw the first color to 10% then interpolating to the second color at 90%. * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size. * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size. * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size. * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.beginLinearGradientStroke = function(colors, ratios, x0, y0, x1, y1) { if (this._active) { this._newPath(); } var o = this._ctx.createLinearGradient(x0, y0, x1, y1); for (var i=0, l=colors.length; i<l; i++) { o.addColorStop(ratios[i], colors[i]); } this._strokeInstructions = [new Command(this._setProp, ["strokeStyle", o], false)]; return this; }; /** * Begins a radial gradient stroke. This ends the current sub-path. For example, the following code defines a red to * blue radial gradient centered at (100, 100), with a radius of 50, and draws a rectangle to display it: * * myGraphics.setStrokeStyle(10) * .beginRadialGradientStroke(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50) * .drawRect(50, 90, 150, 110); * * A tiny API method "rs" also exists. * @method beginRadialGradientStroke * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define * a gradient drawing from red to blue. * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color * to 100%. * @param {Number} x0 Center position of the inner circle that defines the gradient. * @param {Number} y0 Center position of the inner circle that defines the gradient. * @param {Number} r0 Radius of the inner circle that defines the gradient. * @param {Number} x1 Center position of the outer circle that defines the gradient. * @param {Number} y1 Center position of the outer circle that defines the gradient. * @param {Number} r1 Radius of the outer circle that defines the gradient. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.beginRadialGradientStroke = function(colors, ratios, x0, y0, r0, x1, y1, r1) { if (this._active) { this._newPath(); } var o = this._ctx.createRadialGradient(x0, y0, r0, x1, y1, r1); for (var i=0, l=colors.length; i<l; i++) { o.addColorStop(ratios[i], colors[i]); } this._strokeInstructions = [new Command(this._setProp, ["strokeStyle", o], false)]; return this; }; /** * Begins a pattern fill using the specified image. This ends the current sub-path. Note that unlike bitmap fills, * strokes do not currently support a matrix parameter due to limitations in the canvas API. A tiny API method "bs" * also exists. * @method beginBitmapStroke * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use * as the pattern. * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.beginBitmapStroke = function(image, repetition) { // NOTE: matrix is not supported for stroke because transforms on strokes also affect the drawn stroke width. if (this._active) { this._newPath(); } repetition = repetition || ""; var o = this._ctx.createPattern(image, repetition); this._strokeInstructions = [new Command(this._setProp, ["strokeStyle", o], false)]; return this; }; /** * Ends the current sub-path, and begins a new one with no stroke. Functionally identical to <code>beginStroke(null)</code>. * A tiny API method "es" also exists. * @method endStroke * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.endStroke = function() { this.beginStroke(); return this; }; /** * Maps the familiar ActionScript <code>curveTo()</code> method to the functionally similar {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} * method. * @method curveTo * @type {Function} **/ p.curveTo = p.quadraticCurveTo; /** * Maps the familiar ActionScript <code>drawRect()</code> method to the functionally similar {{#crossLink "Graphics/rect"}}{{/crossLink}} * method. * @method drawRect * @type {Function} **/ p.drawRect = p.rect; /** * Draws a rounded rectangle with all corners with the specified radius. * @method drawRoundRect * @param {Number} x * @param {Number} y * @param {Number} w * @param {Number} h * @param {Number} radius Corner radius. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.drawRoundRect = function(x, y, w, h, radius) { this.drawRoundRectComplex(x, y, w, h, radius, radius, radius, radius); return this; }; /** * Draws a rounded rectangle with different corner radii. Supports positive and negative corner radii. A tiny API * method "rc" also exists. * @method drawRoundRectComplex * @param {Number} x The horizontal coordinate to draw the round rect. * @param {Number} y The vertical coordinate to draw the round rect. * @param {Number} w The width of the round rect. * @param {Number} h The height of the round rect. * @param {Number} radiusTL Top left corner radius. * @param {Number} radiusTR Top right corner radius. * @param {Number} radiusBR Bottom right corner radius. * @param {Number} radiusBL Bottom left corner radius. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.drawRoundRectComplex = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) { var max = (w<h?w:h)/2; var mTL=0, mTR=0, mBR=0, mBL=0; if (radiusTL < 0) { radiusTL *= (mTL=-1); } if (radiusTL > max) { radiusTL = max; } if (radiusTR < 0) { radiusTR *= (mTR=-1); } if (radiusTR > max) { radiusTR = max; } if (radiusBR < 0) { radiusBR *= (mBR=-1); } if (radiusBR > max) { radiusBR = max; } if (radiusBL < 0) { radiusBL *= (mBL=-1); } if (radiusBL > max) { radiusBL = max; } this._dirty = this._active = true; var arcTo=this._ctx.arcTo, lineTo=this._ctx.lineTo; this._activeInstructions.push( new Command(this._ctx.moveTo, [x+w-radiusTR, y]), new Command(arcTo, [x+w+radiusTR*mTR, y-radiusTR*mTR, x+w, y+radiusTR, radiusTR]), new Command(lineTo, [x+w, y+h-radiusBR]), new Command(arcTo, [x+w+radiusBR*mBR, y+h+radiusBR*mBR, x+w-radiusBR, y+h, radiusBR]), new Command(lineTo, [x+radiusBL, y+h]), new Command(arcTo, [x-radiusBL*mBL, y+h+radiusBL*mBL, x, y+h-radiusBL, radiusBL]), new Command(lineTo, [x, y+radiusTL]), new Command(arcTo, [x-radiusTL*mTL, y-radiusTL*mTL, x+radiusTL, y, radiusTL]), new Command(this._ctx.closePath) ); return this; }; /** * Draws a circle with the specified radius at (x, y). * * var g = new createjs.Graphics(); * g.setStrokeStyle(1); * g.beginStroke(createjs.Graphics.getRGB(0,0,0)); * g.beginFill(createjs.Graphics.getRGB(255,0,0)); * g.drawCircle(0,0,3); * * var s = new createjs.Shape(g); * s.x = 100; * s.y = 100; * * stage.addChild(s); * stage.update(); * * A tiny API method "dc" also exists. * @method drawCircle * @param {Number} x x coordinate center point of circle. * @param {Number} y y coordinate center point of circle. * @param {Number} radius Radius of circle. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.drawCircle = function(x, y, radius) { this.arc(x, y, radius, 0, Math.PI*2); return this; }; /** * Draws an ellipse (oval) with a specified width (w) and height (h). Similar to {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}, * except the width and height can be different. A tiny API method "de" also exists. * @method drawEllipse * @param {Number} x x coordinate center point of ellipse. * @param {Number} y y coordinate center point of ellipse. * @param {Number} w height (horizontal diameter) of ellipse. The horizontal radius will be half of this number. * @param {Number} h width (vertical diameter) of ellipse. The vertical radius will be half of this number. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.drawEllipse = function(x, y, w, h) { this._dirty = this._active = true; var k = 0.5522848; var ox = (w / 2) * k; var oy = (h / 2) * k; var xe = x + w; var ye = y + h; var xm = x + w / 2; var ym = y + h / 2; this._activeInstructions.push( new Command(this._ctx.moveTo, [x, ym]), new Command(this._ctx.bezierCurveTo, [x, ym-oy, xm-ox, y, xm, y]), new Command(this._ctx.bezierCurveTo, [xm+ox, y, xe, ym-oy, xe, ym]), new Command(this._ctx.bezierCurveTo, [xe, ym+oy, xm+ox, ye, xm, ye]), new Command(this._ctx.bezierCurveTo, [xm-ox, ye, x, ym+oy, x, ym]) ); return this; }; /** * Provides a method for injecting arbitrary Context2D (aka Canvas) API calls into a Graphics queue. The specified * callback function will be called in sequence with other drawing instructions. The callback will be executed in the * scope of the target canvas's Context2D object, and will be passed the data object as a parameter. * * This is an advanced feature. It can allow for powerful functionality, like injecting output from tools that * export Context2D instructions, executing raw canvas calls within the context of the display list, or dynamically * modifying colors or stroke styles within a Graphics instance over time, but it is not intended for general use. * * Within a Graphics queue, each path begins by applying the fill and stroke styles and settings, followed by * drawing instructions, followed by the fill() and/or stroke() commands. This means that within a path, inject() can * update the fill & stroke styles, but for it to be applied in a predictable manner, you must have begun a fill or * stroke (as appropriate) normally via the Graphics API. For example: * * function setColor(color) { * this.fillStyle = color; * } * * // this will not draw anything - no fill was begun, so fill() is not called: * myGraphics.inject(setColor, "red").drawRect(0,0,100,100); * * // this will draw the rect in green: * myGraphics.beginFill("#000").inject(setColor, "green").drawRect(0,0,100,100); * * // this will draw both rects in blue, because there is only a single path * // so the second inject overwrites the first: * myGraphics.beginFill("#000").inject(setColor, "green").drawRect(0,0,100,100) * .inject(setColor, "blue").drawRect(100,0,100,100); * * // this will draw the first rect in green, and the second in blue: * myGraphics.beginFill("#000").inject(setColor, "green").drawRect(0,0,100,100) * .beginFill("#000").inject(setColor, "blue").drawRect(100,0,100,100); * * @method inject * @param {Function} callback The function to execute. * @param {Object} data Arbitrary data that will be passed to the callback when it is executed. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.inject = function(callback, data) { this._dirty = this._active = true; this._activeInstructions.push( new Command(callback, [data]) ); return this; }; /** * Draws a star if pointSize is greater than 0, or a regular polygon if pointSize is 0 with the specified number of * points. For example, the following code will draw a familiar 5 pointed star shape centered at 100, 100 and with a * radius of 50: * * myGraphics.beginFill("#FF0").drawPolyStar(100, 100, 50, 5, 0.6, -90); * // Note: -90 makes the first point vertical * * A tiny API method "dp" also exists. * * @method drawPolyStar * @param {Number} x Position of the center of the shape. * @param {Number} y Position of the center of the shape. * @param {Number} radius The outer radius of the shape. * @param {Number} sides The number of points on the star or sides on the polygon. * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy. * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point * directly to the right of the center. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.drawPolyStar = function(x, y, radius, sides, pointSize, angle) { this._dirty = this._active = true; if (pointSize == null) { pointSize = 0; } pointSize = 1-pointSize; if (angle == null) { angle = 0; } else { angle /= 180/Math.PI; } var a = Math.PI/sides; this._activeInstructions.push(new Command(this._ctx.moveTo, [x+Math.cos(angle)*radius, y+Math.sin(angle)*radius])); for (var i=0; i<sides; i++) { angle += a; if (pointSize != 1) { this._activeInstructions.push(new Command(this._ctx.lineTo, [x+Math.cos(angle)*radius*pointSize, y+Math.sin(angle)*radius*pointSize])); } angle += a; this._activeInstructions.push(new Command(this._ctx.lineTo, [x+Math.cos(angle)*radius, y+Math.sin(angle)*radius])); } return this; }; /** * Decodes a compact encoded path string into a series of draw instructions. * This format is not intended to be human readable, and is meant for use by authoring tools. * The format uses a base64 character set, with each character representing 6 bits, to define a series of draw * commands. * * Each command is comprised of a single "header" character followed by a variable number of alternating x and y * position values. Reading the header bits from left to right (most to least significant): bits 1 to 3 specify the * type of operation (0-moveTo, 1-lineTo, 2-quadraticCurveTo, 3-bezierCurveTo, 4-closePath, 5-7 unused). Bit 4 * indicates whether position values use 12 bits (2 characters) or 18 bits (3 characters), with a one indicating the * latter. Bits 5 and 6 are currently unused. * * Following the header is a series of 0 (closePath), 2 (moveTo, lineTo), 4 (quadraticCurveTo), or 6 (bezierCurveTo) * parameters. These parameters are alternating x/y positions represented by 2 or 3 characters (as indicated by the * 4th bit in the command char). These characters consist of a 1 bit sign (1 is negative, 0 is positive), followed * by an 11 (2 char) or 17 (3 char) bit integer value. All position values are in tenths of a pixel. Except in the * case of move operations which are absolute, this value is a delta from the previous x or y position (as * appropriate). * * For example, the string "A3cAAMAu4AAA" represents a line starting at -150,0 and ending at 150,0. * <br />A - bits 000000. First 3 bits (000) indicate a moveTo operation. 4th bit (0) indicates 2 chars per * parameter. * <br />n0 - 110111011100. Absolute x position of -150.0px. First bit indicates a negative value, remaining bits * indicate 1500 tenths of a pixel. * <br />AA - 000000000000. Absolute y position of 0. * <br />I - 001100. First 3 bits (001) indicate a lineTo operation. 4th bit (1) indicates 3 chars per parameter. * <br />Au4 - 000000101110111000. An x delta of 300.0px, which is added to the previous x value of -150.0px to * provide an absolute position of +150.0px. * <br />AAA - 000000000000000000. A y delta value of 0. * * A tiny API method "p" also exists. * @method decodePath * @param {String} str The path string to decode. * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) **/ p.decodePath = function(str) { var instructions = [this.moveTo, this.lineTo, this.quadraticCurveTo, this.bezierCurveTo, this.closePath]; var paramCount = [2, 2, 4, 6, 0]; var i=0, l=str.length; var params = []; var x=0, y=0; var base64 = Graphics.BASE_64; while (i<l) { var c = str.charAt(i); var n = base64[c]; var fi = n>>3; // highest order bits 1-3 code for operation. var f = instructions[fi]; // check that we have a valid instruction & that the unused bits are empty: if (!f || (n&3)) { throw("bad path data (@"+i+"): "+c); } var pl = paramCount[fi]; if (!fi) { x=y=0; } // move operations reset the position. params.length = 0; i++; var charCount = (n>>2&1)+2; // 4th header bit indicates number size for this operation. for (var p=0; p<pl; p++) { var num = base64[str.charAt(i)]; var sign = (num>>5) ? -1 : 1; num = ((num&31)<<6)|(base64[str.charAt(i+1)]); if (charCount == 3) { num = (num<<6)|(base64[str.charAt(i+2)]); } num = sign*num/10; if (p%2) { x = (num += x); } else { y = (num += y); } params[p] = num; i += charCount; } f.apply(this,params); } return this; }; /** * Returns a clone of this Graphics instance. * @method clone * @return {Graphics} A clone of the current Graphics instance. **/ p.clone = function() { var o = new Graphics(); o._instructions = this._instructions.slice(); o._activeInstructions = this._activeInstructions.slice(); o._oldInstructions = this._oldInstructions.slice(); if (this._fillInstructions) { o._fillInstructions = this._fillInstructions.slice(); } if (this._strokeInstructions) { o._strokeInstructions = this._strokeInstructions.slice(); } if (this._strokeStyleInstructions) { o._strokeStyleInstructions = this._strokeStyleInstructions.slice(); } o._active = this._active; o._dirty = this._dirty; o._fillMatrix = this._fillMatrix; o._strokeIgnoreScale = this._strokeIgnoreScale; return o; }; /** * Returns a string representation of this object. * @method toString * @return {String} a string representation of the instance. **/ p.toString = function() { return "[Graphics]"; }; // tiny API: /** Shortcut to moveTo. * @method mt * @protected * @type {Function} **/ p.mt = p.moveTo; /** Shortcut to lineTo. * @method lt * @protected * @type {Function} **/ p.lt = p.lineTo; /** Shortcut to arcTo. * @method at * @protected * @type {Function} **/ p.at = p.arcTo; /** Shortcut to bezierCurveTo. * @method bt * @protected * @type {Function} **/ p.bt = p.bezierCurveTo; /** Shortcut to quadraticCurveTo / curveTo. * @method qt * @protected * @type {Function} **/ p.qt = p.quadraticCurveTo; /** Shortcut to arc. * @method a * @protected * @type {Function} **/ p.a = p.arc; /** Shortcut to rect. * @method r * @protected * @type {Function} **/ p.r = p.rect; /** Shortcut to closePath. * @method cp * @protected * @type {Function} **/ p.cp = p.closePath; /** Shortcut to clear. * @method c * @protected * @type {Function} **/ p.c = p.clear; /** Shortcut to beginFill. * @method f * @protected * @type {Function} **/ p.f = p.beginFill; /** Shortcut to beginLinearGradientFill. * @method lf * @protected * @type {Function} **/ p.lf = p.beginLinearGradientFill; /** Shortcut to beginRadialGradientFill. * @method rf * @protected * @type {Function} **/ p.rf = p.beginRadialGradientFill; /** Shortcut to beginBitmapFill. * @method bf * @protected * @type {Function} **/ p.bf = p.beginBitmapFill; /** Shortcut to endFill. * @method ef * @protected * @type {Function} **/ p.ef = p.endFill; /** Shortcut to setStrokeStyle. * @method ss * @protected * @type {Function} **/ p.ss = p.setStroke