Skip to content

Commit

Permalink
Implement fill-opacity on Path objects with gradients. (fabricjs#5812)
Browse files Browse the repository at this point in the history
* Implement fill-opacity on Path objects with gradients.

* Reorder fabric.Gradient.fromElement arguments.
  • Loading branch information
ajvincent authored and asturur committed Jul 21, 2019
1 parent f9a3013 commit f166c7f
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 25 deletions.
10 changes: 6 additions & 4 deletions src/elements_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
var _this = this;
return function(obj) {
var _options;
_this.resolveGradient(obj, 'fill');
_this.resolveGradient(obj, 'stroke');
_this.resolveGradient(obj, el, 'fill');
_this.resolveGradient(obj, el, 'stroke');
if (obj instanceof fabric.Image && obj._originalElement) {
_options = obj.parsePreserveAspectRatioAttribute(el);
}
Expand All @@ -69,10 +69,12 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
return fabric[storage][this.svgUid][id];
};

proto.resolveGradient = function(obj, property) {
proto.resolveGradient = function(obj, el, property) {
var gradientDef = this.extractPropertyDefinition(obj, property, 'gradientDefs');
if (gradientDef) {
obj.set(property, fabric.Gradient.fromElement(gradientDef, obj));
var opacityAttr = el.getAttribute(property + '-opacity');
var gradient = fabric.Gradient.fromElement(gradientDef, obj, opacityAttr);
obj.set(property, gradient);
}
};

Expand Down
15 changes: 11 additions & 4 deletions src/gradient.class.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(function() {

/* _FROM_SVG_START_ */
function getColorStop(el) {
function getColorStop(el, multiplier) {
var style = el.getAttribute('style'),
offset = el.getAttribute('offset') || 0,
color, colorAlpha, opacity, i;
Expand Down Expand Up @@ -41,7 +41,7 @@
color = new fabric.Color(color);
colorAlpha = color.getAlpha();
opacity = isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity);
opacity *= colorAlpha;
opacity *= colorAlpha * multiplier;

return {
offset: offset,
Expand Down Expand Up @@ -302,11 +302,12 @@
* @memberOf fabric.Gradient
* @param {SVGGradientElement} el SVG gradient element
* @param {fabric.Object} instance
* @param {String} opacityAttr A fill-opacity or stroke-opacity attribute to multiply to each stop's opacity.
* @return {fabric.Gradient} Gradient instance
* @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement
* @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement
*/
fromElement: function(el, instance) {
fromElement: function(el, instance, opacityAttr) {
/**
* @example:
*
Expand Down Expand Up @@ -340,6 +341,12 @@
*
*/

var multiplier = parseFloat(opacityAttr) / (/%$/.test(opacityAttr) ? 100 : 1);
multiplier = multiplier < 0 ? 0 : multiplier > 1 ? 1 : multiplier;
if (isNaN(multiplier)) {
multiplier = 1;
}

var colorStopEls = el.getElementsByTagName('stop'),
type,
gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
Expand All @@ -362,7 +369,7 @@
}

for (i = colorStopEls.length; i--; ) {
colorStops.push(getColorStop(colorStopEls[i]));
colorStops.push(getColorStop(colorStopEls[i], multiplier));
}

ellipseMatrix = _convertPercentUnitsToValues(instance, coords, gradientUnits);
Expand Down
34 changes: 17 additions & 17 deletions test/unit/gradient.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@
element.appendChild(stop2);

var object = new fabric.Object({ width: 100, height: 100 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');

assert.ok(gradient instanceof fabric.Gradient);

Expand Down Expand Up @@ -250,7 +250,7 @@
element.appendChild(stop2);

var object = new fabric.Object({ width: 200, height: 200 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');

assert.ok(gradient instanceof fabric.Gradient);

Expand Down Expand Up @@ -283,7 +283,7 @@
element.appendChild(stop2);

var object = new fabric.Object({ width: 200, height: 200 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');

assert.ok(gradient instanceof fabric.Gradient);

Expand Down Expand Up @@ -315,7 +315,7 @@
element.appendChild(stop2);

var object = new fabric.Object({ width: 100, height: 100, top: 0, left: 0 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');

assert.ok(gradient instanceof fabric.Gradient);

Expand Down Expand Up @@ -350,7 +350,7 @@
element.appendChild(stop2);

var object = new fabric.Object({ width: 100, height: 100 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');

assert.ok(gradient instanceof fabric.Gradient);

Expand All @@ -369,13 +369,13 @@
element.setAttribute('y2', 'Infinity');

var object = new fabric.Object({ width: 200, height: 200 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');
assert.equal(gradient.coords.x1, 60);
assert.equal(gradient.coords.y1, 20);
assert.equal(gradient.coords.x2, 40);
assert.equal(gradient.coords.y2, 200);
object = new fabric.Object({ width: 200, height: 200, top: 50, left: 10 });
gradient = fabric.Gradient.fromElement(element, object);
gradient = fabric.Gradient.fromElement(element, object, '');
assert.equal(gradient.coords.x1, 70);
assert.equal(gradient.coords.y1, 70);
assert.equal(gradient.coords.x2, 50);
Expand All @@ -394,7 +394,7 @@
element.setAttribute('r', '100%');

var object = new fabric.Object({ width: 200, height: 200 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');
assert.equal(gradient.coords.x1, 60, 'should change with width height');
assert.equal(gradient.coords.y1, 40, 'should change with width height');
assert.equal(gradient.coords.x2, 20, 'should change with width height');
Expand All @@ -403,7 +403,7 @@
assert.equal(gradient.coords.r2, 200, 'should change with width height');

object = new fabric.Object({ width: 200, height: 200, top: 10, left: 10 });
gradient = fabric.Gradient.fromElement(element, object);
gradient = fabric.Gradient.fromElement(element, object, '');
assert.equal(gradient.coords.x1, 70, 'should change with top left');
assert.equal(gradient.coords.y1, 50, 'should change with top left');
assert.equal(gradient.coords.x2, 30, 'should change with top left');
Expand All @@ -425,7 +425,7 @@
element.setAttribute('gradientUnits', 'userSpaceOnUse');

var object = new fabric.Object({ width: 200, height: 200 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');
assert.equal(gradient.coords.x1, 30, 'should not change with width height');
assert.equal(gradient.coords.y1, 20, 'should not change with width height');
assert.equal(gradient.coords.x2, 15, 'should not change with width height');
Expand All @@ -434,7 +434,7 @@
assert.equal(gradient.coords.r2, 100, 'should not change with width height');

object = new fabric.Object({ width: 200, height: 200, top: 50, left: 60 });
gradient = fabric.Gradient.fromElement(element, object);
gradient = fabric.Gradient.fromElement(element, object, '');
assert.equal(gradient.coords.x1, 30, 'should not change with top left');
assert.equal(gradient.coords.y1, 20, 'should not change with top left');
assert.equal(gradient.coords.x2, 15, 'should not change with top left');
Expand All @@ -455,14 +455,14 @@
element.setAttribute('gradientUnits', 'userSpaceOnUse');

var object = new fabric.Object({ width: 200, height: 200 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');
assert.equal(gradient.coords.x1, 30, 'should not change with width height');
assert.equal(gradient.coords.y1, 20, 'should not change with width height');
assert.equal(gradient.coords.x2, 15, 'should not change with width height');
assert.equal(gradient.coords.y2, 18, 'should not change with width height');

object = new fabric.Object({ width: 200, height: 200, top: 40, left: 40 });
gradient = fabric.Gradient.fromElement(element, object);
gradient = fabric.Gradient.fromElement(element, object, '');
assert.equal(gradient.coords.x1, 30, 'should not change with top left');
assert.equal(gradient.coords.y1, 20, 'should not change with top left');
assert.equal(gradient.coords.x2, 15, 'should not change with top left');
Expand All @@ -486,7 +486,7 @@
element.appendChild(stop2);

var object = new fabric.Object({ width: 100, height: 100 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');

assert.ok(gradient instanceof fabric.Gradient);

Expand Down Expand Up @@ -519,7 +519,7 @@
element.appendChild(stop2);
element.setAttribute('gradientTransform', 'matrix(3.321 -0.6998 0.4077 1.9347 -440.9168 -408.0598)');
var object = new fabric.Object({ width: 100, height: 100 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');

assert.ok(gradient instanceof fabric.Gradient);

Expand Down Expand Up @@ -568,7 +568,7 @@
element.appendChild(stop4);

var object = new fabric.Object({ width: 100, height: 100 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');

assert.ok(gradient instanceof fabric.Gradient);

Expand Down Expand Up @@ -625,7 +625,7 @@
element.appendChild(stop4);

var object = new fabric.Object({ width: 100, height: 100 });
var gradient = fabric.Gradient.fromElement(element, object);
var gradient = fabric.Gradient.fromElement(element, object, '');

assert.ok(gradient instanceof fabric.Gradient);

Expand Down
22 changes: 22 additions & 0 deletions test/unit/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,28 @@
});
});

QUnit.test('parseSVGFromString path fill-opacity with gradient', function(assert) {
var done = assert.async();
var string = '<?xml version="1.0" encoding="UTF-8"?>' +
'<svg version="1.2" baseProfile="tiny" xml:id="svg-root" width="300" height="400" ' +
'viewBox="0 0 300 400" xmlns="http://www.w3.org/2000/svg" ' +
'xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xe="http://www.w3.org/2001/xml-events">' +
'<linearGradient id="red-to-red">' +
'<stop offset="0%" stop-color="#00ff00" stop-opacity="0.5"/>' +
'<stop offset="100%" stop-color="#ff0000"/>' +
'</linearGradient>' +
'<path d="M 0 0 l 100 0 l 0 100 l -100 0 z" fill="url(#red-to-red)" fill-opacity="0.5"/>' +
'</svg>';

fabric.loadSVGFromString(string, function(objects) {
assert.equal(objects[0].fill.colorStops[0].opacity, 0.5);
assert.equal(objects[0].fill.colorStops[0].color, 'rgb(255,0,0)');
assert.equal(objects[0].fill.colorStops[1].opacity, 0.25);
assert.equal(objects[0].fill.colorStops[1].color, 'rgb(0,255,0)');
done();
});
});

QUnit.test('parseSVGFromString with svg:namespace', function(assert) {
var done = assert.async();
var string = '<?xml version="1.0" standalone="no"?><svg width="100%" height="100%" version="1.1" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">' +
Expand Down

0 comments on commit f166c7f

Please sign in to comment.