Skip to content

Commit

Permalink
fix gradient toSVG for radial gradients (#3807)
Browse files Browse the repository at this point in the history
* support svg crossorigin on images
* fix gradient export
* added tests
  • Loading branch information
asturur authored Mar 26, 2017
1 parent e4a42e0 commit f877f52
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 17 deletions.
49 changes: 35 additions & 14 deletions src/gradient.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
for (var position in colorStops) {
var color = new fabric.Color(colorStops[position]);
this.colorStops.push({
offset: position,
offset: parseFloat(position),
color: color.toRgb(),
opacity: color.getAlpha()
});
Expand Down Expand Up @@ -170,16 +170,16 @@
*/
toSVG: function(object) {
var coords = fabric.util.object.clone(this.coords),
markup, commonAttributes;

markup, commonAttributes, colorStops = this.colorStops,
needsSwap = coords.r1 > coords.r2;
// colorStops must be sorted ascending
this.colorStops.sort(function(a, b) {
return a.offset - b.offset;
});

if (!(object.group && object.group.type === 'path-group')) {
for (var prop in coords) {
if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
if (prop === 'x1' || prop === 'x2') {
coords[prop] += this.offsetX - object.width / 2;
}
else if (prop === 'y1' || prop === 'y2') {
Expand All @@ -205,24 +205,45 @@
];
}
else if (this.type === 'radial') {
// svg radial gradient has just 1 radius. the biggest.
markup = [
'<radialGradient ',
commonAttributes,
' cx="', coords.x2,
'" cy="', coords.y2,
'" r="', coords.r2,
'" fx="', coords.x1,
'" fy="', coords.y1,
' cx="', needsSwap ? coords.x1 : coords.x2,
'" cy="', needsSwap ? coords.y1 : coords.y2,
'" r="', needsSwap ? coords.r1 : coords.r2,
'" fx="', needsSwap ? coords.x2 : coords.x1,
'" fy="', needsSwap ? coords.y2 : coords.y1,
'">\n'
];
}

for (var i = 0; i < this.colorStops.length; i++) {
if (this.type === 'radial') {
if (needsSwap) {
// svg goes from internal to external radius. if radius are inverted, swap color stops.
colorStops = colorStops.concat().reverse();
for (var i = 0; i < colorStops.length; i++) {
colorStops[i].offset = 1 - colorStops[i].offset;
}
}
var minRadius = Math.min(coords.r1, coords.r2);
if (minRadius > 0) {
// i have to shift all colorStops and add new one in 0.
var maxRadius = Math.max(coords.r1, coords.r2),
percentageShift = minRadius / maxRadius;
for (var i = 0; i < colorStops.length; i++) {
colorStops[i].offset += percentageShift * (1 - colorStops[i].offset);
}
}
}

for (var i = 0; i < colorStops.length; i++) {
var colorStop = colorStops[i];
markup.push(
'<stop ',
'offset="', (this.colorStops[i].offset * 100) + '%',
'" style="stop-color:', this.colorStops[i].color,
(this.colorStops[i].opacity !== null ? ';stop-opacity: ' + this.colorStops[i].opacity : ';'),
'offset="', (colorStop.offset * 100) + '%',
'" style="stop-color:', colorStop.color,
(colorStop.opacity !== null ? ';stop-opacity: ' + colorStop.opacity : ';'),
'"/>\n'
);
}
Expand Down Expand Up @@ -274,7 +295,7 @@
if (typeof opacity !== 'undefined') {
color = new fabric.Color(color).setAlpha(opacity).toRgba();
}
gradient.addColorStop(parseFloat(offset), color);
gradient.addColorStop(offset, color);
}

return gradient;
Expand Down
2 changes: 1 addition & 1 deletion src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@
var svgUid = fabric.Object.__uid++,
options = applyViewboxTransform(doc),
descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
options.crossOrigin = parsingOptions.crossOrigin;
options.crossOrigin = parsingOptions && parsingOptions.crossOrigin;
options.svgUid = svgUid;

if (descendants.length === 0 && fabric.isLikelyNode) {
Expand Down
70 changes: 68 additions & 2 deletions test/unit/gradient.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,47 @@
});
}

function createRadialGradientWithInternalRadius() {
return new fabric.Gradient({
type: 'radial',
coords: {
x1: 0,
y1: 10,
x2: 100,
y2: 200,
r1: 10,
r2: 50
},
colorStops: [
{ offset: 0, color: 'red' },
{ offset: 1, color: 'green', opacity: 0 }
]
});
}

function createRadialGradientSwapped() {
return new fabric.Gradient({
type: 'radial',
coords: {
x1: 0,
y1: 10,
x2: 100,
y2: 200,
r1: 50,
r2: 10
},
colorStops: [
{ offset: 0, color: 'red' },
{ offset: 1, color: 'green', opacity: 0 }
]
});
}

var SVG_LINEAR = '<linearGradient id="SVGID_0" gradientUnits="userSpaceOnUse" x1="-50" y1="-40" x2="50" y2="150">\n<stop offset="0%" style="stop-color:red;stop-opacity: 0"/>\n<stop offset="100%" style="stop-color:green;stop-opacity: undefined"/>\n</linearGradient>\n';
var SVG_RADIAL = '<radialGradient id="SVGID_0" gradientUnits="userSpaceOnUse" cx="50" cy="150" r="50" fx="-50" fy="-40">\n<stop offset="0%" style="stop-color:red;stop-opacity: undefined"/>\n<stop offset="100%" style="stop-color:green;stop-opacity: 0"/>\n</radialGradient>\n';
var SVG_INTERNALRADIUS = '<radialGradient id="SVGID_0" gradientUnits="userSpaceOnUse" cx="50" cy="150" r="50" fx="-50" fy="-40">\n<stop offset="20%" style="stop-color:red;stop-opacity: undefined"/>\n<stop offset="100%" style="stop-color:green;stop-opacity: 0"/>\n</radialGradient>\n';
var SVG_SWAPPED = '<radialGradient id="SVGID_0" gradientUnits="userSpaceOnUse" cx="-50" cy="-40" r="50" fx="50" fy="150">\n<stop offset="20%" style="stop-color:green;stop-opacity: 0"/>\n<stop offset="100%" style="stop-color:red;stop-opacity: undefined"/>\n</radialGradient>\n';

test('constructor linearGradient', function() {
ok(fabric.Gradient);

Expand Down Expand Up @@ -634,10 +675,35 @@

test('toSVG', function() {
var gradient = createLinearGradient();

ok(typeof gradient.toSVG == 'function');
});

test('toSVG linear', function() {
fabric.Object.__uid = 0;
var gradient = createLinearGradient();
var obj = new fabric.Object({ width: 100, height: 100 });
equal(gradient.toSVG(obj), SVG_LINEAR);
});

test('toSVG radial', function() {
fabric.Object.__uid = 0;
var gradient = createRadialGradient();
var obj = new fabric.Object({ width: 100, height: 100 });
equal(gradient.toSVG(obj), SVG_RADIAL);
});

test('toSVG radial with r1 > 0', function() {
fabric.Object.__uid = 0;
var gradient = createRadialGradientWithInternalRadius();
var obj = new fabric.Object({ width: 100, height: 100 });
equal(gradient.toSVG(obj), SVG_INTERNALRADIUS);
});

// TODO: test toSVG
test('toSVG radial with r1 > 0', function() {
fabric.Object.__uid = 0;
var gradient = createRadialGradientSwapped();
var obj = new fabric.Object({ width: 100, height: 100 });
equal(gradient.toSVG(obj), SVG_SWAPPED);
});

})();

0 comments on commit f877f52

Please sign in to comment.