diff --git a/build.js b/build.js index 43a1f0ff..5ecb1ad7 100644 --- a/build.js +++ b/build.js @@ -113,7 +113,7 @@ var filesToInclude = [ 'src/static_canvas.class.js', - ifSpecifiedInclude('freedrawing', 'src/freedrawing.class.js'), + ifSpecifiedInclude('freedrawing', 'src/pencil_brush.class.js'), ifSpecifiedInclude('interaction', 'src/canvas.class.js'), diff --git a/dist/all.js b/dist/all.js index ce6084a9..08a33b28 100644 --- a/dist/all.js +++ b/dist/all.js @@ -6580,45 +6580,118 @@ fabric.util.string = { })(); -(function(global) { - - "use strict"; - - var fabric = global.fabric || (global.fabric = { }); +(function() { var utilMin = fabric.util.array.min, utilMax = fabric.util.array.max; - if (fabric.FreeDrawing) { - fabric.warn('fabric.FreeDrawing is already defined'); - return; - } - /** - * Free drawing class - * Free Drawer handles scribbling on a fabric canvas - * It converts the hand writting to a SVG Path and adds this path to the canvas - * - * @class FreeDrawing - * @memberOf fabric + * PencilBrush class + * @class fabric.PencilBrush */ - fabric.FreeDrawing = fabric.util.createClass( /** @scope fabric.FreeDrawing.prototype */ { + fabric.PencilBrush = fabric.util.createClass( /** @scope fabric.PencilBrush.prototype */ { + + /** + * Color of the pencil + * @property + * @type String + */ + color: 'rgb(0, 0, 0)', + + /** + * Width of a pencil + * @property + * @type Number + */ + width: 1, + + /** + * Shadow blur of a pencil + * @property + * @type Number + */ + shadowBlur: 0, + + /** + * Shadow color of a pencil + * @property + * @type String + */ + shadowColor: '', + + /** + * Shadow offset x of a pencil + * @property + * @type Number + */ + shadowOffsetX: 0, + + /** + * Shadow offset y of a pencil + * @property + * @type Number + */ + shadowOffsetY: 0, /** * Constructor - * @metod initialize - * @param fabricCanvas {fabric.Canvas} - * @return {fabric.FreeDrawing} + * @method initialize + * @param {fabric.Canvas} canvas + * @return {fabric.PencilBrush} Instance of a pencil brush */ - initialize: function(fabricCanvas) { - this.canvas = fabricCanvas; - this._points = []; + initialize: function(canvas) { + this.canvas = canvas; + this._points = [ ]; + }, + + /** + * @method onMouseDown + * @param {Object} pointer + */ + onMouseDown: function(pointer) { + this._prepareForDrawing(pointer); + // capture coordinates immediately + // this allows to draw dots (when movement never occurs) + this._captureDrawingPath(pointer); + }, + + /** + * @method onMouseMove + * @param {Object} pointer + */ + onMouseMove: function(pointer) { + this._captureDrawingPath(pointer); + // redraw curve + // clear top canvas + this.canvas.clearContext(this.canvas.contextTop); + this._render(this.canvas.contextTop); + }, + + /** + * @method onMouseUp + */ + onMouseUp: function() { + this._finalizeAndAddPath(); + }, + + /** + * @method _prepareForDrawing + * @param {Object} pointer + */ + _prepareForDrawing: function(pointer) { + + var p = new fabric.Point(pointer.x, pointer.y); + + this._reset(); + this._addPoint(p); + + this.canvas.contextTop.moveTo(p.x, p.y); }, /** * @private * @method _addPoint - * + * @param {fabric.Point} point */ _addPoint: function(point) { this._points.push(point); @@ -6634,28 +6707,22 @@ fabric.util.string = { */ _reset: function() { this._points.length = 0; + var ctx = this.canvas.contextTop; - // set freehanddrawing line canvas parameters - ctx.strokeStyle = this.canvas.freeDrawingColor; - ctx.lineWidth = this.canvas.freeDrawingLineWidth; + ctx.strokeStyle = this.color; + ctx.lineWidth = this.width; + + if (this.shadowBlur) { + ctx.shadowBlur = this.shadowBlur; + ctx.shadowColor = this.shadowColor || this.color; + ctx.shadowOffsetX = this.shadowOffsetX; + ctx.shadowOffsetY = this.shadowOffsetY; + } + ctx.lineCap = ctx.lineJoin = 'round'; }, - /** - * @method _prepareForDrawing - */ - _prepareForDrawing: function(pointer) { - - this.canvas._isCurrentlyDrawing = true; - this.canvas.discardActiveObject().renderAll(); - - var p = new fabric.Point(pointer.x, pointer.y); - this._reset(); - this._addPoint(p); - this.canvas.contextTop.moveTo(p.x, p.y); - }, - /** * @private * @method _captureDrawingPath @@ -6669,12 +6736,10 @@ fabric.util.string = { }, /** - * Draw a smooth path on the topCanvas using - * quadraticCurveTo. + * Draw a smooth path on the topCanvas using quadraticCurveTo * * @private * @method _render - * */ _render: function() { var ctx = this.canvas.contextTop; @@ -6702,12 +6767,10 @@ fabric.util.string = { }, /** - * Return an SVG path based on our - * captured points and their boundinb box. + * Return an SVG path based on our captured points and their bounding box * * @private * @method _getSVGPathData - * */ _getSVGPathData: function() { this.box = this.getPathBoundingBox(this._points); @@ -6787,9 +6850,9 @@ fabric.util.string = { * @method _finalizeAndAddPath */ _finalizeAndAddPath: function() { - this.canvas._isCurrentlyDrawing = false; var ctx = this.canvas.contextTop; ctx.closePath(); + var path = this._getSVGPathData(); path = path.join(''); @@ -6804,8 +6867,8 @@ fabric.util.string = { var p = new fabric.Path(path); p.fill = null; - p.stroke = this.canvas.freeDrawingColor; - p.strokeWidth = this.canvas.freeDrawingLineWidth; + p.stroke = this.color; + p.strokeWidth = this.width; this.canvas.add(p); // set path origin coordinates based on our bounding box @@ -6825,9 +6888,7 @@ fabric.util.string = { this.canvas.fire('path:created', { path: p }); } }); - -})(typeof exports !== 'undefined' ? exports : this); - +})(); (function() { var extend = fabric.util.object.extend, @@ -6936,20 +6997,6 @@ fabric.util.string = { */ selectionLineWidth: 1, - /** - * Color of the line used in free drawing mode - * @property - * @type String - */ - freeDrawingColor: 'rgb(0, 0, 0)', - - /** - * Width of a line used in free drawing mode - * @property - * @type Number - */ - freeDrawingLineWidth: 1, - /** * Default cursor value used when hovering over an object on canvas * @property @@ -6971,6 +7018,13 @@ fabric.util.string = { */ defaultCursor: 'default', + /** + * Cursor value used during free drawing + * @property + * @type String + */ + freeDrawingCursor: 'crosshair', + /** * Cursor value used for rotation point * @property @@ -7006,10 +7060,12 @@ fabric.util.string = { _initInteractive: function() { this._currentTransform = null; this._groupSelector = null; - this.freeDrawing = fabric.FreeDrawing && new fabric.FreeDrawing(this); this._initWrapperElement(); this._createUpperCanvas(); this._initEvents(); + + this.freeDrawingBrush = fabric.PencilBrush && new fabric.PencilBrush(this); + this.calcOffset(); }, @@ -7083,7 +7139,8 @@ fabric.util.string = { var target; if (this.isDrawingMode && this._isCurrentlyDrawing) { - this.freeDrawing._finalizeAndAddPath(); + this._isCurrentlyDrawing = false; + this.freeDrawingBrush.onMouseUp(); this.fire('mouse:up', { e: e }); return; } @@ -7167,12 +7224,11 @@ fabric.util.string = { if (this.isDrawingMode) { pointer = this.getPointer(e); - this.freeDrawing._prepareForDrawing(pointer); - // capture coordinates immediately; - // this allows to draw dots (when movement never occurs) - this.freeDrawing._captureDrawingPath(pointer); + this._isCurrentlyDrawing = true; + this.discardActiveObject().renderAll(); + this.freeDrawingBrush.onMouseDown(pointer); this.fire('mouse:down', { e: e }); return; } @@ -7258,13 +7314,9 @@ fabric.util.string = { if (this.isDrawingMode) { if (this._isCurrentlyDrawing) { pointer = this.getPointer(e); - this.freeDrawing._captureDrawingPath(pointer); - - // redraw curve - // clear top canvas - this.clearContext(this.contextTop); - this.freeDrawing._render(this.contextTop); + this.freeDrawingBrush.onMouseMove(pointer); } + this.upperCanvasEl.style.cursor = this.freeDrawingCursor; this.fire('mouse:move', { e: e }); return; } @@ -8637,9 +8689,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, { if (!serialized || (serialized && !serialized.objects)) return; this.clear(); + var _this = this; this._enlivenObjects(serialized.objects, function () { _this.backgroundColor = serialized.background; + var backgroundImageLoaded, overlayImageLoaded; if (serialized.backgroundImage) { _this.setBackgroundImage(serialized.backgroundImage, function() { @@ -8648,11 +8702,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, { _this.backgroundImageStretch = serialized.backgroundImageStretch; _this.renderAll(); + backgroundImageLoaded = true; - callback && callback(); + callback && overlayImageLoaded && callback(); }); - return; } + else { + backgroundImageLoaded = true; + } + if (serialized.overlayImage) { _this.setOverlayImage(serialized.overlayImage, function() { @@ -8660,12 +8718,18 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, { _this.overlayImageTop = serialized.overlayImageTop || 0; _this.renderAll(); + overlayImageLoaded = true; - callback && callback(); + callback && backgroundImageLoaded && callback(); }); - return; } - callback && callback(); + else { + overlayImageLoaded = true; + } + + if (!serialized.backgroundImage && !serialized.overlayImage) { + callback && callback(); + } }); return this; @@ -9397,7 +9461,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, { if (this.active && !noTransform) { this.drawBorders(ctx); - this.drawCorners(ctx); + this.hideCorners || this.drawCorners(ctx); } ctx.restore(); }, @@ -10026,7 +10090,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, { /** * Converts an object into a data-url-like string * @method toDataURL - * @return {String} string of data + * @param callback {Function} callback that recieves resulting data-url string */ toDataURL: function(callback) { var el = fabric.document.createElement('canvas'); @@ -10581,7 +10645,13 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, { var obj = this; to = to.toString(); - options || (options = { }); + + if (!options) { + options = { }; + } + else { + options = fabric.util.object.clone(options); + } if (!('from' in options)) { options.from = this.get(property); @@ -13887,7 +13957,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, { }; /** @ignore */ - img.onerror = function(e) { + img.onerror = function() { fabric.log('Error loading ' + img.src); callback && callback(null, true); img = img.onload = img.onerror = null; diff --git a/dist/all.min.js b/dist/all.min.js index 758904d2..6ad36a56 100644 --- a/dist/all.min.js +++ b/dist/all.min.js @@ -1,5 +1,5 @@ /* build: `node build.js modules=ALL` *//*! Fabric.js Copyright 2008-2012, Printio (Juriy Zaytsev, Maxim Chernyak) */var fabric=fabric||{version:"1.0.0"};typeof exports!="undefined"&&(exports.fabric=fabric),typeof document!="undefined"&&typeof window!="undefined"?(fabric.document=document,fabric.window=window):(fabric.document=require("jsdom").jsdom("
"),fabric.window=fabric.document.createWindow()),fabric.isTouchSupported="ontouchstart"in fabric.document.documentElement,fabric.isLikelyNode=typeof Buffer!="undefined"&&typeof window=="undefined";var Cufon=function(){function r(e){var t=this.face=e.face;this.glyphs=e.glyphs,this.w=e.w,this.baseSize=parseInt(t["units-per-em"],10),this.family=t["font-family"].toLowerCase(),this.weight=t["font-weight"],this.style=t["font-style"]||"normal",this.viewBox=function(){var e=t.bbox.split(/\s+/),n={minX:parseInt(e[0],10),minY:parseInt(e[1],10),maxX:parseInt(e[2],10),maxY:parseInt(e[3],10)};return n.width=n.maxX-n.minX,n.height=n.maxY-n.minY,n.toString=function(){return[this.minX,this.minY,this.width,this.height].join(" ")},n}(),this.ascent=-parseInt(t.ascent,10),this.descent=-parseInt(t.descent,10),this.height=-this.ascent+this.descent}function i(){var e={},t={oblique:"italic",italic:"oblique"};this.add=function(t){(e[t.style]||(e[t.style]={}))[t.weight]=t},this.get=function(n,r){var i=e[n]||e[t[n]]||e.normal||e.italic||e.oblique;if(!i)return null;r={normal:400,bold:700}[r]||parseInt(r,10);if(i[r])return i[r];var s={1:1,99:0}[r%100],o=[],u,a;s===undefined&&(s=r>400),r==500&&(r=400);for(var f in i){f=parseInt(f,10);if(!u||fa)a=f;o.push(f)}return ra&&(r=a),o.sort(function(e,t){return(s?e>r&&t>r?er?n:i-t;s(u(f,a,c,n));if(i>r||o()){e.onComplete&&e.onComplete();return}l(h)}()}function c(e,t,n){if(e){var r=new Image;r.onload=function(){t&&t.call(n,r),r=r.onload=null},r.src=e}else t&&t.call(n,e)}function h(e,t){function n(e){return fabric[fabric.util.string.camelize(fabric.util.string.capitalize(e))]}function r(){++s===o&&t&&t(i)}var i=[],s=0,o=e.length;e.forEach(function(e,t){if(!e.type)return;var s=n(e.type);s.async?s.fromObject(e,function(e,n){n||(i[t]=e),r()}):(i[t]=s.fromObject(e),r())})}function p(e,t,n){var r;if(e.length>1){var i=e.some(function(e){return e.type==="text"});i?(r=new fabric.Group([],t),e.reverse().forEach(function(e){e.cx&&(e.left=e.cx),e.cy&&(e.top=e.cy),r.addWithUpdate(e)})):r=new fabric.PathGroup(e,t)}else r=e[0];return typeof n!="undefined"&&r.setSourcePath(n),r}function d(e,t,n){if(n&&Object.prototype.toString.call(n)==="[object Array]")for(var r=0,i=n.length;r=r&&(r=e[n][t]);else while(n--)e[n]>=r&&(r=e[n]);return r}function r(e,t){if(!e||e.length===0)return undefined;var n=e.length-1,r=t?e[n][t]:e[n];if(t)while(n--)e[n][t] 0&&this.init(e,t)}var t=e.fabric||(e.fabric={});if(t.Point){t.warn("fabric.Point is already defined");return}t.Point=n,n.prototype={constructor:n,init:function(e,t){this.x=e,this.y=t},add:function(e){return new n(this.x+e.x,this.y+e.y)},addEquals:function(e){return this.x+=e.x,this.y+=e.y,this},scalarAdd:function(e){return new n(this.x+e,this.y+e)},scalarAddEquals:function(e){return this.x+=e,this.y+=e,this},subtract:function(e){return new n(this.x-e.x,this.y-e.y)},subtractEquals:function(e){return this.x-=e.x,this.y-=e.y,this},scalarSubtract:function(e){return new n(this.x-e,this.y-e)},scalarSubtractEquals:function(e){return this.x-=e,this.y-=e,this},multiply:function(e){return new n(this.x*e,this.y*e)},multiplyEquals:function(e){return this.x*=e,this.y*=e,this},divide:function(e){return new n(this.x/e,this.y/e)},divideEquals:function(e){return this.x/=e,this.y/=e,this},eq:function(e){return this.x===e.x&&this.y===e.y},lt:function(e){return this.x>>0,n=0,r;if(arguments.length>1)r=arguments[1];else do{if(n in this){r=this[n++];break}if(++n>=t)throw new TypeError}while(!0);for(;n0&&(i.status="Intersection"),i},t.Intersection.intersectPolygonPolygon=function(e,t){var r=new n("No Intersection"),i=e.length;for(var s=0;s0&&(r.status="Intersection"),r},t.Intersection.intersectPolygonRectangle=function(e,r,i){var s=r.min(i),o=r.max(i),u=new t.Point(o.x,s.y),a=new t.Point(s.x,o.y),f=n.intersectLinePolygon(s,u,e),l=n.intersectLinePolygon(u,o,e),c=n.intersectLinePolygon(o,a,e),h=n.intersectLinePolygon(a,s,e),p=new n("No Intersection");return p.appendPoints(f.points),p.appendPoints(l.points),p.appendPoints(c.points),p.appendPoints(h.points),p.points.length>0&&(p.status="Intersection"),p}}(typeof exports!="undefined"?exports:this),function(e){"use strict";function n(e){e?this._tryParsingColor(e):this.setSource([0,0,0,1])}var t=e.fabric||(e.fabric={});if(t.Color){t.warn("fabric.Color is already defined.");return}t.Color=n,t.Color.prototype={_tryParsingColor:function(e){var t=n.sourceFromHex(e);t||(t=n.sourceFromRgb(e)),t&&this.setSource(t)},getSource:function(){return this._source},setSource:function(e){this._source=e},toRgb:function(){var e=this.getSource();return"rgb("+e[0]+","+e[1]+","+e[2]+")"},toRgba:function(){var e=this.getSource();return"rgba("+e[0]+","+e[1]+","+e[2]+","+e[3]+")"},toHex:function(){var e=this.getSource(),t=e[0].toString(16);t=t.length===1?"0"+t:t;var n=e[1].toString(16);n=n.length===1?"0"+n:n;var r=e[2].toString(16);return r=r.length===1?"0"+r:r,t.toUpperCase()+n.toUpperCase()+r.toUpperCase()},getAlpha:function(){return this.getSource()[3]},setAlpha:function(e){var t=this.getSource();return t[3]=e,this.setSource(t),this},toGrayscale:function(){var e=this.getSource(),t=parseInt((e[0]*.3+e[1]*.59+e[2]*.11).toFixed(0),10),n=e[3];return this.setSource([t,t,t,n]),this},toBlackWhite:function(e){var t=this.getSource(),n=(t[0]*.3+t[1]*.59+t[2]*.11).toFixed(0),r=t[3];return e=e||127,n=Number(n)