i){r=!0;var d=a-f,g=a,p=Math.abs(d-i),v=Math.abs(g-i);h=vthis.text.length&&this.setSelectionEnd(this.text.length)},getUpCursorOffset:function(t,e){var i=e?this.selectionEnd:this.selectionStart,r=this.get2DCursorLocation(i),n=r.lineIndex;if(0===n||t.metaKey||33===t.keyCode)return i;for(var s,o=this._textLines[n].slice(0,r.charIndex),a=this._textLines[n-1]||"",h=this._getLineWidth(this.ctx,r.lineIndex),c=this._getLineLeftOffset(h),l=c,u=0,f=o.length;ui){r=!0;var d=a-f,g=a,p=Math.abs(d-i),v=Math.abs(g-i);h=v=this.text.length&&this.selectionEnd>=this.text.length||(this.abortCursorAnimation(),this._currentCursorOpacity=1,t.shiftKey?this.moveCursorRightWithShift(t):this.moveCursorRightWithoutShift(t),this.initDelayedCursor())},moveCursorRightWithShift:function(t){"left"===this._selectionDirection&&this.selectionStart!==this.selectionEnd?this._moveRight(t,"selectionStart"):(this._selectionDirection="right",this._moveRight(t,"selectionEnd"))},moveCursorRightWithoutShift:function(t){this._selectionDirection="right",this.selectionStart===this.selectionEnd?(this._moveRight(t,"selectionStart"),this.setSelectionEnd(this.selectionStart)):(this.setSelectionEnd(this.selectionEnd+this.getNumNewLinesInSelectedText()),this.setSelectionStart(this.selectionEnd))},removeChars:function(t){this.selectionStart===this.selectionEnd?this._removeCharsNearCursor(t):this._removeCharsFromTo(this.selectionStart,this.selectionEnd),this.setSelectionEnd(this.selectionStart),this._removeExtraneousStyles(),this.canvas&&this.canvas.renderAll(),this.setCoords(),this.fire("changed"),this.canvas&&this.canvas.fire("text:changed",{target:this})},_removeCharsNearCursor:function(t){if(0!==this.selectionStart)if(t.metaKey){var e=this.findLineBoundaryLeft(this.selectionStart);this._removeCharsFromTo(e,this.selectionStart),this.setSelectionStart(e)}else if(t.altKey){var i=this.findWordBoundaryLeft(this.selectionStart);this._removeCharsFromTo(i,this.selectionStart),this.setSelectionStart(i)}else this._removeSingleCharAndStyle(this.selectionStart),this.setSelectionStart(this.selectionStart-1)}}),function(){var t=fabric.util.toFixed,e=fabric.Object.NUM_FRACTION_DIGITS;fabric.util.object.extend(fabric.IText.prototype,{_setSVGTextLineText:function(t,e,i,r,n,s){this._getLineStyle(t)?this._setSVGTextLineChars(t,e,i,r,s):fabric.Text.prototype._setSVGTextLineText.call(this,t,e,i,r,n)},_setSVGTextLineChars:function(t,e,i,r,n){for(var s=this._textLines[t],o=0,a=this._getLineLeftOffset(this._getLineWidth(this.ctx,t))-this.width/2,h=this._getSVGLineTopOffset(t),c=this._getHeightOfLine(this.ctx,t),l=0,u=s.length;l\n'].join("")},_createTextCharSpan:function(i,r,n,s,o){var a=this.getSvgStyles.call(fabric.util.object.extend({visible:!0,fill:this.fill,stroke:this.stroke,type:"text",getSvgFilter:fabric.Object.prototype.getSvgFilter},r));return['\t\t\t',fabric.util.string.escapeXml(i),"\n"].join("")}})}(),function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.object.clone;e.Textbox=e.util.createClass(e.IText,e.Observable,{type:"textbox",minWidth:20,dynamicMinWidth:0,__cachedLines:null,lockScalingY:!0,lockScalingFlip:!0,initialize:function(t,i){this.ctx=e.util.createCanvasElement().getContext("2d"),this.callSuper("initialize",t,i),this.setControlsVisibility(e.Textbox.getTextboxControlVisibility()),this._dimensionAffectingProps.width=!0},_initDimensions:function(t){this.__skipDimension||(t||(t=e.util.createCanvasElement().getContext("2d"),this._setTextStyles(t)),this.dynamicMinWidth=0,this._textLines=this._splitTextIntoLines(),this.dynamicMinWidth>this.width&&this._set("width",this.dynamicMinWidth),this._clearCache(),this.height=this._getTextHeight(t))},_generateStyleMap:function(){for(var t=0,e=0,i=0,r={},n=0;n=this.width&&!d&&(n.push(s),s="",r=l,d=!0),d||(s+=c),s+=a,u=this._measureText(t,c,i,h),h++,d=!1,l>f&&(f=l);return g&&n.push(s),f>this.dynamicMinWidth&&(this.dynamicMinWidth=f),n},_splitTextIntoLines:function(){var t=this.textAlign;this.ctx.save(),this._setTextStyles(this.ctx),this.textAlign="left";var e=this._wrapText(this.ctx,this.text);return this.textAlign=t,this.ctx.restore(),this._textLines=e,this._styleMap=this._generateStyleMap(),e},setOnGroup:function(t,e){"scaleX"===t&&(this.set("scaleX",Math.abs(1/e)),this.set("width",this.get("width")*e/("undefined"==typeof this.__oldScaleX?1:this.__oldScaleX)),this.__oldScaleX=e)},get2DCursorLocation:function(t){"undefined"==typeof t&&(t=this.selectionStart);for(var e=this._textLines.length,i=0,r=0;r=h.getMinWidth()?(h.set("width",c),!0):void 0},fabric.Group.prototype._refreshControlsVisibility=function(){if("undefined"!=typeof fabric.Textbox)for(var t=this._objects.length;t--;)if(this._objects[t]instanceof fabric.Textbox)return void this.setControlsVisibility(fabric.Textbox.getTextboxControlVisibility())};var e=fabric.util.object.clone;fabric.util.object.extend(fabric.Textbox.prototype,{_removeExtraneousStyles:function(){for(var t in this._styleMap)this._textLines[t]||delete this.styles[this._styleMap[t].line]},insertCharStyleObject:function(t,e,i){var r=this._styleMap[t];t=r.line,e=r.offset+e,fabric.IText.prototype.insertCharStyleObject.apply(this,[t,e,i])},insertNewlineStyleObject:function(t,e,i){var r=this._styleMap[t];t=r.line,e=r.offset+e,fabric.IText.prototype.insertNewlineStyleObject.apply(this,[t,e,i])},shiftLineStyles:function(t,i){var r=e(this.styles),n=this._styleMap[t];t=n.line;for(var s in this.styles){var o=parseInt(s,10);o>t&&(this.styles[o+i]=r[o],r[o-i]||delete this.styles[o])}},_getTextOnPreviousLine:function(t){for(var e=this._textLines[t-1];this._styleMap[t-2]&&this._styleMap[t-2].line===this._styleMap[t-1].line;)e=this._textLines[t-2]+e,t--;return e},removeStyleObject:function(t,e){var i=this.get2DCursorLocation(e),r=this._styleMap[i.lineIndex],n=r.line,s=r.offset+i.charIndex;this._removeStyleObject(t,i,n,s)}})}(),function(){var t=fabric.IText.prototype._getNewSelectionStartFromOffset;fabric.IText.prototype._getNewSelectionStartFromOffset=function(e,i,r,n,s){n=t.call(this,e,i,r,n,s);for(var o=0,a=0,h=0;h=n));h++)"\n"!==this.text[o+a]&&" "!==this.text[o+a]||a++;return n-h+a}}(),function(){function request(t,e,i){var r=URL.parse(t);r.port||(r.port=0===r.protocol.indexOf("https:")?443:80);var n=0===r.protocol.indexOf("https:")?HTTPS:HTTP,s=n.request({hostname:r.hostname,port:r.port,path:r.path,method:"GET"},function(t){var r="";e&&t.setEncoding(e),t.on("end",function(){i(r)}),t.on("data",function(e){200===t.statusCode&&(r+=e)})});s.on("error",function(t){t.errno===process.ECONNREFUSED?fabric.log("ECONNREFUSED: connection refused to "+r.hostname+":"+r.port):fabric.log(t.message),i(null)}),s.end()}function requestFs(t,e){var i=require("fs");i.readFile(t,function(t,i){if(t)throw fabric.log(t),t;e(i)})}if("undefined"==typeof document||"undefined"==typeof window){var DOMParser=require("xmldom").DOMParser,URL=require("url"),HTTP=require("http"),HTTPS=require("https"),Canvas=require("canvas"),Image=require("canvas").Image;fabric.util.loadImage=function(t,e,i){function r(r){r?(n.src=new Buffer(r,"binary"),n._src=t,e&&e.call(i,n)):(n=null,e&&e.call(i,null,!0))}var n=new Image;t&&(t instanceof Buffer||0===t.indexOf("data"))?(n.src=n._src=t,e&&e.call(i,n)):t&&0!==t.indexOf("http")?requestFs(t,r):t?request(t,"binary",r):e&&e.call(i,t)},fabric.loadSVGFromURL=function(t,e,i){t=t.replace(/^\n\s*/,"").replace(/\?.*$/,"").trim(),0!==t.indexOf("http")?requestFs(t,function(t){fabric.loadSVGFromString(t.toString(),e,i)}):request(t,"",function(t){fabric.loadSVGFromString(t,e,i)})},fabric.loadSVGFromString=function(t,e,i){var r=(new DOMParser).parseFromString(t);fabric.parseSVGDocument(r.documentElement,function(t,i){e&&e(t,i)},i)},fabric.util.getScript=function(url,callback){request(url,"",function(body){eval(body),callback&&callback()})},fabric.Image.fromObject=function(t,e){fabric.util.loadImage(t.src,function(i){var r=new fabric.Image(i);r._initConfig(t),r._initFilters(t.filters,function(i){r.filters=i||[],r._initFilters(t.resizeFilters,function(t){r.resizeFilters=t||[],e&&e(r)})})})},fabric.createCanvasForNode=function(t,e,i,r){r=r||i;var n=fabric.document.createElement("canvas"),s=new Canvas(t||600,e||600,r),o=new Canvas(t||600,e||600,r);n.style={},n.width=s.width,n.height=s.height;var a=fabric.Canvas||fabric.StaticCanvas,h=new a(n,i);return h.contextContainer=s.getContext("2d"),h.nodeCanvas=s,h.contextCache=o.getContext("2d"),h.nodeCacheCanvas=o,h.Font=Canvas.Font,h},fabric.StaticCanvas.prototype.createPNGStream=function(){return this.nodeCanvas.createPNGStream()},fabric.StaticCanvas.prototype.createJPEGStream=function(t){return this.nodeCanvas.createJPEGStream(t)};var origSetWidth=fabric.StaticCanvas.prototype.setWidth;fabric.StaticCanvas.prototype.setWidth=function(t,e){return origSetWidth.call(this,t,e),this.nodeCanvas.width=t,this},fabric.Canvas&&(fabric.Canvas.prototype.setWidth=fabric.StaticCanvas.prototype.setWidth);var origSetHeight=fabric.StaticCanvas.prototype.setHeight;fabric.StaticCanvas.prototype.setHeight=function(t,e){return origSetHeight.call(this,t,e),this.nodeCanvas.height=t,this},fabric.Canvas&&(fabric.Canvas.prototype.setHeight=fabric.StaticCanvas.prototype.setHeight)}}();
\ No newline at end of file
diff --git a/dist/fabric.min.js.gz b/dist/fabric.min.js.gz
index 26ffa812..dc6bebd9 100644
Binary files a/dist/fabric.min.js.gz and b/dist/fabric.min.js.gz differ
diff --git a/dist/fabric.require.js b/dist/fabric.require.js
index bc3f1505..00c9c8b2 100644
--- a/dist/fabric.require.js
+++ b/dist/fabric.require.js
@@ -5870,6 +5870,18 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
get: function(property) {
return this[property];
},
+ getObjectScaling: function() {
+ var scaleX = this.scaleX, scaleY = this.scaleY;
+ if (this.group) {
+ var scaling = this.group.getObjectScaling();
+ scaleX *= scaling.scaleX;
+ scaleY *= scaling.scaleY;
+ }
+ return {
+ scaleX: scaleX,
+ scaleY: scaleY
+ };
+ },
_setObject: function(obj) {
for (var prop in obj) {
this._set(prop, obj[prop]);
@@ -6005,15 +6017,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
if (!this.shadow) {
return;
}
- var multX = this.canvas && this.canvas.viewportTransform[0] || 1, multY = this.canvas && this.canvas.viewportTransform[3] || 1;
+ var multX = this.canvas && this.canvas.viewportTransform[0] || 1, multY = this.canvas && this.canvas.viewportTransform[3] || 1, scaling = this.getObjectScaling();
if (this.canvas && this.canvas._isRetinaScaling()) {
multX *= fabric.devicePixelRatio;
multY *= fabric.devicePixelRatio;
}
ctx.shadowColor = this.shadow.color;
- ctx.shadowBlur = this.shadow.blur * (multX + multY) * (this.scaleX + this.scaleY) / 4;
- ctx.shadowOffsetX = this.shadow.offsetX * multX * this.scaleX;
- ctx.shadowOffsetY = this.shadow.offsetY * multY * this.scaleY;
+ ctx.shadowBlur = this.shadow.blur * (multX + multY) * (scaling.scaleX + scaling.scaleY) / 4;
+ ctx.shadowOffsetX = this.shadow.offsetX * multX * scaling.scaleX;
+ ctx.shadowOffsetY = this.shadow.offsetY * multY * scaling.scaleY;
},
_removeShadow: function(ctx) {
if (!this.shadow) {
@@ -6225,7 +6237,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
};
fabric.util.object.extend(fabric.Object.prototype, {
translateToGivenOrigin: function(point, fromOriginX, fromOriginY, toOriginX, toOriginY) {
- var x = point.x, y = point.y, offsetX, offsetY;
+ var x = point.x, y = point.y, offsetX, offsetY, dim;
if (typeof fromOriginX === "string") {
fromOriginX = originXOffset[fromOriginX];
} else {
diff --git a/src/shapes/object.class.js b/src/shapes/object.class.js
index 0c2d4f9e..9100ba6f 100644
--- a/src/shapes/object.class.js
+++ b/src/shapes/object.class.js
@@ -951,6 +951,20 @@
return this[property];
},
+ /**
+ * Return the object scale factor counting also the group scaling
+ * @return {Object} object with scaleX and scaleY properties
+ */
+ getObjectScaling: function() {
+ var scaleX = this.scaleX, scaleY = this.scaleY;
+ if (this.group) {
+ var scaling = this.group.getObjectScaling();
+ scaleX *= scaling.scaleX;
+ scaleY *= scaling.scaleY;
+ }
+ return { scaleX: scaleX, scaleY: scaleY };
+ },
+
/**
* @private
*/
@@ -1194,15 +1208,16 @@
}
var multX = (this.canvas && this.canvas.viewportTransform[0]) || 1,
- multY = (this.canvas && this.canvas.viewportTransform[3]) || 1;
+ multY = (this.canvas && this.canvas.viewportTransform[3]) || 1,
+ scaling = this.getObjectScaling();
if (this.canvas && this.canvas._isRetinaScaling()) {
multX *= fabric.devicePixelRatio;
multY *= fabric.devicePixelRatio;
}
ctx.shadowColor = this.shadow.color;
- ctx.shadowBlur = this.shadow.blur * (multX + multY) * (this.scaleX + this.scaleY) / 4;
- ctx.shadowOffsetX = this.shadow.offsetX * multX * this.scaleX;
- ctx.shadowOffsetY = this.shadow.offsetY * multY * this.scaleY;
+ ctx.shadowBlur = this.shadow.blur * (multX + multY) * (scaling.scaleX + scaling.scaleY) / 4;
+ ctx.shadowOffsetX = this.shadow.offsetX * multX * scaling.scaleX;
+ ctx.shadowOffsetY = this.shadow.offsetY * multY * scaling.scaleY;
},
/**
diff --git a/test/unit/object.js b/test/unit/object.js
index 270a6c97..8f09e4a5 100644
--- a/test/unit/object.js
+++ b/test/unit/object.js
@@ -1427,4 +1427,55 @@ test('toDataURL & reference to canvas', function() {
equal(typeof deserializedObject.clipTo, 'function');
});
+ test('getObjectScale', function() {
+ var object = new fabric.Object({ scaleX: 3, scaleY : 2});
+ var objectScale = object.getObjectScaling();
+ deepEqual(objectScale, {scaleX: object.scaleX, scaleY: object.scaleY});
+ });
+
+ test('getObjectScale in group', function() {
+ var object = new fabric.Object({ scaleX: 3, scaleY : 2});
+ var group = new fabric.Group();
+ group.scaleX = 2;
+ group.scaleY = 2;
+ object.group = group;
+ var objectScale = object.getObjectScaling();
+ console.log(objectScale);
+ deepEqual(objectScale, {
+ scaleX: object.scaleX * group.scaleX,
+ scaleY: object.scaleY * group.scaleY
+ });
+ });
+
+ test('_setShadow', function(){
+ var el = fabric.document.createElement('canvas');
+ el.width = 600; el.height = 600;
+ var canvas = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.StaticCanvas(el);
+ var context = canvas.contextContainer;
+ var object = new fabric.Object({ scaleX: 1, scaleY : 1});
+ var group = new fabric.Group();
+ group.scaleX = 2;
+ group.scaleY = 2;
+ object.setShadow({
+ color: 'red',
+ blur: 10,
+ offsetX: 5,
+ offsetY: 15
+ });
+ object._setShadow(context);
+ equal(context.shadowOffsetX, object.shadow.offsetX);
+ equal(context.shadowOffsetY, object.shadow.offsetY);
+ equal(context.shadowBlur, object.shadow.blur);
+ object.scaleX = 2;
+ object.scaleY = 3;
+ object._setShadow(context);
+ equal(context.shadowOffsetX, object.shadow.offsetX * object.scaleX);
+ equal(context.shadowOffsetY, object.shadow.offsetY * object.scaleY);
+ equal(context.shadowBlur, object.shadow.blur * (object.scaleX + object.scaleY) / 2);
+ object.group = group;
+ object._setShadow(context);
+ equal(context.shadowOffsetX, object.shadow.offsetX * object.scaleX * group.scaleX);
+ equal(context.shadowOffsetY, object.shadow.offsetY * object.scaleY * group.scaleY);
+ equal(context.shadowBlur, object.shadow.blur * (object.scaleX * group.scaleX + object.scaleY * group.scaleY) / 2);
+ });
})();