mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-05-16 17:51:07 +00:00
[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:
parent
eeb5fc1400
commit
8c74f2b335
15 changed files with 399 additions and 264 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 = '';
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -172,7 +172,6 @@
|
|||
fontStyle: true,
|
||||
lineHeight: true,
|
||||
textDecoration: true,
|
||||
textShadow: true,
|
||||
textAlign: true,
|
||||
backgroundColor: true
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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>');
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
@ -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');
|
||||
|
|
|
|||
Loading…
Reference in a new issue