diff --git a/examples/SVG Export/Gradients.html b/examples/SVG Export/Gradients.html new file mode 100644 index 00000000..0ac4b391 --- /dev/null +++ b/examples/SVG Export/Gradients.html @@ -0,0 +1,23 @@ + + + + + Gradients + + + + + + + + + \ No newline at end of file diff --git a/src/color/Gradient.js b/src/color/Gradient.js index f6e6eb62..6c9b5117 100644 --- a/src/color/Gradient.js +++ b/src/color/Gradient.js @@ -25,6 +25,8 @@ var Gradient = this.Gradient = Base.extend(/** @lends Gradient# */{ * @param {String} [type='linear'] 'linear' or 'radial' */ initialize: function(stops, type) { + // Define this Gradient's unique id. + this._id = ++Base._uid; this.setStops(stops || ['white', 'black']); this.type = type || 'linear'; }, diff --git a/src/svg/SvgExport.js b/src/svg/SvgExport.js index 1119245f..52909e49 100644 --- a/src/svg/SvgExport.js +++ b/src/svg/SvgExport.js @@ -374,19 +374,56 @@ new function() { return createElement('use', attrs); } - function exportColor(color) { - /* - var gradientSvg = getDefinition(symbol) - - - - - - */ - if (color.gradient) { - return null; + function exportGradient(color) { + // NOTE: As long as the fillTransform attribute is not implemented, + // we need to create a separate gradient object for each gradient, + // even when they share the same gradient defintion. + // http://www.svgopen.org/2011/papers/20-Separating_gradients_from_geometry/ + // TODO: Implement gradient mergin in SvgImport + var gradient = color.gradient, + gradientNode = getDefinition(color); + if (!gradientNode) { + var origin = color._origin, + destination = color._destination, + highlight = color._hilite, + attrs; + if (gradient.type == 'radial') { + attrs = { + cx: origin.x, + cy: origin.y, + r: origin.getDistance(destination) + }; + if (highlight) { + attrs.fx = highlight.x; + attrs.fy = highlight.y; + } + } else { + attrs = { + x1: origin.x, + y1: origin.y, + x2: destination.x, + y2: destination.y + }; + } + attrs.gradientUnits = 'userSpaceOnUse'; + gradientNode = createElement(gradient.type + 'Gradient', attrs); + var stops = gradient._stops; + for (var i = 0, l = stops.length; i < l; i++) { + var stop = stops[i], + color = stop._color, + attrs = { + offset: stop._rampPoint, + 'stop-color': color.toCss(true) + }; + // See applyStyle for an explanation of why there are separated + // opacity / color attributes. + if (color.getAlpha() < 1) + attrs['stop-opacity'] = color._alpha; + gradientNode.appendChild(createElement('stop', attrs)); + } + setDefinition(gradient, gradientNode, 'gradient'); } - return color.toCss(true); // false for noAlpha, see above + return 'url(#' + gradientNode.id + ')'; } var exporters = { @@ -417,11 +454,13 @@ new function() { // separate the alpha value of colors with alpha into the // separate fill- / stroke-opacity attribute: if (entry.type === 'color' && value != null && value.getAlpha() < 1) - attrs[entry.attribute + '-opacity'] = value.getAlpha(); + attrs[entry.attribute + '-opacity'] = value._alpha; attrs[entry.attribute] = value == null ? 'none' : entry.type === 'color' - ? exportColor(value) + ? value.gradient + ? exportGradient(value) + : value.toCss(true) // false for noAlpha, see above : entry.type === 'array' ? value.join(',') : entry.type === 'number'