mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-05-21 03:41:52 +00:00
commit
4c7ad6a82d
24 changed files with 151 additions and 270 deletions
|
|
@ -1,6 +1,6 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
- "0.10.29"
|
||||
script: 'npm run-script build && npm test'
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
var fill = this.fill
|
||||
? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
|
||||
: 'none',
|
||||
|
||||
fillRule = (this.fillRule === 'destination-over' ? 'evenodd' : this.fillRule),
|
||||
stroke = this.stroke
|
||||
? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
|
||||
: 'none',
|
||||
|
|
@ -33,6 +33,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
'stroke-linejoin: ', strokeLineJoin, '; ',
|
||||
'stroke-miterlimit: ', strokeMiterLimit, '; ',
|
||||
'fill: ', fill, '; ',
|
||||
'fill-rule: ', fillRule, '; ',
|
||||
'opacity: ', opacity, ';',
|
||||
filter,
|
||||
visibility
|
||||
|
|
@ -44,6 +45,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @return {String}
|
||||
*/
|
||||
getSvgTransform: function() {
|
||||
if (this.group) return '';
|
||||
var toFixed = fabric.util.toFixed,
|
||||
angle = this.getAngle(),
|
||||
vpt = this.getViewportTransform(),
|
||||
|
|
@ -51,7 +53,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
|
||||
NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
|
||||
|
||||
translatePart = 'translate(' +
|
||||
translatePart = this.type === 'path-group' ? '' : 'translate(' +
|
||||
toFixed(center.x, NUM_FRACTION_DIGITS) +
|
||||
' ' +
|
||||
toFixed(center.y, NUM_FRACTION_DIGITS) +
|
||||
|
|
@ -68,16 +70,24 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
' ' +
|
||||
toFixed(this.scaleY * vpt[3], NUM_FRACTION_DIGITS) +
|
||||
')'),
|
||||
|
||||
flipXPart = this.flipX ? 'matrix(-1 0 0 1 0 0) ' : '',
|
||||
|
||||
flipYPart = this.flipY ? 'matrix(1 0 0 -1 0 0)' : '';
|
||||
addTranslateX = this.type === 'path-group' ? this.width * vpt[0] : 0,
|
||||
flipXPart = this.flipX ? ' matrix(-1 0 0 1 ' + addTranslateX + ' 0) ' : '',
|
||||
addTranslateY = this.type === 'path-group' ? this.height * vpt[3] : 0,
|
||||
flipYPart = this.flipY ? ' matrix(1 0 0 -1 0 ' + addTranslateY + ')' : '';
|
||||
|
||||
return [
|
||||
translatePart, anglePart, scalePart, flipXPart, flipYPart
|
||||
].join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns transform-string for svg-export from the transform matrix of single elements
|
||||
* @return {String}
|
||||
*/
|
||||
getSvgTransformMatrix: function() {
|
||||
return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ')' : '';
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -527,7 +527,7 @@
|
|||
if (heightAttr && heightAttr !== viewBoxHeight) {
|
||||
scaleY = heightAttr / viewBoxHeight;
|
||||
}
|
||||
addSvgTransform(doc, [scaleX, 0, 0, scaleY, -minX, -minY]);
|
||||
addSvgTransform(doc, [scaleX, 0, 0, scaleY, scaleX * -minX, scaleY * -minY]);
|
||||
}
|
||||
|
||||
var descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
|
||||
|
|
|
|||
|
|
@ -79,15 +79,19 @@
|
|||
* @return {String} svg representation of an instance
|
||||
*/
|
||||
toSVG: function(reviver) {
|
||||
var markup = this._createBaseSVGMarkup();
|
||||
|
||||
var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
|
||||
if (this.group) {
|
||||
x = this.left + this.radius;
|
||||
y = this.top + this.radius;
|
||||
}
|
||||
markup.push(
|
||||
'<circle ',
|
||||
'cx="0" cy="0" ',
|
||||
'cx="' + x + '" cy="' + y + '" ',
|
||||
'r="', this.radius,
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="', this.getSvgTransform(),
|
||||
'"/>'
|
||||
' ', this.getSvgTransformMatrix(),
|
||||
'"/>\n'
|
||||
);
|
||||
|
||||
return reviver ? reviver(markup.join('')) : markup.join('');
|
||||
|
|
@ -101,11 +105,9 @@
|
|||
*/
|
||||
_render: function(ctx, noTransform) {
|
||||
ctx.beginPath();
|
||||
// multiply by currently set alpha (the one that was set by path group where this object is contained, for example)
|
||||
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
|
||||
ctx.arc(noTransform ? this.left : 0, noTransform ? this.top : 0, this.radius, 0, piBy2, false);
|
||||
ctx.arc(noTransform ? this.left + this.radius : 0, noTransform ? this.top + this.radius : 0, this.radius, 0, piBy2, false);
|
||||
this._renderFill(ctx);
|
||||
this.stroke && this._renderStroke(ctx);
|
||||
this._renderStroke(ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -169,22 +171,13 @@
|
|||
throw new Error('value of `r` attribute is required and can not be negative');
|
||||
}
|
||||
|
||||
if (!('left' in parsedAttributes)) {
|
||||
parsedAttributes.left = 0;
|
||||
}
|
||||
if (!('top' in parsedAttributes)) {
|
||||
parsedAttributes.top = 0;
|
||||
}
|
||||
if (!('transformMatrix' in parsedAttributes)) {
|
||||
parsedAttributes.left -= options.width ? (options.width / 2) : 0;
|
||||
parsedAttributes.top -= options.height ? (options.height / 2) : 0;
|
||||
}
|
||||
parsedAttributes.left = parsedAttributes.left || 0;
|
||||
parsedAttributes.top = parsedAttributes.top || 0;
|
||||
|
||||
var obj = new fabric.Circle(extend(parsedAttributes, options));
|
||||
|
||||
obj.cx = parseFloat(element.getAttribute('cx')) || 0;
|
||||
obj.cy = parseFloat(element.getAttribute('cy')) || 0;
|
||||
|
||||
obj.left -= obj.radius;
|
||||
obj.top -= obj.radius;
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -77,34 +77,26 @@
|
|||
* @return {String} svg representation of an instance
|
||||
*/
|
||||
toSVG: function(reviver) {
|
||||
var markup = this._createBaseSVGMarkup();
|
||||
|
||||
var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
|
||||
if (this.group) {
|
||||
x = this.left + this.rx;
|
||||
y = this.top + this.ry;
|
||||
}
|
||||
markup.push(
|
||||
'<ellipse ',
|
||||
'rx="', this.get('rx'),
|
||||
'" ry="', this.get('ry'),
|
||||
'cx="', x, '" cy="', y, '" ',
|
||||
'rx="', this.rx,
|
||||
'" ry="', this.ry,
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="', this.getSvgTransform(),
|
||||
'"/>'
|
||||
this.getSvgTransformMatrix(),
|
||||
'"/>\n'
|
||||
);
|
||||
|
||||
return reviver ? reviver(markup.join('')) : markup.join('');
|
||||
},
|
||||
/* _TO_SVG_END_ */
|
||||
|
||||
/**
|
||||
* Renders this instance on a given context
|
||||
* @param {CanvasRenderingContext2D} ctx context to render on
|
||||
* @param {Boolean} [noTransform] When true, context is not transformed
|
||||
*/
|
||||
render: function(ctx, noTransform) {
|
||||
// do not use `get` for perf. reasons
|
||||
if (this.rx === 0 || this.ry === 0) {
|
||||
return;
|
||||
}
|
||||
return this.callSuper('render', ctx, noTransform);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx context to render on
|
||||
|
|
@ -112,10 +104,9 @@
|
|||
*/
|
||||
_render: function(ctx, noTransform) {
|
||||
ctx.beginPath();
|
||||
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
|
||||
ctx.save();
|
||||
ctx.transform(1, 0, 0, this.ry/this.rx, 0, 0);
|
||||
ctx.arc(noTransform ? this.left : 0, noTransform ? this.top * this.rx/this.ry : 0, this.rx, 0, piBy2, false);
|
||||
ctx.arc(noTransform ? this.left + this.rx : 0, noTransform ? (this.top + this.ry) * this.rx/this.ry : 0, this.rx, 0, piBy2, false);
|
||||
ctx.restore();
|
||||
this._renderFill(ctx);
|
||||
this._renderStroke(ctx);
|
||||
|
|
@ -152,21 +143,13 @@
|
|||
|
||||
var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES);
|
||||
|
||||
if (!('left' in parsedAttributes)) {
|
||||
parsedAttributes.left = 0;
|
||||
}
|
||||
if (!('top' in parsedAttributes)) {
|
||||
parsedAttributes.top = 0;
|
||||
}
|
||||
if (!('transformMatrix' in parsedAttributes)) {
|
||||
parsedAttributes.left -= options.width ? (options.width / 2) : 0;
|
||||
parsedAttributes.top -= options.height ? (options.height / 2) : 0;
|
||||
}
|
||||
parsedAttributes.left = parsedAttributes.left || 0;
|
||||
parsedAttributes.top = parsedAttributes.top || 0;
|
||||
|
||||
var ellipse = new fabric.Ellipse(extend(parsedAttributes, options));
|
||||
|
||||
ellipse.cx = parseFloat(element.getAttribute('cx')) || 0;
|
||||
ellipse.cy = parseFloat(element.getAttribute('cy')) || 0;
|
||||
|
||||
ellipse.top -= ellipse.ry;
|
||||
ellipse.left -= ellipse.rx;
|
||||
return ellipse;
|
||||
};
|
||||
/* _FROM_SVG_END_ */
|
||||
|
|
|
|||
|
|
@ -482,7 +482,7 @@
|
|||
//jscs:disable validateIndentation
|
||||
'<g ',
|
||||
'transform="', this.getSvgTransform(),
|
||||
'">'
|
||||
'">\n'
|
||||
//jscs:enable validateIndentation
|
||||
];
|
||||
|
||||
|
|
@ -490,7 +490,7 @@
|
|||
markup.push(this._objects[i].toSVG(reviver));
|
||||
}
|
||||
|
||||
markup.push('</g>');
|
||||
markup.push('</g>\n');
|
||||
|
||||
return reviver ? reviver(markup.join('')) : markup.join('');
|
||||
},
|
||||
|
|
|
|||
|
|
@ -112,46 +112,6 @@
|
|||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders image on a specified context
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
* @param {Boolean} [noTransform] When true, context is not transformed
|
||||
*/
|
||||
render: function(ctx, noTransform) {
|
||||
// do not render if object is not visible
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.save();
|
||||
var m = this.transformMatrix,
|
||||
isInPathGroup = this.group && this.group.type === 'path-group';
|
||||
|
||||
// this._resetWidthHeight();
|
||||
if (isInPathGroup) {
|
||||
ctx.translate(-this.group.width/2, -this.group.height/2);
|
||||
}
|
||||
if (m) {
|
||||
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
if (!noTransform) {
|
||||
this.transform(ctx);
|
||||
}
|
||||
if (isInPathGroup) {
|
||||
ctx.translate(this.width/2, this.height/2);
|
||||
}
|
||||
|
||||
this._setShadow(ctx);
|
||||
this.clipTo && fabric.util.clipContext(this, ctx);
|
||||
this._render(ctx);
|
||||
if (this.shadow && !this.shadow.affectStroke) {
|
||||
this._removeShadow(ctx);
|
||||
}
|
||||
this._renderStroke(ctx);
|
||||
this.clipTo && ctx.restore();
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
|
|
@ -209,20 +169,23 @@
|
|||
* @return {String} svg representation of an instance
|
||||
*/
|
||||
toSVG: function(reviver) {
|
||||
var markup = [];
|
||||
|
||||
var markup = [], x = -this.width / 2, y = -this.height / 2;
|
||||
if (this.group) {
|
||||
x = this.left;
|
||||
y = this.top;
|
||||
}
|
||||
markup.push(
|
||||
'<g transform="', this.getSvgTransform(), '">',
|
||||
'<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
|
||||
'<image xlink:href="', this.getSvgSrc(),
|
||||
'" x="', x, '" y="', y,
|
||||
'" style="', this.getSvgStyles(),
|
||||
// we're essentially moving origin of transformation from top/left corner to the center of the shape
|
||||
// by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
|
||||
// so that object's center aligns with container's left/top
|
||||
'" transform="translate(' + (-this.width/2) + ' ' + (-this.height/2) + ')',
|
||||
'" width="', this.width,
|
||||
'" height="', this.height,
|
||||
'" preserveAspectRatio="none"',
|
||||
'></image>'
|
||||
'></image>\n'
|
||||
);
|
||||
|
||||
if (this.stroke || this.strokeDashArray) {
|
||||
|
|
@ -230,15 +193,15 @@
|
|||
this.fill = null;
|
||||
markup.push(
|
||||
'<rect ',
|
||||
'x="', (-1 * this.width / 2), '" y="', (-1 * this.height / 2),
|
||||
'x="', x, '" y="', y,
|
||||
'" width="', this.width, '" height="', this.height,
|
||||
'" style="', this.getSvgStyles(),
|
||||
'"/>'
|
||||
'"/>\n'
|
||||
);
|
||||
this.fill = origFill;
|
||||
}
|
||||
|
||||
markup.push('</g>');
|
||||
markup.push('</g>\n');
|
||||
|
||||
return reviver ? reviver(markup.join('')) : markup.join('');
|
||||
},
|
||||
|
|
@ -332,15 +295,16 @@
|
|||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
_render: function(ctx) {
|
||||
_render: function(ctx, noTransform) {
|
||||
this._element &&
|
||||
ctx.drawImage(
|
||||
this._element,
|
||||
-this.width / 2,
|
||||
-this.height / 2,
|
||||
noTransform ? this.left : -this.width/2,
|
||||
noTransform ? this.top : -this.height/2,
|
||||
this.width,
|
||||
this.height
|
||||
);
|
||||
this._renderStroke(ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -150,11 +150,10 @@
|
|||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
_render: function(ctx) {
|
||||
_render: function(ctx, noTransform) {
|
||||
ctx.beginPath();
|
||||
|
||||
var isInPathGroup = this.group && this.group.type === 'path-group';
|
||||
if (isInPathGroup) {
|
||||
if (noTransform) {
|
||||
// Line coords are distances from left-top of canvas to origin of line.
|
||||
//
|
||||
// To render line in a path-group, we need to translate them to
|
||||
|
|
@ -164,9 +163,6 @@
|
|||
cp.x,
|
||||
cp.y
|
||||
);
|
||||
if (!this.transformMatrix) {
|
||||
ctx.translate(-this.group.width / 2, -this.group.height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) {
|
||||
|
|
@ -234,16 +230,22 @@
|
|||
* @return {String} svg representation of an instance
|
||||
*/
|
||||
toSVG: function(reviver) {
|
||||
var markup = this._createBaseSVGMarkup();
|
||||
|
||||
var markup = this._createBaseSVGMarkup(), addTranslate = '';
|
||||
if (!this.group) {
|
||||
var x = - this.width / 2 - (this.x1 > this.x2 ? this.x2 : this.x1),
|
||||
y = - this.height / 2 - (this.y1 > this.y2 ? this.y2 : this.y1);
|
||||
addTranslate = 'translate(' + x + ', ' + y + ') ';
|
||||
}
|
||||
markup.push(
|
||||
'<line ',
|
||||
'x1="', this.get('x1'),
|
||||
'" y1="', this.get('y1'),
|
||||
'" x2="', this.get('x2'),
|
||||
'" y2="', this.get('y2'),
|
||||
'x1="', this.x1,
|
||||
'" y1="', this.y1,
|
||||
'" x2="', this.x2,
|
||||
'" y2="', this.y2,
|
||||
'" style="', this.getSvgStyles(),
|
||||
'"/>'
|
||||
'" transform="', this.getSvgTransform(), addTranslate,
|
||||
this.getSvgTransformMatrix(),
|
||||
'"/>\n'
|
||||
);
|
||||
|
||||
return reviver ? reviver(markup.join('')) : markup.join('');
|
||||
|
|
|
|||
|
|
@ -960,12 +960,14 @@
|
|||
this._setStrokeStyles(ctx);
|
||||
this._setFillStyles(ctx);
|
||||
|
||||
var m = this.transformMatrix;
|
||||
if (m && this.group) {
|
||||
if (this.group && this.group.type === 'path-group') {
|
||||
ctx.translate(-this.group.width/2, -this.group.height/2);
|
||||
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
var m = this.transformMatrix;
|
||||
if (m) {
|
||||
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
|
||||
this._setShadow(ctx);
|
||||
this.clipTo && fabric.util.clipContext(this, ctx);
|
||||
this._render(ctx, noTransform);
|
||||
|
|
|
|||
|
|
@ -545,15 +545,13 @@
|
|||
var path = chunks.join(' ');
|
||||
|
||||
markup.push(
|
||||
//jscs:disable validateIndentation
|
||||
'<g transform="', (this.group ? '' : this.getSvgTransform()), '">',
|
||||
'<path ',
|
||||
'd="', path,
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="translate(', (-this.width / 2), ' ', (-this.height/2), ')',
|
||||
'" stroke-linecap="round" ',
|
||||
'/>',
|
||||
'</g>'
|
||||
//jscs:disable validateIndentation
|
||||
'<path ',
|
||||
'd="', path,
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="', this.getSvgTransform(),
|
||||
this.getSvgTransformMatrix(), '" stroke-linecap="round" ',
|
||||
'/>\n'
|
||||
//jscs:enable validateIndentation
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -149,19 +149,20 @@
|
|||
*/
|
||||
toSVG: function(reviver) {
|
||||
var objects = this.getObjects(),
|
||||
translatePart = 'translate(' + this.left + ' ' + this.top + ')',
|
||||
markup = [
|
||||
//jscs:disable validateIndentation
|
||||
'<g ',
|
||||
'style="', this.getSvgStyles(), '" ',
|
||||
'transform="', this.getSvgTransform(), '" ',
|
||||
'>'
|
||||
'transform="', translatePart, this.getSvgTransform(), '" ',
|
||||
'>\n'
|
||||
//jscs:enable validateIndentation
|
||||
];
|
||||
|
||||
for (var i = 0, len = objects.length; i < len; i++) {
|
||||
markup.push(objects[i].toSVG(reviver));
|
||||
}
|
||||
markup.push('</g>');
|
||||
markup.push('</g>\n');
|
||||
|
||||
return reviver ? reviver(markup.join('')) : markup.join('');
|
||||
},
|
||||
|
|
|
|||
|
|
@ -111,7 +111,8 @@
|
|||
'points="', points.join(''),
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="', this.getSvgTransform(),
|
||||
'"/>'
|
||||
' ', this.getSvgTransformMatrix(),
|
||||
'"/>\n'
|
||||
);
|
||||
|
||||
return reviver ? reviver(markup.join('')) : markup.join('');
|
||||
|
|
@ -125,7 +126,6 @@
|
|||
_render: function(ctx) {
|
||||
var point;
|
||||
ctx.beginPath();
|
||||
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
|
||||
ctx.moveTo(this.points[0].x, this.points[0].y);
|
||||
for (var i = 0, len = this.points.length; i < len; i++) {
|
||||
point = this.points[i];
|
||||
|
|
@ -184,6 +184,7 @@
|
|||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
options || (options = { });
|
||||
|
||||
var points = fabric.parsePointsAttribute(element.getAttribute('points')),
|
||||
|
|
@ -193,9 +194,6 @@
|
|||
return null;
|
||||
}
|
||||
|
||||
if (!('transformMatrix' in parsedAttributes)) {
|
||||
fabric.util.normalizePoints(points, options);
|
||||
}
|
||||
return new fabric.Polygon(points, extend(parsedAttributes, options), true);
|
||||
};
|
||||
/* _FROM_SVG_END_ */
|
||||
|
|
|
|||
|
|
@ -95,7 +95,8 @@
|
|||
'points="', points.join(''),
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="', this.getSvgTransform(),
|
||||
'"/>'
|
||||
' ', this.getSvgTransformMatrix(),
|
||||
'"/>\n'
|
||||
);
|
||||
|
||||
return reviver ? reviver(markup.join('')) : markup.join('');
|
||||
|
|
@ -169,9 +170,10 @@
|
|||
var points = fabric.parsePointsAttribute(element.getAttribute('points')),
|
||||
parsedAttributes = fabric.parseAttributes(element, fabric.Polyline.ATTRIBUTE_NAMES);
|
||||
|
||||
if (!('transformMatrix' in parsedAttributes)) {
|
||||
fabric.util.normalizePoints(points, options);
|
||||
if (points === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new fabric.Polyline(points, fabric.util.object.extend(parsedAttributes, options), true);
|
||||
};
|
||||
/* _FROM_SVG_END_ */
|
||||
|
|
|
|||
|
|
@ -50,17 +50,6 @@
|
|||
*/
|
||||
ry: 0,
|
||||
|
||||
/**
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
x: 0,
|
||||
|
||||
/**
|
||||
* @type Number
|
||||
* @default
|
||||
*/
|
||||
y: 0,
|
||||
|
||||
/**
|
||||
* Used to specify dash pattern for stroke on this object
|
||||
|
|
@ -79,8 +68,6 @@
|
|||
this.callSuper('initialize', options);
|
||||
this._initRxRy();
|
||||
|
||||
this.x = options.x || 0;
|
||||
this.y = options.y || 0;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -100,7 +87,7 @@
|
|||
* @private
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
_render: function(ctx) {
|
||||
_render: function(ctx, noTransform) {
|
||||
|
||||
// optimize 1x1 case (used in spray brush)
|
||||
if (this.width === 1 && this.height === 1) {
|
||||
|
|
@ -112,24 +99,15 @@
|
|||
ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
|
||||
w = this.width,
|
||||
h = this.height,
|
||||
x = -w / 2,
|
||||
y = -h / 2,
|
||||
isInPathGroup = this.group && this.group.type === 'path-group',
|
||||
x = noTransform ? this.left : 0,
|
||||
y = noTransform ? this.top : 0,
|
||||
isRounded = rx !== 0 || ry !== 0,
|
||||
k = 1 - 0.5522847498 /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.globalAlpha = isInPathGroup ? (ctx.globalAlpha * this.opacity) : this.opacity;
|
||||
|
||||
if (this.transformMatrix && isInPathGroup) {
|
||||
ctx.translate(
|
||||
this.width / 2 + this.x,
|
||||
this.height / 2 + this.y);
|
||||
}
|
||||
if (!this.transformMatrix && isInPathGroup) {
|
||||
ctx.translate(
|
||||
-this.group.width / 2 + this.width / 2 + this.x,
|
||||
-this.group.height / 2 + this.height / 2 + this.y);
|
||||
if (!noTransform) {
|
||||
ctx.translate(-this.width / 2, -this.height / 2);
|
||||
}
|
||||
|
||||
ctx.moveTo(x + rx, y);
|
||||
|
|
@ -170,22 +148,6 @@
|
|||
ctx.closePath();
|
||||
},
|
||||
|
||||
/**
|
||||
* Since coordinate system differs from that of SVG
|
||||
* @private
|
||||
*/
|
||||
_normalizeLeftTopProperties: function(parsedAttributes) {
|
||||
if ('left' in parsedAttributes) {
|
||||
this.set('left', parsedAttributes.left + this.getWidth() / 2);
|
||||
}
|
||||
this.set('x', parsedAttributes.left || 0);
|
||||
if ('top' in parsedAttributes) {
|
||||
this.set('top', parsedAttributes.top + this.getHeight() / 2);
|
||||
}
|
||||
this.set('y', parsedAttributes.top || 0);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns object representation of an instance
|
||||
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
|
||||
|
|
@ -194,9 +156,7 @@
|
|||
toObject: function(propertiesToInclude) {
|
||||
var object = extend(this.callSuper('toObject', propertiesToInclude), {
|
||||
rx: this.get('rx') || 0,
|
||||
ry: this.get('ry') || 0,
|
||||
x: this.get('x'),
|
||||
y: this.get('y')
|
||||
ry: this.get('ry') || 0
|
||||
});
|
||||
if (!this.includeDefaultValues) {
|
||||
this._removeDefaultValues(object);
|
||||
|
|
@ -211,16 +171,20 @@
|
|||
* @return {String} svg representation of an instance
|
||||
*/
|
||||
toSVG: function(reviver) {
|
||||
var markup = this._createBaseSVGMarkup();
|
||||
|
||||
var markup = this._createBaseSVGMarkup(), x = this.left, y = this.top;
|
||||
if (!this.group) {
|
||||
x = -this.width / 2;
|
||||
y = -this.height / 2;
|
||||
}
|
||||
markup.push(
|
||||
'<rect ',
|
||||
'x="', (-1 * this.width / 2), '" y="', (-1 * this.height / 2),
|
||||
'x="', x, '" y="', y,
|
||||
'" rx="', this.get('rx'), '" ry="', this.get('ry'),
|
||||
'" width="', this.width, '" height="', this.height,
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="', this.getSvgTransform(),
|
||||
'"/>');
|
||||
this.getSvgTransformMatrix(),
|
||||
'"/>\n');
|
||||
|
||||
return reviver ? reviver(markup.join('')) : markup.join('');
|
||||
},
|
||||
|
|
@ -244,15 +208,6 @@
|
|||
*/
|
||||
fabric.Rect.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y rx ry width height'.split(' '));
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _setDefaultLeftTopValues(attributes) {
|
||||
attributes.left = attributes.left || 0;
|
||||
attributes.top = attributes.top || 0;
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link fabric.Rect} instance from an SVG element
|
||||
* @static
|
||||
|
|
@ -265,14 +220,14 @@
|
|||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
options = options || { };
|
||||
|
||||
var parsedAttributes = fabric.parseAttributes(element, fabric.Rect.ATTRIBUTE_NAMES);
|
||||
parsedAttributes = _setDefaultLeftTopValues(parsedAttributes);
|
||||
|
||||
var rect = new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
|
||||
rect._normalizeLeftTopProperties(parsedAttributes);
|
||||
|
||||
return rect;
|
||||
|
||||
parsedAttributes.left = parsedAttributes.left || 0;
|
||||
parsedAttributes.top = parsedAttributes.top || 0;
|
||||
|
||||
return new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
|
||||
};
|
||||
/* _FROM_SVG_END_ */
|
||||
|
||||
|
|
|
|||
|
|
@ -849,8 +849,8 @@
|
|||
: (this.height/2) - (textLines.length * this.fontSize) - this._totalLineHeight;
|
||||
|
||||
return {
|
||||
textLeft: textLeft,
|
||||
textTop: textTop,
|
||||
textLeft: textLeft + (this.group ? this.left : 0),
|
||||
textTop: textTop + (this.group ? this.top : 0),
|
||||
lineTop: lineTop
|
||||
};
|
||||
},
|
||||
|
|
@ -860,7 +860,7 @@
|
|||
*/
|
||||
_wrapSVGTextAndBg: function(markup, textAndBg, shadowSpans, offsets) {
|
||||
markup.push(
|
||||
'<g transform="', this.getSvgTransform(), '">',
|
||||
'<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
|
||||
textAndBg.textBgRects.join(''),
|
||||
'<text ',
|
||||
(this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g,'\'') + '" ': ''),
|
||||
|
|
@ -873,8 +873,8 @@
|
|||
'transform="translate(', toFixed(offsets.textLeft, 2), ' ', toFixed(offsets.textTop, 2), ')">',
|
||||
shadowSpans.join(''),
|
||||
textAndBg.textSpans.join(''),
|
||||
'</text>',
|
||||
'</g>'
|
||||
'</text>\n',
|
||||
'</g>\n'
|
||||
);
|
||||
},
|
||||
|
||||
|
|
@ -992,7 +992,7 @@
|
|||
toFixed(this._boundaries[i].width, 2),
|
||||
'" height="',
|
||||
toFixed(this._boundaries[i].height, 2),
|
||||
'"></rect>');
|
||||
'"></rect>\n');
|
||||
},
|
||||
|
||||
_setSVGBg: function(textBgRects) {
|
||||
|
|
|
|||
|
|
@ -505,26 +505,6 @@
|
|||
return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
|
||||
},
|
||||
|
||||
/**
|
||||
* Normalizes polygon/polyline points according to their dimensions
|
||||
* @param {Array} points
|
||||
* @param {Object} options
|
||||
*/
|
||||
normalizePoints: function(points, options) {
|
||||
var minX = fabric.util.array.min(points, 'x'),
|
||||
minY = fabric.util.array.min(points, 'y');
|
||||
|
||||
minX = minX < 0 ? minX : 0;
|
||||
minY = minX < 0 ? minY : 0;
|
||||
|
||||
for (var i = 0, len = points.length; i < len; i++) {
|
||||
// normalize coordinates, according to containing box
|
||||
// (dimensions of which are passed via `options`)
|
||||
points[i].x -= (options.width / 2 + minX) || 0;
|
||||
points[i].y -= (options.height / 2 + minY) || 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if context has transparent pixel
|
||||
* at specified location (taking tolerance into account)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
var RECT_JSON = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":10,"fill":"rgb(0,0,0)",'+
|
||||
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
|
||||
'"shadow":null,'+
|
||||
'"visible":true,"clipTo":null,"backgroundColor":"","rx":0,"ry":0,"x":0,"y":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
|
||||
'"visible":true,"clipTo":null,"backgroundColor":"","rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
|
||||
|
||||
var el = fabric.document.createElement('canvas');
|
||||
el.width = 600; el.height = 600;
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@
|
|||
var RECT_JSON = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":10,"fill":"rgb(0,0,0)",'+
|
||||
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
|
||||
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","rx":0,"ry":0,"x":0,"y":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
|
||||
|
||||
var RECT_JSON_WITH_PADDING = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":20,"fill":"rgb(0,0,0)",'+
|
||||
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
|
||||
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","padding":123,"foo":"bar","rx":0,"ry":0,"x":0,"y":0}],"background":""}';
|
||||
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","padding":123,"foo":"bar","rx":0,"ry":0}],"background":""}';
|
||||
|
||||
function getAbsolutePath(path) {
|
||||
var isAbsolute = /^https?:/.test(path);
|
||||
|
|
|
|||
|
|
@ -152,8 +152,8 @@
|
|||
ok(oCircle instanceof fabric.Circle);
|
||||
|
||||
equal(oCircle.get('radius'), radius);
|
||||
equal(oCircle.get('left'), left);
|
||||
equal(oCircle.get('top'), top);
|
||||
equal(oCircle.get('left'), left - radius);
|
||||
equal(oCircle.get('top'), top - radius);
|
||||
equal(oCircle.get('fill'), fill);
|
||||
equal(oCircle.get('opacity'), opacity);
|
||||
equal(oCircle.get('strokeWidth'), strokeWidth);
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@
|
|||
|
||||
equal(oEllipse.get('rx'), rx);
|
||||
equal(oEllipse.get('ry'), ry);
|
||||
equal(oEllipse.get('left'), left);
|
||||
equal(oEllipse.get('top'), top);
|
||||
equal(oEllipse.get('left'), left - rx);
|
||||
equal(oEllipse.get('top'), top - ry);
|
||||
equal(oEllipse.get('fill'), fill);
|
||||
equal(oEllipse.get('opacity'), opacity);
|
||||
equal(oEllipse.get('strokeWidth'), strokeWidth);
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ test('toObject without default values', function() {
|
|||
var group = makeGroupWith2Objects();
|
||||
ok(typeof group.toSVG == 'function');
|
||||
|
||||
var expectedSVG = '<g transform="translate(130 160)"><rect x="-15" y="-5" rx="0" ry="0" width="30" height="10" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(25 -25)"/><rect x="-5" y="-20" rx="0" ry="0" width="10" height="40" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(-35 10)"/></g>';
|
||||
var expectedSVG = '<g transform="translate(130 160)">\n<rect x="10" y="-30" rx="0" ry="0" width="30" height="10" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform=""/>\n<rect x="-40" y="-10" rx="0" ry="0" width="10" height="40" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform=""/>\n</g>\n';
|
||||
equal(group.toSVG(), expectedSVG);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -116,16 +116,13 @@
|
|||
deepEqual(polylineWithAttrs.get('transformMatrix'), [ 2, 0, 0, 2, -10, -20 ]);
|
||||
|
||||
var elPolylineWithoutPoints = fabric.document.createElement('polyline');
|
||||
equal(fabric.Polyline.fromElement(elPolylineWithoutPoints), null);
|
||||
|
||||
var error;
|
||||
try {
|
||||
fabric.Polyline.fromElement(elPolylineWithoutPoints);
|
||||
}
|
||||
catch(err) {
|
||||
error = err;
|
||||
}
|
||||
var elPolylineWithEmptyPoints = fabric.document.createElement('polyline');
|
||||
elPolylineWithEmptyPoints.setAttribute('points', '');
|
||||
|
||||
ok(typeof error !== 'undefined', 'missing points attribute should result in error');
|
||||
equal(fabric.Polyline.fromElement(), null);
|
||||
equal(fabric.Polyline.fromElement(elPolylineWithEmptyPoints), null);
|
||||
|
||||
equal(fabric.Polyline.fromElement(), null);
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@
|
|||
'clipTo': null,
|
||||
'rx': 0,
|
||||
'ry': 0,
|
||||
'x': 0,
|
||||
'y': 0
|
||||
};
|
||||
|
||||
QUnit.module('fabric.Rect');
|
||||
|
|
@ -99,8 +97,8 @@
|
|||
ok(rectWithAttrs instanceof fabric.Rect);
|
||||
|
||||
var expectedObject = fabric.util.object.extend(REFERENCE_RECT, {
|
||||
left: 121,
|
||||
top: 186.5,
|
||||
left: 10,
|
||||
top: 20,
|
||||
width: 222,
|
||||
height: 333,
|
||||
fill: 'rgb(255,255,255)',
|
||||
|
|
@ -112,9 +110,7 @@
|
|||
strokeLineJoin: 'bevil',
|
||||
strokeMiterLimit: 5,
|
||||
rx: 11,
|
||||
ry: 12,
|
||||
x: 10,
|
||||
y: 20
|
||||
ry: 12
|
||||
});
|
||||
deepEqual(rectWithAttrs.toObject(), expectedObject);
|
||||
});
|
||||
|
|
@ -135,7 +131,7 @@
|
|||
var rect = new fabric.Rect({ width: 100, height: 100, rx: 20, ry: 30 });
|
||||
var svg = rect.toSVG();
|
||||
|
||||
equal(svg, '<rect x="-50" y="-50" rx="20" ry="30" width="100" height="100" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(50 50)"/>');
|
||||
equal(svg, '<rect x="-50" y="-50" rx="20" ry="30" width="100" height="100" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform="translate(50 50)"/>\n');
|
||||
});
|
||||
|
||||
test('toObject without default values', function() {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
'useNative': true
|
||||
};
|
||||
|
||||
var TEXT_SVG = '<g transform="translate(10 26)"><text font-family="Times New Roman" font-size="40" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(-10 39)"><tspan x="0" y="-26" fill="rgb(0,0,0)">x</tspan></text></g>';
|
||||
var TEXT_SVG = '<g transform="translate(10 26)">\n<text font-family="Times New Roman" font-size="40" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform="translate(-10 39)"><tspan x="0" y="-26" fill="rgb(0,0,0)">x</tspan></text>\n</g>\n';
|
||||
|
||||
test('constructor', function() {
|
||||
ok(fabric.Text);
|
||||
|
|
|
|||
Loading…
Reference in a new issue