[BACK_INCOMPAT] fabric.Text#textShadow has been removed - new fabric.Text.shadow property (type of fabric.Shadow)

[BACK_INCOMPAT] `fabric.BaseBrush`shadow properties are combined into one property => `fabric.BaseBrush.shadow`(shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY no longer exist)
Removed fabric.Text.getText method => is generated with `fabric.util.createAccessors`
`fabric.Shadow` can now initialized with string or object (e.g. '10px 10px 5px rgb(0,0,255)', 'rgb(0,0,255) 10px 10px 5px', {color: 'gb(0,0,255)', offsetX: 10, offsetY: 10, blur: 5})
`fabric.Shadow.getShadow`to get css3 declaration of shadow (String)
`fabric.Object.set('shadow', value)`is now the same as `fabric.Shadow.setShadow(value)`
Add unit tests
This commit is contained in:
Kienz 2013-09-03 19:11:21 +02:00
parent eeb5fc1400
commit 8c74f2b335
15 changed files with 399 additions and 264 deletions

View file

@ -12,7 +12,7 @@ Using Fabric.js, you can create and populate objects on canvas; objects like sim
### Goals
- Unit tested (1800+ tests at the moment)
- Unit tested (2000+ tests at the moment)
- Modular (~60 small "classes", modules, mixins)
- Cross-browser
- [Fast](https://github.com/kangax/fabric.js/wiki/Focus-on-speed)

View file

@ -19,32 +19,13 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
width: 1,
/**
* Shadow blur of a brush
* @type Number
* Shadow object representing shadow of this shape.
* <b>Backwards incompatibility note:</b> This property replaces "shadowColor" (String), "shadowOffsetX" (Number),
* "shadowOffsetY" (Number) and "shadowBlur" (Number) since v1.2.12
* @type fabric.Shadow
* @default
*/
shadowBlur: 0,
/**
* Shadow color of a brush
* @type String
* @default
*/
shadowColor: '',
/**
* Shadow offset x of a brush
* @type Number
* @default
*/
shadowOffsetX: 0,
/**
* Shadow offset y of a brush
* @type Number
* @default
*/
shadowOffsetY: 0,
shadow: null,
/**
* Line endings style of a brush (one of "butt", "round", "square")
@ -61,9 +42,21 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
strokeLineJoin: 'round',
/**
* Sets brush styles
* Sets shadow of an object
* @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
* @return {fabric.Object} thisArg
* @chainable
*/
setBrushStyles: function() {
setShadow: function(options) {
this.shadow = new fabric.Shadow(options);
return this;
},
/**
* Sets brush styles
* @private
*/
_setBrushStyles: function() {
var ctx = this.canvas.contextTop;
ctx.strokeStyle = this.color;
@ -74,22 +67,24 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
/**
* Sets brush shadow styles
* @private
*/
setShadowStyles: function() {
if (!this.shadowColor) return;
_setShadow: function() {
if (!this.shadow) return;
var ctx = this.canvas.contextTop;
ctx.shadowBlur = this.shadowBlur;
ctx.shadowColor = this.shadowColor;
ctx.shadowOffsetX = this.shadowOffsetX;
ctx.shadowOffsetY = this.shadowOffsetY;
ctx.shadowColor = this.shadow.color;
ctx.shadowBlur = this.shadow.blur;
ctx.shadowOffsetX = this.shadow.offsetX;
ctx.shadowOffsetY = this.shadow.offsetY;
},
/**
* Remove brush shadow styles
* Removes brush shadow styles
* @private
*/
removeShadowStyles: function() {
_resetShadow: function() {
var ctx = this.canvas.contextTop;
ctx.shadowColor = '';

View file

@ -2,7 +2,7 @@
* CircleBrush class
* @class fabric.CircleBrush
*/
fabric.CircleBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric.CircleBrush.prototype */ {
fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.CircleBrush.prototype */ {
/**
* Width of a brush
@ -41,7 +41,7 @@ fabric.CircleBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabri
onMouseDown: function(pointer) {
this.points.length = 0;
this.canvas.clearContext(this.canvas.contextTop);
this.setShadowStyles();
this._setShadow();
this.drawDot(pointer);
},
@ -69,21 +69,14 @@ fabric.CircleBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabri
fill: point.fill
});
if (this.shadowColor) {
circle.setShadow({
color: this.shadowColor,
blur: this.shadowBlur,
offsetX: this.shadowOffsetX,
offsetY: this.shadowOffsetY
});
}
this.shadow && circle.setShadow(this.shadow);
this.canvas.add(circle);
this.canvas.fire('path:created', { path: circle });
}
this.canvas.clearContext(this.canvas.contextTop);
this.removeShadowStyles();
this._resetShadow();
this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
this.canvas.renderAll();
},

View file

@ -8,7 +8,7 @@
* @class fabric.PencilBrush
* @extends fabric.BaseBrush
*/
fabric.PencilBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric.PencilBrush.prototype */ {
fabric.PencilBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.PencilBrush.prototype */ {
/**
* Constructor
@ -82,8 +82,8 @@
_reset: function() {
this._points.length = 0;
this.setBrushStyles();
this.setShadowStyles();
this._setBrushStyles();
this._setShadow();
},
/**
@ -221,14 +221,9 @@
path.strokeLineCap = this.strokeLineCap;
path.strokeLineJoin = this.strokeLineJoin;
if (this.shadowColor) {
path.setShadow({
color: this.shadowColor,
blur: this.shadowBlur,
offsetX: this.shadowOffsetX,
offsetY: this.shadowOffsetY,
affectStroke: true
});
if (this.shadow) {
this.shadow.affectStroke = true;
path.setShadow(this.shadow);
}
return path;
@ -267,7 +262,7 @@
path.setCoords();
this.canvas.clearContext(this.canvas.contextTop);
this.removeShadowStyles();
this._resetShadow();
this.canvas.renderAll();
// fire event 'path' created

View file

@ -56,7 +56,7 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
onMouseDown: function(pointer) {
this.sprayChunks.length = 0;
this.canvas.clearContext(this.canvas.contextTop);
this.setShadowStyles();
this._setShadow();
this.addSprayChunk(pointer);
this.render();
@ -91,14 +91,7 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
fill: this.color
});
if (this.shadowColor) {
rect.setShadow({
color: this.shadowColor,
blur: this.shadowBlur,
offsetX: this.shadowOffsetX,
offsetY: this.shadowOffsetY
});
}
this.shadow && rect.setShadow(this.shadow);
this.canvas.add(rect);
this.canvas.fire('path:created', { path: rect });
@ -106,7 +99,7 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
}
this.canvas.clearContext(this.canvas.contextTop);
this.removeShadowStyles();
this._resetShadow();
this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
this.canvas.renderAll();
},

View file

@ -1,93 +1,145 @@
/**
* Shadow class
* @class fabric.Shadow
*/
fabric.Shadow = fabric.util.createClass(/** @lends fabric.Shadow.prototype */ {
(function(global) {
/**
* Shadow color
* @type String
* @default
*/
color: 'rgb(0,0,0)',
"use strict";
/**
* Shadow blur
* @type Number
*/
blur: 0,
var fabric = global.fabric || (global.fabric = { });
/**
* Shadow horizontal offset
* @type Number
* @default
*/
offsetX: 0,
/**
* Shadow vertical offset
* @type Number
* @default
*/
offsetY: 0,
/**
* Whether the shadow should affect stroke operations
* @type Boolean
* @default
*/
affectStroke: false,
/**
* Constructor
* @param {Object} [options] Options object with any of color, blur, offsetX, offsetX properties
* @return {fabric.Shadow} thisArg
*/
initialize: function(options) {
for (var prop in options) {
this[prop] = options[prop];
}
this.id = fabric.Object.__uid++;
},
/* _TO_SVG_START_ */
/**
* Returns SVG representation of a shadow
* @param {Object} object
* @return {String} SVG representation of a shadow
*/
toSVG: function(object) {
var mode = 'SourceAlpha';
if (object.fill === this.color || object.stroke === this.color) {
mode = 'SourceGraphic';
}
return (
'<filter id="SVGID_' + this.id + '" y="-40%" height="180%">' +
'<feGaussianBlur in="' + mode + '" stdDeviation="' +
(this.blur ? this.blur / 3 : 0) +
'"></feGaussianBlur>' +
'<feOffset dx="' + this.offsetX + '" dy="' + this.offsetY + '"></feOffset>' +
'<feMerge>' +
'<feMergeNode></feMergeNode>' +
'<feMergeNode in="SourceGraphic"></feMergeNode>' +
'</feMerge>' +
'</filter>');
},
/* _TO_SVG_END_ */
/**
* Returns object representation of a shadow
* @return {Object} Object representation of a shadow instance
*/
toObject: function() {
return {
color: this.color,
blur: this.blur,
offsetX: this.offsetX,
offsetY: this.offsetY
};
if (fabric.Shadow) {
fabric.warn('fabric.Shadow is already defined.');
return;
}
});
/**
* Shadow class
* @class fabric.Shadow
*/
fabric.Shadow = fabric.util.createClass(/** @lends fabric.Shadow.prototype */ {
/**
* Shadow color
* @type String
* @default
*/
color: 'rgb(0,0,0)',
/**
* Shadow blur
* @type Number
*/
blur: 0,
/**
* Shadow horizontal offset
* @type Number
* @default
*/
offsetX: 0,
/**
* Shadow vertical offset
* @type Number
* @default
*/
offsetY: 0,
/**
* Whether the shadow should affect stroke operations
* @type Boolean
* @default
*/
affectStroke: false,
/**
* Constructor
* @param {Object|String} [options] Options object with any of color, blur, offsetX, offsetX properties or string (e.g. "rgba(0,0,0,0.2) 2px 2px 10px, "2px 2px 10px rgba(0,0,0,0.2)")
* @return {fabric.Shadow} thisArg
*/
initialize: function(options) {
if (typeof options === 'string') {
options = this._parseShadow(options);
}
for (var prop in options) {
this[prop] = options[prop];
}
this.id = fabric.Object.__uid++;
},
/**
* @private
* @param {String} shadow Shadow value to parse
* @return {Object} Shadow object with color, offsetX, offsetY and blur
*/
_parseShadow: function(shadow) {
var shadowStr = shadow.trim();
var offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
color = shadowStr.replace(fabric.Shadow.reOffsetsAndBlur, '') || 'rgb(0,0,0)';
return {
color: color.trim(),
offsetX: parseInt(offsetsAndBlur[1], 10) || 0,
offsetY: parseInt(offsetsAndBlur[2], 10) || 0,
blur: parseInt(offsetsAndBlur[3], 10) || 0
};
},
/**
* @return {String} Returns CSS3 text-shadow declaration
* @see http://www.w3.org/TR/css-text-decor-3/#text-shadow
*/
getShadow: function() {
return [this.offsetX, this.offsetY, this.blur, this.color].join('px ');
},
/* _TO_SVG_START_ */
/**
* Returns SVG representation of a shadow
* @param {Object} object
* @return {String} SVG representation of a shadow
*/
toSVG: function(object) {
var mode = 'SourceAlpha';
if (object && (object.fill === this.color || object.stroke === this.color)) {
mode = 'SourceGraphic';
}
return (
'<filter id="SVGID_' + this.id + '" y="-40%" height="180%">' +
'<feGaussianBlur in="' + mode + '" stdDeviation="' +
(this.blur ? this.blur / 3 : 0) +
'"></feGaussianBlur>' +
'<feOffset dx="' + this.offsetX + '" dy="' + this.offsetY + '"></feOffset>' +
'<feMerge>' +
'<feMergeNode></feMergeNode>' +
'<feMergeNode in="SourceGraphic"></feMergeNode>' +
'</feMerge>' +
'</filter>');
},
/* _TO_SVG_END_ */
/**
* Returns object representation of a shadow
* @return {Object} Object representation of a shadow instance
*/
toObject: function() {
return {
color: this.color,
blur: this.blur,
offsetX: this.offsetX,
offsetY: this.offsetY
};
}
});
/**
* Regex matching shadow offsetX, offsetY and blur (ex: "2px 2px 10px rgba(0,0,0,0.2)", "rgb(0,255,0) 2px 2px")
* @static
* @field
* @memberOf fabric.Shadow
*/
fabric.Shadow.reOffsetsAndBlur = /(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/;
})(typeof exports !== 'undefined' ? exports : this);

View file

@ -172,7 +172,6 @@
fontStyle: true,
lineHeight: true,
textDecoration: true,
textShadow: true,
textAlign: true,
backgroundColor: true
},

View file

@ -404,15 +404,6 @@
}
},
/**
* @private
*/
_initShadow: function(options) {
if (options.shadow && !(options.shadow instanceof fabric.Shadow)) {
this.setShadow(options.shadow);
}
},
/**
* @private
*/
@ -435,7 +426,6 @@
}
this._initGradient(options);
this._initPattern(options);
this._initShadow(options);
this._initClipping(options);
},
@ -539,7 +529,7 @@
var opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1';
var visibility = this.visible ? '' : " visibility: hidden;";
var filter = this.shadow ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
var filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
return [
"stroke: ", stroke, "; ",
@ -685,6 +675,9 @@
else if (key === 'width' || key === 'height') {
this.minScaleLimit = toFixed(Math.min(0.1, 1/Math.max(this.width, this.height)), 2);
}
else if (key === 'shadow' && value && !(value instanceof fabric.Shadow)) {
value = new fabric.Shadow(value);
}
this[key] = value;
@ -1005,7 +998,7 @@
/**
* Sets shadow of an object
* @param {Object} [options] Options object
* @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
* @return {fabric.Object} thisArg
* @chainable
*/

View file

@ -21,7 +21,6 @@
'path',
'text',
'textDecoration',
'textShadow',
'textAlign',
'fontStyle',
'lineHeight',
@ -90,13 +89,6 @@
*/
textDecoration: '',
/**
* Text shadow
* @type String | null
* @default
*/
textShadow: '',
/**
* Text alignment. Possible values: "left", "center", or "right".
* @type String
@ -144,14 +136,14 @@
* @type Boolean
* @default
*/
useNative: true,
useNative: true,
/**
* List of properties to consider when checking if state of an object is changed ({@link fabric.Object#hasStateChanged})
* as well as for history (undo/redo) purposes
* @type Array
*/
stateProperties: stateProperties,
stateProperties: stateProperties,
/**
* When defined, an object is rendered via stroke and this property specifies its color.
@ -159,7 +151,15 @@
* @type String
* @default
*/
stroke: null,
stroke: null,
/**
* Shadow object representing shadow of this shape.
* <b>Backwards incompatibility note:</b> This property was named "textShadow" (String) until v1.2.11
* @type fabric.Shadow
* @default
*/
shadow: null,
/**
* Constructor
@ -244,10 +244,10 @@
}
ctx.save();
this._setTextShadow(ctx);
this._setShadow(ctx);
this._renderTextFill(ctx, textLines);
this._renderTextStroke(ctx, textLines);
this.textShadow && ctx.restore();
this._removeShadow(ctx);
ctx.restore();
if (this.textAlign !== 'left' && this.textAlign !== 'justify') {
@ -334,41 +334,6 @@
return maxWidth;
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_setTextShadow: function(ctx) {
if (!this.textShadow) return;
// "rgba(0,0,0,0.2) 2px 2px 10px"
// "rgb(0, 100, 0) 0 0 5px"
// "red 2px 2px 1px"
// "#f55 123 345 567"
var reOffsetsAndBlur = /\s+(-?\d+)(?:px)?\s+(-?\d+)(?:px)?\s+(\d+)(?:px)?\s*/;
var shadowDeclaration = this.textShadow;
var offsetsAndBlur = reOffsetsAndBlur.exec(this.textShadow);
var shadowColor = shadowDeclaration.replace(reOffsetsAndBlur, '');
ctx.save();
ctx.shadowColor = shadowColor;
ctx.shadowOffsetX = parseInt(offsetsAndBlur[1], 10);
ctx.shadowOffsetY = parseInt(offsetsAndBlur[2], 10);
ctx.shadowBlur = parseInt(offsetsAndBlur[3], 10);
this._shadows = [{
blur: ctx.shadowBlur,
color: ctx.shadowColor,
offX: ctx.shadowOffsetX,
offY: ctx.shadowOffsetY
}];
this._shadowOffsets = [[
parseInt(ctx.shadowOffsetX, 10), parseInt(ctx.shadowOffsetY, 10)
]];
},
/**
* @private
* @param {String} method Method name ("fillText" or "strokeText")
@ -687,7 +652,6 @@
fontStyle: this.fontStyle,
lineHeight: this.lineHeight,
textDecoration: this.textDecoration,
textShadow: this.textShadow,
textAlign: this.textAlign,
path: this.path,
backgroundColor: this.backgroundColor,
@ -744,35 +708,36 @@
* @return {Array}
*/
_getSVGShadows: function(lineTopOffset, textLines) {
var shadowSpans = [], j, i, jlen, ilen, lineTopOffsetMultiplier = 1;
var shadowSpans = [],
i, len,
lineTopOffsetMultiplier = 1;
if (!this._shadows || !this._boundaries) {
if (!this.shadow || !this._boundaries) {
return shadowSpans;
}
for (j = 0, jlen = this._shadows.length; j < jlen; j++) {
for (i = 0, ilen = textLines.length; i < ilen; i++) {
if (textLines[i] !== '') {
var lineLeftOffset = (this._boundaries && this._boundaries[i]) ? this._boundaries[i].left : 0;
shadowSpans.push(
'<tspan x="',
toFixed((lineLeftOffset + lineTopOffsetMultiplier) + this._shadowOffsets[j][0], 2),
((i === 0 || this.useNative) ? '" y' : '" dy'), '="',
toFixed(this.useNative
? ((lineTopOffset * i) - this.height / 2 + this._shadowOffsets[j][1])
: (lineTopOffset + (i === 0 ? this._shadowOffsets[j][1] : 0)), 2),
'" ',
this._getFillAttributes(this._shadows[j].color), '>',
fabric.util.string.escapeXml(textLines[i]),
'</tspan>');
lineTopOffsetMultiplier = 1;
} else {
// in some environments (e.g. IE 7 & 8) empty tspans are completely ignored, using a lineTopOffsetMultiplier
// prevents empty tspans
lineTopOffsetMultiplier++;
}
for (i = 0, len = textLines.length; i < len; i++) {
if (textLines[i] !== '') {
var lineLeftOffset = (this._boundaries && this._boundaries[i]) ? this._boundaries[i].left : 0;
shadowSpans.push(
'<tspan x="',
toFixed((lineLeftOffset + lineTopOffsetMultiplier) + this.shadow.offsetX, 2),
((i === 0 || this.useNative) ? '" y' : '" dy'), '="',
toFixed(this.useNative
? ((lineTopOffset * i) - this.height / 2 + this.shadow.offsetY)
: (lineTopOffset + (i === 0 ? this.shadow.offsetY : 0)), 2),
'" ',
this._getFillAttributes(this.shadow.color), '>',
fabric.util.string.escapeXml(textLines[i]),
'</tspan>');
lineTopOffsetMultiplier = 1;
} else {
// in some environments (e.g. IE 7 & 8) empty tspans are completely ignored, using a lineTopOffsetMultiplier
// prevents empty tspans
lineTopOffsetMultiplier++;
}
}
return shadowSpans;
},
@ -874,28 +839,20 @@
return this;
},
/**
* Returns actual text value of an instance
* @return {String}
*/
getText: function() {
return this.text;
},
/**
* Sets specified property to a specified value
* @param {String} name
* @param {String} key
* @param {Any} value
* @return {fabric.Text} thisArg
* @chainable
*/
_set: function(name, value) {
if (name === 'fontFamily' && this.path) {
_set: function(key, value) {
if (key === 'fontFamily' && this.path) {
this.path = this.path.replace(/(.*?)([^\/]*)(\.font\.js)/, '$1' + value + '$3');
}
this.callSuper('_set', name, value);
this.callSuper('_set', key, value);
if (name in this._dimensionAffectingProps) {
if (key in this._dimensionAffectingProps) {
this._initDimensions();
this.setCoords();
}

View file

@ -25,7 +25,7 @@ fabric.util.object.extend(fabric.Text.prototype, {
fontFamily: this.fontFamily,
fontWeight: this.fontWeight,
textDecoration: this.textDecoration,
textShadow: this.textShadow,
textShadow: this.shadow && this.shadow.getShadow(),
textAlign: this.textAlign,
fontStyle: this.fontStyle,
lineHeight: this.lineHeight,
@ -42,8 +42,6 @@ fabric.util.object.extend(fabric.Text.prototype, {
this._totalLineHeight = o.totalLineHeight;
this._fontAscent = o.fontAscent;
this._boundaries = o.boundaries;
this._shadowOffsets = o.shadowOffsets;
this._shadows = o.shadows || [ ];
el = null;

View file

@ -876,7 +876,7 @@
});
asyncTest('loadFromJSON with text', function() {
var json = '{"objects":[{"type":"text","left":150,"top":200,"width":128,"height":64.32,"fill":"#000000","overlayFill":"","stroke":"","strokeWidth":"","scaleX":0.8,"scaleY":0.8,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"text":"NAME HERE","fontSize":24,"fontWeight":"","fontFamily":"Delicious_500","fontStyle":"","lineHeight":"","textDecoration":"","textShadow":"","textAlign":"center","path":"","strokeStyle":"","backgroundColor":""}],"background":"#ffffff"}';
var json = '{"objects":[{"type":"text","left":150,"top":200,"width":128,"height":64.32,"fill":"#000000","overlayFill":"","stroke":"","strokeWidth":"","scaleX":0.8,"scaleY":0.8,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"text":"NAME HERE","fontSize":24,"fontWeight":"","fontFamily":"Delicious_500","fontStyle":"","lineHeight":"","textDecoration":"","textAlign":"center","path":"","strokeStyle":"","backgroundColor":""}],"background":"#ffffff"}';
canvas.loadFromJSON(json, function() {
canvas.renderAll();

View file

@ -58,6 +58,18 @@
equal(circle.getHeight(), 40);
});
test('set radius', function() {
var circle = new fabric.Circle();
circle.set('radius', 20);
equal(circle.getRadiusX(), 20);
equal(circle.getRadiusY(), 20);
equal(circle.getWidth(), 40);
equal(circle.getHeight(), 40);
});
test('complexity', function() {
var circle = new fabric.Circle();
ok(typeof circle.complexity == 'function');

View file

@ -939,6 +939,26 @@
equal(object.shadow.offsetY, 15);
});
test('set shadow', function() {
var object = new fabric.Object();
object.set('shadow', '10px 5px 0 #FF0000');
ok(object.shadow instanceof fabric.Shadow);
equal(object.shadow.color, '#FF0000');
equal(object.shadow.blur, 0);
equal(object.shadow.offsetX, 10);
equal(object.shadow.offsetY, 5);
object.set('shadow', null);
ok(!(object.shadow instanceof fabric.Shadow));
equal(object.shadow, null);
});
test('intersectsWithRect', function() {
var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 160 }),
point1 = new fabric.Point(0, 0),

View file

@ -2,6 +2,13 @@
QUnit.module('fabric.Shadow');
var REFERENCE_SHADOW_OBJECT = {
'color': 'rgb(0,255,0)',
'blur': 10,
'offsetX': 20,
'offsetY': 5
};
test('constructor', function() {
ok(fabric.Shadow);
@ -9,6 +16,101 @@
ok(shadow instanceof fabric.Shadow, 'should inherit from fabric.Shadow');
});
test('initializing with object', function() {
ok(fabric.Shadow);
var shadow = new fabric.Shadow(REFERENCE_SHADOW_OBJECT);
equal(shadow.color, 'rgb(0,255,0)');
equal(shadow.offsetX, 20);
equal(shadow.offsetY, 5);
equal(shadow.blur, 10);
});
test('initializing with string', function() {
ok(fabric.Shadow);
// old text-shadow definition - color offsetX offsetY blur
var shadow1 = new fabric.Shadow('rgba(0,0,255,0.5) 10px 20px 5px');
equal(shadow1.color, 'rgba(0,0,255,0.5)');
equal(shadow1.offsetX, 10);
equal(shadow1.offsetY, 20);
equal(shadow1.blur, 5);
var shadow2 = new fabric.Shadow('rgb(0,0,255) 10px 20px ');
equal(shadow2.color, 'rgb(0,0,255)');
equal(shadow2.offsetX, 10);
equal(shadow2.offsetY, 20);
equal(shadow2.blur, 0);
var shadow3 = new fabric.Shadow('#00FF00 30 10 ');
equal(shadow3.color, '#00FF00');
equal(shadow3.offsetX, 30);
equal(shadow3.offsetY, 10);
equal(shadow3.blur, 0);
var shadow4 = new fabric.Shadow(' #FF0000 10px');
equal(shadow4.color, '#FF0000');
equal(shadow4.offsetX, 10);
equal(shadow4.offsetY, 0);
equal(shadow4.blur, 0);
var shadow5 = new fabric.Shadow('#000000');
equal(shadow5.color, '#000000');
equal(shadow5.offsetX, 0);
equal(shadow5.offsetY, 0);
equal(shadow5.blur, 0);
// new text-shadow definition - offsetX offsetY blur color
var shadow6 = new fabric.Shadow('10px 20px 5px rgba(0,0,255,0.5)');
equal(shadow6.color, 'rgba(0,0,255,0.5)');
equal(shadow6.offsetX, 10);
equal(shadow6.offsetY, 20);
equal(shadow6.blur, 5);
var shadow7 = new fabric.Shadow('10 20 5px #00FF00');
equal(shadow7.color, '#00FF00');
equal(shadow7.offsetX, 10);
equal(shadow7.offsetY, 20);
equal(shadow7.blur, 5);
var shadow8 = new fabric.Shadow('10px 20px rgb(0,0,255)');
equal(shadow8.color, 'rgb(0,0,255)');
equal(shadow8.offsetX, 10);
equal(shadow8.offsetY, 20);
equal(shadow8.blur, 0);
var shadow9 = new fabric.Shadow(' 10px #FF0000 ');
equal(shadow9.color, '#FF0000');
equal(shadow9.offsetX, 10);
equal(shadow9.offsetY, 0);
equal(shadow9.blur, 0);
var shadow10 = new fabric.Shadow(' #FF0000 ');
equal(shadow10.color, '#FF0000');
equal(shadow10.offsetX, 0);
equal(shadow10.offsetY, 0);
equal(shadow10.blur, 0);
var shadow11 = new fabric.Shadow('');
equal(shadow11.color, 'rgb(0,0,0)');
equal(shadow11.offsetX, 0);
equal(shadow11.offsetY, 0);
equal(shadow11.blur, 0);
});
test('properties', function() {
var shadow = new fabric.Shadow();
@ -18,6 +120,13 @@
equal(shadow.offsetY, 0);
});
test('getShadow', function() {
var shadow = new fabric.Shadow();
ok(typeof shadow.getShadow == 'function');
equal(shadow.getShadow(), '0px 0px 0px rgb(0,0,0)');
});
test('toObject', function() {
var shadow = new fabric.Shadow();
ok(typeof shadow.toObject == 'function');
@ -26,9 +135,17 @@
equal(JSON.stringify(object), '{"color":"rgb(0,0,0)","blur":0,"offsetX":0,"offsetY":0}');
});
// TODO: implement and test this
// test('toSVG', function() {
//
// });
test('toSVG', function() {
// reset uid
fabric.Object.__uid = 0;
var shadow = new fabric.Shadow({color: '#FF0000', offsetX: 10, offsetY: -10, blur: 2});
var object = new fabric.Object({fill: '#FF0000'});
equal(shadow.toSVG(object), '<filter id="SVGID_0" y="-40%" height="180%"><feGaussianBlur in="SourceGraphic" stdDeviation="0.6666666666666666"></feGaussianBlur><feOffset dx="10" dy="-10"></feOffset><feMerge><feMergeNode></feMergeNode><feMergeNode in="SourceGraphic"></feMergeNode></feMerge></filter>');
shadow.color = '#000000';
equal(shadow.toSVG(object), '<filter id="SVGID_0" y="-40%" height="180%"><feGaussianBlur in="SourceAlpha" stdDeviation="0.6666666666666666"></feGaussianBlur><feOffset dx="10" dy="-10"></feOffset><feMerge><feMergeNode></feMergeNode><feMergeNode in="SourceGraphic"></feMergeNode></feMerge></filter>');
});
})();

View file

@ -44,7 +44,6 @@
'fontStyle': '',
'lineHeight': 1.3,
'textDecoration': '',
'textShadow': '',
'textAlign': 'left',
'path': null,
'backgroundColor': '',
@ -106,6 +105,18 @@
equal(text.get('fontFamily'), 'blah');
});
test('setShadow', function(){
var text = createTextObject();
ok(typeof text.setShadow == 'function');
equal(text.setShadow('10px 8px 2px red'), text, 'should be chainable');
ok(text.shadow instanceof fabric.Shadow, 'should inherit from fabric.Shadow');
equal(text.shadow.color, 'red');
equal(text.shadow.offsetX, 10);
equal(text.shadow.offsetY, 8);
equal(text.shadow.blur, 2);
});
test('setColor', function(){
var text = createTextObject();
ok(typeof text.setColor == 'function');