mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-04-01 05:20:22 +00:00
380 lines
11 KiB
JavaScript
380 lines
11 KiB
JavaScript
(function() {
|
|
|
|
/* _FROM_SVG_START_ */
|
|
function getColorStop(el) {
|
|
var style = el.getAttribute('style'),
|
|
offset = el.getAttribute('offset'),
|
|
color, opacity;
|
|
|
|
// convert percents to absolute values
|
|
offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1);
|
|
|
|
if (style) {
|
|
var keyValuePairs = style.split(/\s*;\s*/);
|
|
|
|
if (keyValuePairs[keyValuePairs.length - 1] === '') {
|
|
keyValuePairs.pop();
|
|
}
|
|
|
|
for (var i = keyValuePairs.length; i--; ) {
|
|
|
|
var split = keyValuePairs[i].split(/\s*:\s*/),
|
|
key = split[0].trim(),
|
|
value = split[1].trim();
|
|
|
|
if (key === 'stop-color') {
|
|
color = value;
|
|
}
|
|
else if (key === 'stop-opacity') {
|
|
opacity = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!color) {
|
|
color = el.getAttribute('stop-color') || 'rgb(0,0,0)';
|
|
}
|
|
if (!opacity) {
|
|
opacity = el.getAttribute('stop-opacity');
|
|
}
|
|
|
|
// convert rgba color to rgb color - alpha value has no affect in svg
|
|
color = new fabric.Color(color).toRgb();
|
|
|
|
return {
|
|
offset: offset,
|
|
color: color,
|
|
opacity: isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity)
|
|
};
|
|
}
|
|
|
|
function getLinearCoords(el) {
|
|
return {
|
|
x1: el.getAttribute('x1') || 0,
|
|
y1: el.getAttribute('y1') || 0,
|
|
x2: el.getAttribute('x2') || '100%',
|
|
y2: el.getAttribute('y2') || 0
|
|
};
|
|
}
|
|
|
|
function getRadialCoords(el) {
|
|
return {
|
|
x1: el.getAttribute('fx') || el.getAttribute('cx') || '50%',
|
|
y1: el.getAttribute('fy') || el.getAttribute('cy') || '50%',
|
|
r1: 0,
|
|
x2: el.getAttribute('cx') || '50%',
|
|
y2: el.getAttribute('cy') || '50%',
|
|
r2: el.getAttribute('r') || '50%'
|
|
};
|
|
}
|
|
/* _FROM_SVG_END_ */
|
|
|
|
/**
|
|
* Gradient class
|
|
* @class fabric.Gradient
|
|
* @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#gradients}
|
|
* @see {@link fabric.Gradient#initialize} for constructor definition
|
|
*/
|
|
fabric.Gradient = fabric.util.createClass(/** @lends fabric.Gradient.prototype */ {
|
|
|
|
/**
|
|
* Constructor
|
|
* @param {Object} [options] Options object with type, coords, gradientUnits and colorStops
|
|
* @return {fabric.Gradient} thisArg
|
|
*/
|
|
initialize: function(options) {
|
|
options || (options = { });
|
|
|
|
var coords = { };
|
|
|
|
this.id = fabric.Object.__uid++;
|
|
this.type = options.type || 'linear';
|
|
|
|
coords = {
|
|
x1: options.coords.x1 || 0,
|
|
y1: options.coords.y1 || 0,
|
|
x2: options.coords.x2 || 0,
|
|
y2: options.coords.y2 || 0
|
|
};
|
|
|
|
if (this.type === 'radial') {
|
|
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();
|
|
},
|
|
|
|
/**
|
|
* Adds another colorStop
|
|
* @param {Object} colorStop Object with offset and color
|
|
* @return {fabric.Gradient} thisArg
|
|
*/
|
|
addColorStop: function(colorStop) {
|
|
for (var position in colorStop) {
|
|
var color = new fabric.Color(colorStop[position]);
|
|
this.colorStops.push({
|
|
offset: position,
|
|
color: color.toRgb(),
|
|
opacity: color.getAlpha()
|
|
});
|
|
}
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Returns object representation of a gradient
|
|
* @return {Object}
|
|
*/
|
|
toObject: function() {
|
|
return {
|
|
type: this.type,
|
|
coords: this.coords,
|
|
gradientUnits: this.gradientUnits,
|
|
colorStops: this.colorStops
|
|
};
|
|
},
|
|
|
|
/* _TO_SVG_START_ */
|
|
/**
|
|
* Returns SVG representation of an gradient
|
|
* @param {Object} object Object to create a gradient for
|
|
* @param {Boolean} normalize Whether coords should be normalized
|
|
* @return {String} SVG representation of an gradient (linear/radial)
|
|
*/
|
|
toSVG: function(object, normalize) {
|
|
var coords = fabric.util.object.clone(this.coords),
|
|
markup;
|
|
|
|
// colorStops must be sorted ascending
|
|
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;
|
|
}
|
|
else if (this.gradientUnits === 'objectBoundingBox') {
|
|
_convertValuesToPercentUnits(object, coords);
|
|
}
|
|
|
|
if (this.type === 'linear') {
|
|
markup = [
|
|
'<linearGradient ',
|
|
'id="SVGID_', this.id,
|
|
'" gradientUnits="', this.gradientUnits,
|
|
'" x1="', coords.x1,
|
|
'" y1="', coords.y1,
|
|
'" x2="', coords.x2,
|
|
'" y2="', coords.y2,
|
|
'">'
|
|
];
|
|
}
|
|
else if (this.type === 'radial') {
|
|
markup = [
|
|
'<radialGradient ',
|
|
'id="SVGID_', this.id,
|
|
'" gradientUnits="', this.gradientUnits,
|
|
'" cx="', coords.x2,
|
|
'" cy="', coords.y2,
|
|
'" r="', coords.r2,
|
|
'" fx="', coords.x1,
|
|
'" fy="', coords.y1,
|
|
'">'
|
|
];
|
|
}
|
|
|
|
for (var i = 0; i < this.colorStops.length; i++) {
|
|
markup.push(
|
|
'<stop ',
|
|
'offset="', (this.colorStops[i].offset * 100) + '%',
|
|
'" style="stop-color:', this.colorStops[i].color,
|
|
(this.colorStops[i].opacity ? ';stop-opacity: ' + this.colorStops[i].opacity : ';'),
|
|
'"/>'
|
|
);
|
|
}
|
|
|
|
markup.push((this.type === 'linear' ? '</linearGradient>' : '</radialGradient>'));
|
|
|
|
return markup.join('');
|
|
},
|
|
/* _TO_SVG_END_ */
|
|
|
|
/**
|
|
* Returns an instance of CanvasGradient
|
|
* @param {CanvasRenderingContext2D} ctx Context to render on
|
|
* @return {CanvasGradient}
|
|
*/
|
|
toLive: function(ctx) {
|
|
var gradient;
|
|
|
|
if (!this.type) return;
|
|
|
|
if (this.type === 'linear') {
|
|
gradient = ctx.createLinearGradient(
|
|
this.coords.x1, this.coords.y1, this.coords.x2, this.coords.y2);
|
|
}
|
|
else if (this.type === 'radial') {
|
|
gradient = ctx.createRadialGradient(
|
|
this.coords.x1, this.coords.y1, this.coords.r1, this.coords.x2, this.coords.y2, this.coords.r2);
|
|
}
|
|
|
|
for (var i = 0, len = this.colorStops.length; i < len; i++) {
|
|
var color = this.colorStops[i].color,
|
|
opacity = this.colorStops[i].opacity,
|
|
offset = this.colorStops[i].offset;
|
|
|
|
if (typeof opacity !== 'undefined') {
|
|
color = new fabric.Color(color).setAlpha(opacity).toRgba();
|
|
}
|
|
gradient.addColorStop(parseFloat(offset), color);
|
|
}
|
|
|
|
return gradient;
|
|
}
|
|
});
|
|
|
|
fabric.util.object.extend(fabric.Gradient, {
|
|
|
|
/* _FROM_SVG_START_ */
|
|
/**
|
|
* Returns {@link fabric.Gradient} instance from an SVG element
|
|
* @static
|
|
* @memberof fabric.Gradient
|
|
* @param {SVGGradientElement} el SVG gradient element
|
|
* @param {fabric.Object} instance
|
|
* @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) {
|
|
|
|
/**
|
|
* @example:
|
|
*
|
|
* <linearGradient id="linearGrad1">
|
|
* <stop offset="0%" stop-color="white"/>
|
|
* <stop offset="100%" stop-color="black"/>
|
|
* </linearGradient>
|
|
*
|
|
* OR
|
|
*
|
|
* <linearGradient id="linearGrad2">
|
|
* <stop offset="0" style="stop-color:rgb(255,255,255)"/>
|
|
* <stop offset="1" style="stop-color:rgb(0,0,0)"/>
|
|
* </linearGradient>
|
|
*
|
|
* OR
|
|
*
|
|
* <radialGradient id="radialGrad1">
|
|
* <stop offset="0%" stop-color="white" stop-opacity="1" />
|
|
* <stop offset="50%" stop-color="black" stop-opacity="0.5" />
|
|
* <stop offset="100%" stop-color="white" stop-opacity="1" />
|
|
* </radialGradient>
|
|
*
|
|
* OR
|
|
*
|
|
* <radialGradient id="radialGrad2">
|
|
* <stop offset="0" stop-color="rgb(255,255,255)" />
|
|
* <stop offset="0.5" stop-color="rgb(0,0,0)" />
|
|
* <stop offset="1" stop-color="rgb(255,255,255)" />
|
|
* </radialGradient>
|
|
*
|
|
*/
|
|
|
|
var colorStopEls = el.getElementsByTagName('stop'),
|
|
type = (el.nodeName === 'linearGradient' ? 'linear' : 'radial'),
|
|
gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
|
|
colorStops = [],
|
|
coords = { };
|
|
|
|
if (type === 'linear') {
|
|
coords = getLinearCoords(el);
|
|
}
|
|
else if (type === 'radial') {
|
|
coords = getRadialCoords(el);
|
|
}
|
|
|
|
for (var i = colorStopEls.length; i--; ) {
|
|
colorStops.push(getColorStop(colorStopEls[i]));
|
|
}
|
|
|
|
_convertPercentUnitsToValues(instance, coords);
|
|
|
|
return new fabric.Gradient({
|
|
type: type,
|
|
coords: coords,
|
|
gradientUnits: gradientUnits,
|
|
colorStops: colorStops
|
|
});
|
|
},
|
|
/* _FROM_SVG_END_ */
|
|
|
|
/**
|
|
* Returns {@link fabric.Gradient} instance from its object representation
|
|
* @static
|
|
* @memberof fabric.Gradient
|
|
* @param {Object} obj
|
|
* @param {Object} [options] Options object
|
|
*/
|
|
forObject: function(obj, options) {
|
|
options || (options = { });
|
|
_convertPercentUnitsToValues(obj, options);
|
|
return new fabric.Gradient(options);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
function _convertPercentUnitsToValues(object, options) {
|
|
for (var prop in options) {
|
|
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);
|
|
}
|
|
else if (prop === 'y1' || prop === 'y2') {
|
|
options[prop] = fabric.util.toFixed(object.height * percents / 100, 2);
|
|
}
|
|
}
|
|
normalize(options, prop, object);
|
|
}
|
|
}
|
|
|
|
// normalize rendering point (should be from top/left corner rather than center of the shape)
|
|
function normalize(options, prop, object) {
|
|
if (prop === 'x1' || prop === 'x2') {
|
|
options[prop] -= fabric.util.toFixed(object.width / 2, 2);
|
|
}
|
|
else if (prop === 'y1' || prop === 'y2') {
|
|
options[prop] -= fabric.util.toFixed(object.height / 2, 2);
|
|
}
|
|
}
|
|
|
|
/* _TO_SVG_START_ */
|
|
/**
|
|
* @private
|
|
*/
|
|
function _convertValuesToPercentUnits(object, options) {
|
|
for (var prop in options) {
|
|
|
|
normalize(options, prop, object);
|
|
|
|
// convert to percent units
|
|
if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
|
|
options[prop] = fabric.util.toFixed(options[prop] / object.width * 100, 2) + '%';
|
|
}
|
|
else if (prop === 'y1' || prop === 'y2') {
|
|
options[prop] = fabric.util.toFixed(options[prop] / object.height * 100, 2) + '%';
|
|
}
|
|
}
|
|
}
|
|
/* _TO_SVG_END_ */
|
|
|
|
})();
|