diff --git a/src/gradient.class.js b/src/gradient.class.js index b5cd81e0..9fc69cc5 100644 --- a/src/gradient.class.js +++ b/src/gradient.class.js @@ -8,7 +8,7 @@ // convert percents to absolute values offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1); - + offset = offset < 0 ? 0 : offset > 1 ? 1 : offset; if (style) { var keyValuePairs = style.split(/\s*;\s*/); @@ -78,19 +78,20 @@ * @see {@link fabric.Gradient#initialize} for constructor definition */ fabric.Gradient = fabric.util.createClass(/** @lends fabric.Gradient.prototype */ { - /* - * Stores the original position of the gradient when we convert from % to fixed values, for objectBoundingBox case. - * @type Number - * @default 0 - */ - origX: 0, - /* - * Stores the original position of the gradient when we convert from % to fixed values, for objectBoundingBox case. + /** + * Horizontal offset for aligning gradients coming from SVG when outside pathgroups * @type Number * @default 0 */ - origY: 0, + offsetX : 0, + + /** + * Vertical offset for aligning gradients coming from SVG when outside pathgroups + * @type Number + * @default 0 + */ + offsetY : 0, /** * Constructor @@ -116,15 +117,13 @@ coords.r1 = options.coords.r1 || 0; coords.r2 = options.coords.r2 || 0; } - this.coords = coords; - this.gradientUnits = options.gradientUnits || 'objectBoundingBox'; this.colorStops = options.colorStops.slice(); if (options.gradientTransform) { this.gradientTransform = options.gradientTransform; } - this.origX = options.left || this.origX; - this.origY = options.top || this.origY; + this.offsetX = options.offsetX || this.offsetX; + this.offsetY = options.offsetY || this.offsetY; }, /** @@ -152,8 +151,9 @@ return { type: this.type, coords: this.coords, - gradientUnits: this.gradientUnits, - colorStops: this.colorStops + colorStops: this.colorStops, + offsetX: this.offsetX, + offsetY: this.offsetY }; }, @@ -164,7 +164,7 @@ * @param {Boolean} normalize Whether coords should be normalized * @return {String} SVG representation of an gradient (linear/radial) */ - toSVG: function(object, normalize) { + toSVG: function(object) { var coords = fabric.util.object.clone(this.coords), markup, commonAttributes; @@ -172,20 +172,22 @@ this.colorStops.sort(function(a, b) { return a.offset - b.offset; }); - - if (normalize && this.gradientUnits === 'userSpaceOnUse') { - coords.x1 += object.width / 2; - coords.y1 += object.height / 2; - coords.x2 += object.width / 2; - coords.y2 += object.height / 2; + + if (!(object.group && object.group.type === 'path-group')) { + for (var prop in coords) { + if (prop === 'x1' || prop === 'x2' || prop === 'r2') { + coords[prop] += this.offsetX - object.width / 2; + } + else if (prop === 'y1' || prop === 'y2') { + coords[prop] += this.offsetY - object.height / 2; + } + } } - else if (this.gradientUnits === 'objectBoundingBox') { - _convertValuesToPercentUnits(object, coords); - } - commonAttributes = 'id="SVGID_' + this.id + - '" gradientUnits="' + this.gradientUnits + '"'; + + commonAttributes = 'id="SVGID_' + this.id + + '" gradientUnits="userSpaceOnUse"'; if (this.gradientTransform) { - commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" '; + commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" '; } if (this.type === 'linear') { markup = [ @@ -322,7 +324,7 @@ gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox', gradientTransform = el.getAttribute('gradientTransform'), colorStops = [], - coords = { }; + coords = { }, ellipseMatrix; if (type === 'linear') { coords = getLinearCoords(el); @@ -335,19 +337,19 @@ colorStops.push(getColorStop(colorStopEls[i])); } - _convertPercentUnitsToValues(instance, coords); + ellipseMatrix = _convertPercentUnitsToValues(instance, coords, gradientUnits); var gradient = new fabric.Gradient({ type: type, coords: coords, - gradientUnits: gradientUnits, - colorStops: colorStops + colorStops: colorStops, + offsetX: -instance.left, + offsetY: -instance.top }); - if (gradientTransform) { - gradient.gradientTransform = fabric.parseTransformAttribute(gradientTransform); + if (gradientTransform || ellipseMatrix !== '') { + gradient.gradientTransform = fabric.parseTransformAttribute((gradientTransform || '') + ellipseMatrix); } - return gradient; }, /* _FROM_SVG_END_ */ @@ -361,7 +363,7 @@ */ forObject: function(obj, options) { options || (options = { }); - _convertPercentUnitsToValues(obj, options); + _convertPercentUnitsToValues(obj, options.coords, 'userSpaceOnUse'); return new fabric.Gradient(options); } }); @@ -369,35 +371,35 @@ /** * @private */ - function _convertPercentUnitsToValues(object, options) { + function _convertPercentUnitsToValues(object, options, gradientUnits) { + var propValue, addFactor = 0, multFactor = 1, ellipseMatrix = ''; for (var prop in options) { + propValue = parseFloat(options[prop], 10); if (typeof options[prop] === 'string' && /^\d+%$/.test(options[prop])) { - var percents = parseFloat(options[prop], 10); - if (prop === 'x1' || prop === 'x2' || prop === 'r2') { - options[prop] = fabric.util.toFixed(object.width * percents / 100, 2) + object.left; - } - else if (prop === 'y1' || prop === 'y2') { - options[prop] = fabric.util.toFixed(object.height * percents / 100, 2) + object.top; - } + multFactor = 0.01; + } else { + multFactor = 1; } - } - } - - /* _TO_SVG_START_ */ - /** - * @private - */ - function _convertValuesToPercentUnits(object, options) { - for (var prop in options) { - //convert to percent units if (prop === 'x1' || prop === 'x2' || prop === 'r2') { - options[prop] = fabric.util.toFixed((options[prop] - object.fill.origX) / object.width * 100, 2) + '%'; + multFactor *= gradientUnits === 'objectBoundingBox' ? object.width : 1; + addFactor = gradientUnits === 'objectBoundingBox' ? object.left || 0 : 0; } else if (prop === 'y1' || prop === 'y2') { - options[prop] = fabric.util.toFixed((options[prop] - object.fill.origY) / object.height * 100, 2) + '%'; + multFactor *= gradientUnits === 'objectBoundingBox' ? object.height : 1; + addFactor = gradientUnits === 'objectBoundingBox' ? object.top || 0 : 0; + } + options[prop] = propValue * multFactor + addFactor; + } + if (object.type === 'ellipse' && options['r2'] !== null && gradientUnits === 'objectBoundingBox' && object.rx !== object.ry) { + var scaleFactor = object.ry/object.rx; + ellipseMatrix = ' scale(1, ' + scaleFactor + ')'; + if (options['y1']) { + options['y1'] /= scaleFactor; + } + if (options['y2']) { + options['y2'] /= scaleFactor; } } + return ellipseMatrix; } - /* _TO_SVG_END_ */ - })();