/*! * jSketch 1.0 | Luis A. Leiva | MIT license * A simple JavaScript library for drawing facilities on HTML5 canvas. */ /** * A simple JavaScript library for drawing facilities on HTML5 canvas. * This class is mostly a wrapper for the HTML5 canvas API with some syntactic sugar, * such as function chainability and old-school AS3-like notation. * @name jSketch * @class * @version 1.0 * @author Luis A. Leiva * @license MIT license * @example * var canvas1 = document.getElementById('foo'); * var canvas2 = document.getElementById('bar'); * // Instantiate once, reuse everywhere. * var brush = new jSketch(canvas1).lineStyle('red').moveTo(50,50).lineTo(10,10).stroke(); * // Actually, `.moveTo(50,50).lineTo(10,10)` can be just `.line(50,50, 10,10)`. * // Switching between contexts removes the need of having to reinstantiate the jSketch class. * brush.setContext(canvas2).beginFill('#5F7').fillCircle(30,30,8).endFill(); */ ;(function(window) { /** * @constructor * @param {Object|Strig} elem DOM element or selector. * @param {Object} [options] Configuration (default: {@link Sketchable#defaults}). */ function jSketch(elem, options) { if (!elem) throw new Error('Sketchable requires a DOM element.'); if (typeof elem === 'string') elem = document.querySelector(elem); // Set drawing context first. this.setContext(elem); // Scene defaults. this.stageWidth = elem.width; this.stageHeight = elem.height; // Make room for storing some data such as line type, colors, etc. this.data = options; // Set drawing defaults. // All methods are chainable. return this.setDefaults(); }; /** * jSketch methods (publicly extensible). * @ignore * @memberof jSketch * @see jSketch */ jSketch.prototype = { /** * Allows to change the drawing context at runtime. * @param {Object} elem DOM element. * @return jSketch * @memberof jSketch */ setContext: function(elem) { if (!elem) throw new Error('No canvas element specified.'); // Save shortcuts: canvas (DOM elem) & graphics (2D canvas context). this.canvas = elem; this.context = elem.getContext('2d'); // Always allow chainability. return this; }, /** * Sets drawing defaults: * - fillStyle: Fill style color ('#F00'). * - strokeStyle: Stroke style color ('#F0F'). * - lineWidth: Line width (2). * - lineCap: Line cap ('round'). * - lineJoin: Line join ('round'). * - miterLimit: Line miter (10). Works only if the lineJoin attribute is "miter". * @return jSketch * @memberof jSketch */ setDefaults: function() { return this.saveGraphics({ fillStyle: this.data.fillStyle || '#F00', strokeStyle: this.data.strokeStyle || '#F0F', lineWidth: this.data.lineWidth || 2, lineCap: this.data.lineCap || 'round', lineJoin: this.data.lineJoin || 'round', miterLimit: this.data.miterLimit || 10 }).restoreGraphics(); }, /** * Sets the dimensions of canvas. * @param {Number} width New canvas width. * @param {Number} height New canvas width. * @return jSketch * @memberof jSketch */ size: function(width, height) { this.stageWidth = width; this.stageHeight = height; this.canvas.width = width; this.canvas.height = height; // On resizing we lose drawing options, so restore them. this.restoreGraphics(); return this; }, /** * Sets the background color of canvas. * @param {String} color An HTML color. * @return jSketch * @memberof jSketch */ background: function(color) { this.beginFill(color); this.context.fillRect(0,0, this.stageWidth,this.stageHeight); this.endFill(); return this; }, /** * Shortcut for setting the size + background color. * @param {Number} width New canvas width. * @param {Number} height New canvas width. * @param {String} bgcolor An HTML color. * @return jSketch * @memberof jSketch */ stage: function(width, height, bgcolor) { this.size(width,height).background(bgcolor); return this; }, /** * Sets the fill color. * @param {String} color An HTML color. * @return jSketch * @memberof jSketch */ beginFill: function(color) { this.saveGraphics(); this.context.fillStyle = color; return this; }, /** * Recovers the fill color that was set before beginFill(). * @return jSketch * @memberof jSketch */ endFill: function() { this.restoreGraphics(); return this; }, /** * Sets the line style. * @param {String} color An HTML color. * @param {Number} thickness Line thickness. * @param {String} capStyle Style of line cap. * @param {String} joinStyle Style of line join. * @param {String} miter Style of line miter. Only works if capStyle is "miter". * @return jSketch * @memberof jSketch */ lineStyle: function(color, thickness, capStyle, joinStyle, miter) { return this.saveGraphics({ strokeStyle: color || this.data.strokeStyle, lineWidth: thickness || this.data.lineWidth, lineCap: capStyle || this.data.lineCap, lineJoin: joinStyle || this.data.lineJoin, miterLimit: miter || this.data.miterLimit }).restoreGraphics(); }, /** * Move brush to a coordinate in canvas. * @param {Number} x Horizontal coordinate. * @param {Number} y Vertical coordinate. * @return jSketch * @memberof jSketch */ moveTo: function(x, y) { this.context.moveTo(x,y); return this; }, /** * Draws line to given coordinate. * @param {Number} x Horizontal coordinate. * @param {Number} y Vertical coordinate. * @return jSketch * @memberof jSketch */ lineTo: function(x, y) { this.context.lineTo(x,y); return this; }, /** * Draws line from point 1 to point 2. * @param {Number} x1 Horizontal coordinate of point 1. * @param {Number} y1 Vertical coordinate of point 1. * @param {Number} x2 Horizontal coordinate of point 2. * @param {Number} y2 Vertical coordinate of point 2. * @return jSketch * @memberof jSketch */ line: function(x1,y1, x2,y2) { this.context.moveTo(x1,y1); this.lineTo(x2,y2); return this; }, /** * Draws curve to given coordinate. * @param {Number} x Horizontal coordinate. * @param {Number} y Vertical coordinate. * @param {Number} cpx Horizontal coordinate of control point. * @param {Number} cpy Vertical coordinate of control point. * @return jSketch * @memberof jSketch */ curveTo: function(x,y, cpx,cpy) { this.context.quadraticCurveTo(cpx,cpy, x,y); return this; }, /** * Draws curve from coordinate 1 to coordinate 2. * @param {Number} x1 Horizontal coordinate of point 1. * @param {Number} y1 Vertical coordinate of point 1. * @param {Number} x2 Horizontal coordinate of point 2. * @param {Number} y2 Vertical coordinate of point 2. * @param {Number} cpx Horizontal coordinate of control point. * @param {Number} cpy Vertical coordinate of control point. * @return jSketch * @memberof jSketch */ curve: function(x1,y1, x2,y2, cpx,cpy) { this.context.moveTo(x1,y1); this.curveTo(x2,y2, cpx,cpy); return this; }, /** * Strokes a given path. * @return jSketch * @memberof jSketch */ stroke: function() { this.context.stroke(); return this; }, /** * Draws a stroke-only rectangle. * @param {Number} x Horizontal coordinate. * @param {Number} y Vertical coordinate. * @param {Number} width Rectangle width. * @param {Number} height Rectangle height. * @return jSketch * @memberof jSketch */ strokeRect: function(x,y, width,height) { this.context.beginPath(); this.context.strokeRect(x,y, width,height); this.context.closePath(); return this; }, /** * Draws a filled rectangle. * @param {Number} x Horizontal coordinate. * @param {Number} y Vertical coordinate. * @param {Number} width Rectangle width. * @param {Number} height Rectangle height. * @return jSketch * @memberof jSketch */ fillRect: function(x,y, width,height) { this.context.beginPath(); this.context.fillRect(x,y, width,height); this.context.closePath(); return this; }, /** * Draws a stroke-only circle. * @param {Number} x Horizontal coordinate. * @param {Number} y Vertical coordinate. * @param {Number} radius Circle radius. * @return jSketch * @memberof jSketch */ strokeCircle: function(x,y, radius) { this.context.beginPath(); this.context.arc(x,y, radius, 0, 2*Math.PI, false); this.context.stroke(); this.context.closePath(); return this; }, /** * Draws a filled circle. * @param {Number} x Horizontal coordinate. * @param {Number} y Vertical coordinate. * @param {Number} radius Circle radius. * @return jSketch * @memberof jSketch */ fillCircle: function(x,y, radius) { this.context.beginPath(); this.context.arc(x,y, radius, 0, 2*Math.PI, false); this.context.fill(); this.context.closePath(); return this; }, /** * Experimental. * @ignore */ radialCircle: function(x,y, radius, glowSize, colors) { var g = this.context.createRadialGradient(x,y, radius, x,y, glowSize || 5); if (!colors || colors.constructor.name !== 'array') { colors = [this.context.fillStyle, 'white']; } for (var s = 0; s < colors.length; s++) { var color = colors[s]; g.addColorStop(s, color); } this.beginFill(g).fillCircle(x,y, radius).endFill(); return this; }, /** * A path is started. * @return jSketch * @memberof jSketch */ beginPath: function() { this.saveGraphics(); this.context.beginPath(); return this; }, /** * A path is finished. * @return jSketch * @memberof jSketch */ closePath: function() { this.context.closePath(); this.restoreGraphics(); return this; }, /** * Sets brush to eraser mode. * @return jSketch * @memberof jSketch */ eraser: function() { this.context.globalCompositeOperation = 'destination-out'; return this; }, /** * Sets brush to pencil mode. * @return jSketch * @memberof jSketch */ pencil: function() { this.context.globalCompositeOperation = 'source-over'; return this; }, /** * Clears stage. * @return jSketch * @memberof jSketch */ clear: function() { // Note: using 'this.canvas.width = this.canvas.width' resets _all_ styles, so better use clearRect. this.context.clearRect(0,0, this.stageWidth,this.stageHeight); return this; }, /** * Saves a snapshot of all styles and transformations. * @return jSketch * @memberof jSketch */ save: function() { this.context.save(); return this; }, /** * Restores previous drawing state. * @return jSketch * @memberof jSketch */ restore: function() { this.context.restore(); return this; }, /** * Saves given drawing settings. * @param {Object} [options] Graphics options. * @return jSketch * @memberof jSketch */ saveGraphics: function(options) { for (var opt in options) { this.data[opt] = options[opt]; } return this; }, /** * Restores given drawing settings. * @return jSketch * @memberof jSketch */ restoreGraphics: function() { for (var opt in this.data) { this.context[opt] = this.data[opt]; } return this; }, /** * Draws an image. * @param {String} src Image source path. * @param {Number} [x] Horizontal coordinate. * @param {Number} [y] Vertical coordinate. * @return jSketch * @memberof jSketch */ drawImage: function(src, x,y) { if (typeof x === 'undefined') x = 0; if (typeof y === 'undefined') y = 0; var self = this, img = new Image(); img.src = src; img.onload = function() { self.context.drawImage(img, x,y); } return this; } }; // Expose. window.jSketch = jSketch; })(this);