This commit is contained in:
Andrea Bogazzi 2016-12-18 11:05:13 +01:00 committed by GitHub
parent 5ab46d61de
commit ff21b07be4
10 changed files with 432 additions and 116 deletions

View file

@ -1,3 +1,15 @@
**Version 1.7.2**
- Fix: Textbox do not use stylemap for line wrapping [#3546](https://github.com/kangax/fabric.js/pull/3546)
- Fix: Fix for firing object:modfied in macOS sierra [#3539](https://github.com/kangax/fabric.js/pull/3539)
- Fix: Itext with object caching was not refreshing selection correctly. [#3538](https://github.com/kangax/fabric.js/pull/3538)
- Fix: stateful now works again with activeGroup and dinamyc swap between stateful false/true. [#3537](https://github.com/kangax/fabric.js/pull/3537)
- Fix: includeDefaultValues was not applied to child objects of groups and path-groups. [#3497](https://github.com/kangax/fabric.js/pull/3497)
- Fix: Itext style is cloned on paste action now, allow copie of styles to be independent. [#3502](https://github.com/kangax/fabric.js/pull/3502)
- Fix: Add subclasses properties to cacheProperties. [#3490](https://github.com/kangax/fabric.js/pull/3490)
- Add: Shift and Alt key used for transformations are now dinamic. [#3479](https://github.com/kangax/fabric.js/pull/3479)
- Fix: fix to polygon and cache. Added cacheProperties for all classes [#3490](https://github.com/kangax/fabric.js/pull/3490)
**Version 1.7.1**
- Add: Gradients/Patterns support customAttributes in toObject method [#3477](https://github.com/kangax/fabric.js/pull/3477)

View file

@ -1,6 +1,6 @@
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
var fabric = fabric || { version: "1.7.1" };
var fabric = fabric || { version: "1.7.2" };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}

View file

@ -5,16 +5,17 @@ Have a usage question?
======================
The issue tracker is only for bugs (with reproducible minimal test case) and feature requests, so please do the following if you have a question:
- Read the tutorial: http://fabricjs.com/articles/
- Read the docs: http://fabricjs.com/docs/
- Explore demos: http://fabricjs.com/demos/
- Read the tutorial: http://fabricjs.com/articles
- Read the object caching introduction: http://fabricjs.com/fabric-object-caching
- Read the docs: http://fabricjs.com/docs
- Explore demos: http://fabricjs.com/demos
- Look for/ask questions on StackOverflow: http://stackoverflow.com/questions/tagged/fabricjs
- Ask on Google Group: https://groups.google.com/forum/#!forum/fabricjs
Think you found a bug?
======================
The best bug report is a failing test in the repository as a pull request. Otherwise, please use the "BUG REPORT" template below.
The best bug report is a failing test in the repository as a pull request. Otherwise, please use the "BUG REPORT" template below. You NEED to create a fiddle with very simple test case that illustrate the problem. Otherwise the issue will be closed.
Have a feature request?
@ -24,7 +25,7 @@ Remove the template from below and provide thoughtful commentary *and code sampl
<!-- BUG TEMPLATE -->
## Version
1.7.0
1.7.2
## Test Case
http://jsfiddle.net/fabricjs/Da7SP/

288
dist/fabric.js vendored
View file

@ -1,7 +1,7 @@
/* build: `node build.js modules=ALL exclude=json,gestures minifier=uglifyjs` */
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
var fabric = fabric || { version: "1.7.1" };
var fabric = fabric || { version: "1.7.2" };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
@ -15,7 +15,9 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
else {
// assume we're running under node.js when document/window are not present
fabric.document = require("jsdom")
.jsdom("<!DOCTYPE html><html><head></head><body></body></html>");
.jsdom(
decodeURIComponent("%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E")
);
if (fabric.document.createWindow) {
fabric.window = fabric.document.createWindow();
@ -2594,6 +2596,57 @@ if (typeof console !== 'undefined') {
})();
(function() {
// Calculate an in-between color. Returns a "rgba()" string.
// Credit: Edwin Martin <edwin@bitstorm.org>
// http://www.bitstorm.org/jquery/color-animation/jquery.animate-colors.js
function calculateColor(begin, end, pos) {
var color = 'rgba('
+ parseInt((begin[0] + pos * (end[0] - begin[0])), 10) + ','
+ parseInt((begin[1] + pos * (end[1] - begin[1])), 10) + ','
+ parseInt((begin[2] + pos * (end[2] - begin[2])), 10);
color += ',' + (begin && end ? parseFloat(begin[3] + pos * (end[3] - begin[3])) : 1);
color += ')';
return color;
}
/**
* Changes the color from one to another within certain period of time, invoking callbacks as value is being changed.
* @memberOf fabric.util
* @param {String} fromColor The starting color in hex or rgb(a) format.
* @param {String} toColor The starting color in hex or rgb(a) format.
* @param {Number} [duration] Duration of change (in ms).
* @param {Object} [options] Animation options
* @param {Function} [options.onChange] Callback; invoked on every value change
* @param {Function} [options.onComplete] Callback; invoked when value change is completed
* @param {Function} [options.colorEasing] Easing function. Note that this function only take two arguments (currentTime, duration). Thus the regular animation easing functions cannot be used.
*/
function animateColor(fromColor, toColor, duration, options) {
var startColor = new fabric.Color(fromColor).getSource(),
endColor = new fabric.Color(toColor).getSource();
options = options || {};
fabric.util.animate(fabric.util.object.extend(options, {
duration: duration || 500,
startValue: startColor,
endValue: endColor,
byValue: endColor,
easing: function (currentTime, startValue, byValue, duration) {
var posValue = options.colorEasing
? options.colorEasing(currentTime, duration)
: 1 - Math.cos(currentTime / duration * (Math.PI / 2));
return calculateColor(startValue, byValue, posValue);
}
}));
}
fabric.util.animateColor = animateColor;
})();
(function() {
function normalize(a, c, p, s) {
@ -6666,14 +6719,14 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @chainable true
*/
setViewportTransform: function (vpt) {
var activeGroup = this._activeGroup, object;
var activeGroup = this._activeGroup, object, ingoreVpt = false, skipAbsolute = true;
this.viewportTransform = vpt;
for (var i = 0, len = this._objects.length; i < len; i++) {
object = this._objects[i];
object.group || object.setCoords();
object.group || object.setCoords(ingoreVpt, skipAbsolute);
}
if (activeGroup) {
activeGroup.setCoords();
activeGroup.setCoords(ingoreVpt, skipAbsolute);
}
this.renderAll();
return this;
@ -7093,11 +7146,13 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
* @private
*/
__serializeBgOverlay: function(methodName, propertiesToInclude) {
var data = {
background: (this.backgroundColor && this.backgroundColor.toObject)
var data = { }
if (this.backgroundColor) {
data.background = this.backgroundColor.toObject
? this.backgroundColor.toObject(propertiesToInclude)
: this.backgroundColor
};
}
if (this.overlayColor) {
data.overlay = this.overlayColor.toObject
@ -9299,7 +9354,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
angle = 360 + angle;
}
angle %= 360
angle %= 360;
if (t.target.snapAngle > 0) {
var snapAngle = t.target.snapAngle,
@ -9315,7 +9370,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
}
if (t.target.angle === angle) {
hasRoated = false
hasRoated = false;
}
}
@ -10246,6 +10301,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_shouldRender: function(target, pointer) {
var activeObject = this.getActiveGroup() || this.getActiveObject();
if (activeObject && activeObject.isEditing) {
// if we mouse up/down over a editing textbox a cursor change,
// there is no need to re render
return false;
}
return !!(
(target && (
target.isMoving ||
@ -10618,6 +10678,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
transform.reset = false;
transform.target.isMoving = true;
transform.shiftKey = e.shiftKey;
transform.altKey = e[this.centeredKey];
this._beforeScaleTransform(e, transform);
this._performTransformAction(e, transform, pointer);
@ -10660,7 +10722,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.setCursor(target.moveCursor || this.moveCursor);
}
}
transform.actionPerformed = actionPerformed;
transform.actionPerformed = transform.actionPerformed || actionPerformed;
},
/**
@ -12105,7 +12167,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* default to true
* since 1.7.0
* @type Boolean
* @default
* @default true
*/
objectCaching: objectCaching,
@ -12125,7 +12187,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* When `true`, cache does not get updated during scaling. The picture will get blocky if scaled
* too much and will be redrawn with correct details at the end of scaling.
* this setting is performance and application dependant.
* default to false
* default to true
* since 1.7.0
* @type Boolean
* @default true
@ -12134,6 +12196,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* When set to `true`, object's cache will be rerendered next render call.
* since 1.7.0
* @type Boolean
* @default false
*/
@ -12186,6 +12249,32 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this._updateCacheCanvas();
},
/**
* Return the dimension and the zoom level needed to create a cache canvas
* big enough to host the object to be cached.
* @private
* @return {Object}.width width of canvas
* @return {Object}.height height of canvas
* @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
* @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache
*/
_getCacheCanvasDimensions: function() {
var zoom = this.canvas && this.canvas.getZoom() || 1,
objectScale = this.getObjectScaling(),
dim = this._getNonTransformedDimensions(),
retina = this.canvas && this.canvas._isRetinaScaling() ? fabric.devicePixelRatio : 1,
zoomX = objectScale.scaleX * zoom * retina,
zoomY = objectScale.scaleY * zoom * retina,
width = dim.x * zoomX,
height = dim.y * zoomY;
return {
width: width,
height: height,
zoomX: zoomX,
zoomY: zoomY
};
},
/**
* Update width and height of the canvas for cache
* returns true or false if canvas needed resize.
@ -12199,14 +12288,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return false;
}
}
var zoom = this.getViewportTransform()[0],
objectScale = this.getObjectScaling(),
dim = this._getNonTransformedDimensions(),
retina = this.canvas && this.canvas._isRetinaScaling() ? fabric.devicePixelRatio : 1,
zoomX = objectScale.scaleX * zoom * retina,
zoomY = objectScale.scaleY * zoom * retina,
width = dim.x * zoomX,
height = dim.y * zoomY;
var dims = this._getCacheCanvasDimensions(),
width = dims.width, height = dims.height,
zoomX = dims.zoomX, zoomY = dims.zoomY;
if (width !== this.cacheWidth || height !== this.cacheHeight) {
this._cacheCanvas.width = width;
this._cacheCanvas.height = height;
@ -13796,12 +13881,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
tl = new fabric.Point(coords.x - offsetX, coords.y - offsetY),
tr = new fabric.Point(tl.x + (currentWidth * cosTh), tl.y + (currentWidth * sinTh)),
bl = new fabric.Point(tl.x - (currentHeight * sinTh), tl.y + (currentHeight * cosTh)),
br = new fabric.Point(coords.x + offsetX, coords.y + offsetY),
ml = new fabric.Point((tl.x + bl.x) / 2, (tl.y + bl.y) / 2),
mt = new fabric.Point((tr.x + tl.x) / 2, (tr.y + tl.y) / 2),
mr = new fabric.Point((br.x + tr.x) / 2, (br.y + tr.y) / 2),
mb = new fabric.Point((br.x + bl.x) / 2, (br.y + bl.y) / 2),
mtr = new fabric.Point(mt.x + sinTh * this.rotatingPointOffset, mt.y - cosTh * this.rotatingPointOffset);
br = new fabric.Point(coords.x + offsetX, coords.y + offsetY);
if (!ignoreVpt) {
var ml = new fabric.Point((tl.x + bl.x) / 2, (tl.y + bl.y) / 2),
mt = new fabric.Point((tr.x + tl.x) / 2, (tr.y + tl.y) / 2),
mr = new fabric.Point((br.x + tr.x) / 2, (br.y + tr.y) / 2),
mb = new fabric.Point((br.x + bl.x) / 2, (br.y + bl.y) / 2),
mtr = new fabric.Point(mt.x + sinTh * this.rotatingPointOffset, mt.y - cosTh * this.rotatingPointOffset);
}
// debugging
/* setTimeout(function() {
@ -13817,14 +13905,20 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
canvas.contextTop.fillRect(mtr.x, mtr.y, 3, 3);
}, 50); */
return {
var coords = {
// corners
tl: tl, tr: tr, br: br, bl: bl,
// middle
ml: ml, mt: mt, mr: mr, mb: mb,
// rotating point
mtr: mtr
};
if (!ignoreVpt) {
// middle
coords.ml = ml;
coords.mt = mt;
coords.mr = mr;
coords.mb = mb;
// rotating point
coords.mtr = mtr;
}
return coords;
},
/**
@ -13833,8 +13927,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @return {fabric.Object} thisArg
* @chainable
*/
setCoords: function(ignoreZoom) {
setCoords: function(ignoreZoom, skipAbsolute) {
this.oCoords = this.calcCoords(ignoreZoom);
if (!skipAbsolute && !ignoreZoom) {
this.absoluteCoords = this.calcCoords(true);
}
// set coordinates of the draggable boxes in the corners used to scale/rotate the image
ignoreZoom || this._setCornerCoords && this._setCornerCoords();
@ -14190,6 +14287,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
saveState: function(options) {
var propertySet = options && options.propertySet || originalSet,
destination = '_' + propertySet;
if (!this[destination]) {
return this.setupState(options);
}
saveProps(this, destination, this[propertySet]);
if (options && options.stateProperties) {
saveProps(this, destination, options.stateProperties);
@ -14908,6 +15008,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push(
'x1',
'x2',
'y1',
'y2'
);
/**
* Line class
* @class fabric.Line
@ -14951,6 +15059,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
y2: 0,
cacheProperties: cacheProperties,
/**
* Constructor
* @param {Array} [points] Array of points
@ -15260,6 +15370,11 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push(
'radius'
);
/**
* Circle class
* @class fabric.Circle
@ -15296,6 +15411,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
endAngle: pi * 2,
cacheProperties: cacheProperties,
/**
* Constructor
* @param {Object} [options] Options object
@ -15629,6 +15746,12 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push(
'rx',
'ry'
);
/**
* Ellipse class
* @class fabric.Ellipse
@ -15659,6 +15782,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
ry: 0,
cacheProperties: cacheProperties,
/**
* Constructor
* @param {Object} [options] Options object
@ -15839,7 +15964,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
}
var stateProperties = fabric.Object.prototype.stateProperties.concat();
stateProperties.push('rx', 'ry', 'x', 'y');
stateProperties.push('rx', 'ry');
/**
* Rectangle class
@ -16075,6 +16200,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push('points');
/**
* Polyline class
* @class fabric.Polyline
@ -16111,6 +16239,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
minY: 0,
cacheProperties: cacheProperties,
/**
* Constructor
* @param {Array} points Array of points (where each point is an object with x and y)
@ -16260,6 +16390,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push('points');
/**
* Polygon class
* @class fabric.Polygon
@ -16296,6 +16429,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
minY: 0,
cacheProperties: cacheProperties,
/**
* Constructor
* @param {Array} points Array of points
@ -16398,20 +16533,20 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @param {Boolean} noTransform
*/
commonRender: function(ctx, noTransform) {
var point, len = this.points.length;
var point, len = this.points.length,
x = noTransform ? 0 : this.pathOffset.x,
y = noTransform ? 0 : this.pathOffset.y;
if (!len || isNaN(this.points[len - 1].y)) {
// do not draw if no points or odd points
// NaN comes from parseFloat of a empty string in parser
return false;
}
noTransform || ctx.translate(-this.pathOffset.x, -this.pathOffset.y);
ctx.beginPath();
ctx.moveTo(this.points[0].x, this.points[0].y);
ctx.moveTo(this.points[0].x - x, this.points[0].y - y);
for (var i = 0; i < len; i++) {
point = this.points[i];
ctx.lineTo(point.x, point.y);
ctx.lineTo(point.x - x, point.y - y);
}
return true;
},
@ -16513,6 +16648,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push('path');
/**
* Path class
* @class fabric.Path
@ -16550,6 +16688,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
minY: 0,
cacheProperties: cacheProperties,
/**
* Constructor
* @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
@ -17464,9 +17604,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
invoke = fabric.util.array.invoke,
parentToObject = fabric.Object.prototype.toObject;
extend = fabric.util.object.extend;
if (fabric.PathGroup) {
fabric.warn('fabric.PathGroup is already defined');
@ -17614,8 +17752,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {Object} object representation of an instance
*/
toObject: function(propertiesToInclude) {
var o = extend(parentToObject.call(this, ['sourcePath'].concat(propertiesToInclude)), {
paths: invoke(this.getObjects(), 'toObject', propertiesToInclude)
var pathsToObject = this.paths.map(function(path) {
var originalDefaults = path.includeDefaultValues;
path.includeDefaultValues = path.group.includeDefaultValues;
var obj = path.toObject(propertiesToInclude);
path.includeDefaultValues = originalDefaults;
return obj;
});
var o = extend(this.callSuper('toObject', ['sourcePath'].concat(propertiesToInclude)), {
paths: pathsToObject
});
return o;
},
@ -17751,8 +17896,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
min = fabric.util.array.min,
max = fabric.util.array.max,
invoke = fabric.util.array.invoke;
max = fabric.util.array.max;
if (fabric.Group) {
return;
@ -18005,8 +18149,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {Object} object representation of an instance
*/
toObject: function(propertiesToInclude) {
var objsToObject = this.getObjects().map(function(obj) {
var originalDefaults = obj.includeDefaultValues;
obj.includeDefaultValues = obj.group.includeDefaultValues;
var _obj = obj.toObject(propertiesToInclude);
obj.includeDefaultValues = originalDefaults;
return _obj;
});
return extend(this.callSuper('toObject', propertiesToInclude), {
objects: invoke(this._objects, 'toObject', propertiesToInclude)
objects: objsToObject
});
},
@ -21573,6 +21724,22 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
'): { "text": "' + this.text + '", "fontFamily": "' + this.fontFamily + '" }>';
},
/**
* Return the dimension and the zoom level needed to create a cache canvas
* big enough to host the object to be cached.
* @private
* @return {Object}.width width of canvas
* @return {Object}.height height of canvas
* @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
* @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache
*/
_getCacheCanvasDimensions: function() {
var dim = this.callSuper('_getCacheCanvasDimensions');
dim.width += 2 * this.fontSize;
dim.height += 2 * this.fontSize;
return dim;
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
@ -22711,6 +22878,10 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
render: function(ctx, noTransform) {
this.clearContextTop();
this.callSuper('render', ctx, noTransform);
// clear the cursorOffsetCache, so we ensure to calculate once per renderCursor
// the correct position but not at every cursor animation.
this.cursorOffsetCache = { };
this.renderCursorOrSelection();
},
/**
@ -22720,10 +22891,6 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
_render: function(ctx) {
this.callSuper('_render', ctx);
this.ctx = ctx;
// clear the cursorOffsetCache, so we ensure to calculate once per renderCursor
// the correct position but not at every cursor animation.
this.cursorOffsetCache = { };
this.renderCursorOrSelection();
},
/**
@ -24196,7 +24363,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
}
for (var i = 0, len = _chars.length; i < len; i++) {
if (useCopiedStyle) {
style = fabric.copiedTextStyle[i];
style = fabric.util.object.clone(fabric.copiedTextStyle[i], true);
}
this.insertChar(_chars[i], i < len - 1, style);
}
@ -24785,7 +24952,14 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
e.stopImmediatePropagation();
e.preventDefault();
this.canvas && this.canvas.renderAll();
if (e.keyCode >= 33 && e.keyCode <= 40) {
// if i press an arrow key just update selection
this.clearContextTop();
this.renderCursorOrSelection();
}
else {
this.canvas && this.canvas.renderAll();
}
},
/**
@ -24898,12 +25072,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}
fabric.copiedText = selectedText;
fabric.copiedTextStyle = fabric.util.object.clone(
this.getSelectionStyles(
this.selectionStart,
this.selectionEnd
)
);
fabric.copiedTextStyle = this.getSelectionStyles(this.selectionStart, this.selectionEnd);
e.stopImmediatePropagation();
e.preventDefault();
this._copyDone = true;
@ -25797,6 +25966,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
_splitTextIntoLines: function(ctx) {
ctx = ctx || this.ctx;
var originalAlign = this.textAlign;
this._styleMap = null;
ctx.save();
this._setTextStyles(ctx);
this.textAlign = 'left';

17
dist/fabric.min.js vendored

File diff suppressed because one or more lines are too long

BIN
dist/fabric.min.js.gz vendored

Binary file not shown.

162
dist/fabric.require.js vendored
View file

@ -1,5 +1,5 @@
var fabric = fabric || {
version: "1.7.1"
version: "1.7.2"
};
if (typeof exports !== "undefined") {
@ -11,7 +11,7 @@ if (typeof document !== "undefined" && typeof window !== "undefined") {
fabric.window = window;
window.fabric = fabric;
} else {
fabric.document = require("jsdom").jsdom("<!DOCTYPE html><html><head></head><body></body></html>");
fabric.document = require("jsdom").jsdom(decodeURIComponent("%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E"));
if (fabric.document.createWindow) {
fabric.window = fabric.document.createWindow();
} else {
@ -1400,6 +1400,30 @@ if (typeof console !== "undefined") {
fabric.util.requestAnimFrame = requestAnimFrame;
})();
(function() {
function calculateColor(begin, end, pos) {
var color = "rgba(" + parseInt(begin[0] + pos * (end[0] - begin[0]), 10) + "," + parseInt(begin[1] + pos * (end[1] - begin[1]), 10) + "," + parseInt(begin[2] + pos * (end[2] - begin[2]), 10);
color += "," + (begin && end ? parseFloat(begin[3] + pos * (end[3] - begin[3])) : 1);
color += ")";
return color;
}
function animateColor(fromColor, toColor, duration, options) {
var startColor = new fabric.Color(fromColor).getSource(), endColor = new fabric.Color(toColor).getSource();
options = options || {};
fabric.util.animate(fabric.util.object.extend(options, {
duration: duration || 500,
startValue: startColor,
endValue: endColor,
byValue: endColor,
easing: function(currentTime, startValue, byValue, duration) {
var posValue = options.colorEasing ? options.colorEasing(currentTime, duration) : 1 - Math.cos(currentTime / duration * (Math.PI / 2));
return calculateColor(startValue, byValue, posValue);
}
}));
}
fabric.util.animateColor = animateColor;
})();
(function() {
function normalize(a, c, p, s) {
if (a < Math.abs(c)) {
@ -3385,14 +3409,14 @@ fabric.Pattern = fabric.util.createClass({
return this.viewportTransform[0];
},
setViewportTransform: function(vpt) {
var activeGroup = this._activeGroup, object;
var activeGroup = this._activeGroup, object, ingoreVpt = false, skipAbsolute = true;
this.viewportTransform = vpt;
for (var i = 0, len = this._objects.length; i < len; i++) {
object = this._objects[i];
object.group || object.setCoords();
object.group || object.setCoords(ingoreVpt, skipAbsolute);
}
if (activeGroup) {
activeGroup.setCoords();
activeGroup.setCoords(ingoreVpt, skipAbsolute);
}
this.renderAll();
return this;
@ -3597,9 +3621,10 @@ fabric.Pattern = fabric.util.createClass({
return object;
},
__serializeBgOverlay: function(methodName, propertiesToInclude) {
var data = {
background: this.backgroundColor && this.backgroundColor.toObject ? this.backgroundColor.toObject(propertiesToInclude) : this.backgroundColor
};
var data = {};
if (this.backgroundColor) {
data.background = this.backgroundColor.toObject ? this.backgroundColor.toObject(propertiesToInclude) : this.backgroundColor;
}
if (this.overlayColor) {
data.overlay = this.overlayColor.toObject ? this.overlayColor.toObject(propertiesToInclude) : this.overlayColor;
}
@ -5199,6 +5224,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, {
},
_shouldRender: function(target, pointer) {
var activeObject = this.getActiveGroup() || this.getActiveObject();
if (activeObject && activeObject.isEditing) {
return false;
}
return !!(target && (target.isMoving || target !== activeObject) || !target && !!activeObject || !target && !activeObject && !this._groupSelector || pointer && this._previousPointer && this.selection && (pointer.x !== this._previousPointer.x || pointer.y !== this._previousPointer.y));
},
__onMouseUp: function(e) {
@ -5401,6 +5429,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, {
var pointer = this.getPointer(e), transform = this._currentTransform;
transform.reset = false;
transform.target.isMoving = true;
transform.shiftKey = e.shiftKey;
transform.altKey = e[this.centeredKey];
this._beforeScaleTransform(e, transform);
this._performTransformAction(e, transform, pointer);
transform.actionPerformed && this.renderAll();
@ -5426,7 +5456,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, {
this.setCursor(target.moveCursor || this.moveCursor);
}
}
transform.actionPerformed = actionPerformed;
transform.actionPerformed = transform.actionPerformed || actionPerformed;
},
_fire: function(eventName, target, e) {
this.fire("object:" + eventName, {
@ -5881,6 +5911,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
this._cacheContext = this._cacheCanvas.getContext("2d");
this._updateCacheCanvas();
},
_getCacheCanvasDimensions: function() {
var zoom = this.canvas && this.canvas.getZoom() || 1, objectScale = this.getObjectScaling(), dim = this._getNonTransformedDimensions(), retina = this.canvas && this.canvas._isRetinaScaling() ? fabric.devicePixelRatio : 1, zoomX = objectScale.scaleX * zoom * retina, zoomY = objectScale.scaleY * zoom * retina, width = dim.x * zoomX, height = dim.y * zoomY;
return {
width: width,
height: height,
zoomX: zoomX,
zoomY: zoomY
};
},
_updateCacheCanvas: function() {
if (this.noScaleCache && this.canvas && this.canvas._currentTransform) {
var action = this.canvas._currentTransform.action;
@ -5888,7 +5927,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
return false;
}
}
var zoom = this.getViewportTransform()[0], objectScale = this.getObjectScaling(), dim = this._getNonTransformedDimensions(), retina = this.canvas && this.canvas._isRetinaScaling() ? fabric.devicePixelRatio : 1, zoomX = objectScale.scaleX * zoom * retina, zoomY = objectScale.scaleY * zoom * retina, width = dim.x * zoomX, height = dim.y * zoomY;
var dims = this._getCacheCanvasDimensions(), width = dims.width, height = dims.height, zoomX = dims.zoomX, zoomY = dims.zoomY;
if (width !== this.cacheWidth || height !== this.cacheHeight) {
this._cacheCanvas.width = width;
this._cacheCanvas.height = height;
@ -6664,21 +6703,30 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, {
return this.scale(value / this.height / boundingRectFactor);
},
calcCoords: function(ignoreVpt) {
var theta = degreesToRadians(this.angle), vpt = this.getViewportTransform(), dim = ignoreVpt ? this._getTransformedDimensions() : this._calculateCurrentDimensions(), currentWidth = dim.x, currentHeight = dim.y, sinTh = Math.sin(theta), cosTh = Math.cos(theta), _angle = currentWidth > 0 ? Math.atan(currentHeight / currentWidth) : 0, _hypotenuse = currentWidth / Math.cos(_angle) / 2, offsetX = Math.cos(_angle + theta) * _hypotenuse, offsetY = Math.sin(_angle + theta) * _hypotenuse, center = this.getCenterPoint(), coords = ignoreVpt ? center : fabric.util.transformPoint(center, vpt), tl = new fabric.Point(coords.x - offsetX, coords.y - offsetY), tr = new fabric.Point(tl.x + currentWidth * cosTh, tl.y + currentWidth * sinTh), bl = new fabric.Point(tl.x - currentHeight * sinTh, tl.y + currentHeight * cosTh), br = new fabric.Point(coords.x + offsetX, coords.y + offsetY), ml = new fabric.Point((tl.x + bl.x) / 2, (tl.y + bl.y) / 2), mt = new fabric.Point((tr.x + tl.x) / 2, (tr.y + tl.y) / 2), mr = new fabric.Point((br.x + tr.x) / 2, (br.y + tr.y) / 2), mb = new fabric.Point((br.x + bl.x) / 2, (br.y + bl.y) / 2), mtr = new fabric.Point(mt.x + sinTh * this.rotatingPointOffset, mt.y - cosTh * this.rotatingPointOffset);
return {
var theta = degreesToRadians(this.angle), vpt = this.getViewportTransform(), dim = ignoreVpt ? this._getTransformedDimensions() : this._calculateCurrentDimensions(), currentWidth = dim.x, currentHeight = dim.y, sinTh = Math.sin(theta), cosTh = Math.cos(theta), _angle = currentWidth > 0 ? Math.atan(currentHeight / currentWidth) : 0, _hypotenuse = currentWidth / Math.cos(_angle) / 2, offsetX = Math.cos(_angle + theta) * _hypotenuse, offsetY = Math.sin(_angle + theta) * _hypotenuse, center = this.getCenterPoint(), coords = ignoreVpt ? center : fabric.util.transformPoint(center, vpt), tl = new fabric.Point(coords.x - offsetX, coords.y - offsetY), tr = new fabric.Point(tl.x + currentWidth * cosTh, tl.y + currentWidth * sinTh), bl = new fabric.Point(tl.x - currentHeight * sinTh, tl.y + currentHeight * cosTh), br = new fabric.Point(coords.x + offsetX, coords.y + offsetY);
if (!ignoreVpt) {
var ml = new fabric.Point((tl.x + bl.x) / 2, (tl.y + bl.y) / 2), mt = new fabric.Point((tr.x + tl.x) / 2, (tr.y + tl.y) / 2), mr = new fabric.Point((br.x + tr.x) / 2, (br.y + tr.y) / 2), mb = new fabric.Point((br.x + bl.x) / 2, (br.y + bl.y) / 2), mtr = new fabric.Point(mt.x + sinTh * this.rotatingPointOffset, mt.y - cosTh * this.rotatingPointOffset);
}
var coords = {
tl: tl,
tr: tr,
br: br,
bl: bl,
ml: ml,
mt: mt,
mr: mr,
mb: mb,
mtr: mtr
bl: bl
};
if (!ignoreVpt) {
coords.ml = ml;
coords.mt = mt;
coords.mr = mr;
coords.mb = mb;
coords.mtr = mtr;
}
return coords;
},
setCoords: function(ignoreZoom) {
setCoords: function(ignoreZoom, skipAbsolute) {
this.oCoords = this.calcCoords(ignoreZoom);
if (!skipAbsolute && !ignoreZoom) {
this.absoluteCoords = this.calcCoords(true);
}
ignoreZoom || this._setCornerCoords && this._setCornerCoords();
return this;
},
@ -6841,6 +6889,9 @@ fabric.util.object.extend(fabric.Object.prototype, {
},
saveState: function(options) {
var propertySet = options && options.propertySet || originalSet, destination = "_" + propertySet;
if (!this[destination]) {
return this.setupState(options);
}
saveProps(this, destination, this[propertySet]);
if (options && options.stateProperties) {
saveProps(this, destination, options.stateProperties);
@ -7230,12 +7281,15 @@ fabric.util.object.extend(fabric.Object.prototype, {
fabric.warn("fabric.Line is already defined");
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push("x1", "x2", "y1", "y2");
fabric.Line = fabric.util.createClass(fabric.Object, {
type: "line",
x1: 0,
y1: 0,
x2: 0,
y2: 0,
cacheProperties: cacheProperties,
initialize: function(points, options) {
if (!points) {
points = [ 0, 0, 0, 0 ];
@ -7379,11 +7433,14 @@ fabric.util.object.extend(fabric.Object.prototype, {
fabric.warn("fabric.Circle is already defined.");
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push("radius");
fabric.Circle = fabric.util.createClass(fabric.Object, {
type: "circle",
radius: 0,
startAngle: 0,
endAngle: pi * 2,
cacheProperties: cacheProperties,
initialize: function(options) {
this.callSuper("initialize", options);
this.set("radius", options && options.radius || 0);
@ -7510,10 +7567,13 @@ fabric.util.object.extend(fabric.Object.prototype, {
fabric.warn("fabric.Ellipse is already defined.");
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push("rx", "ry");
fabric.Ellipse = fabric.util.createClass(fabric.Object, {
type: "ellipse",
rx: 0,
ry: 0,
cacheProperties: cacheProperties,
initialize: function(options) {
this.callSuper("initialize", options);
this.set("rx", options && options.rx || 0);
@ -7591,7 +7651,7 @@ fabric.util.object.extend(fabric.Object.prototype, {
return;
}
var stateProperties = fabric.Object.prototype.stateProperties.concat();
stateProperties.push("rx", "ry", "x", "y");
stateProperties.push("rx", "ry");
fabric.Rect = fabric.util.createClass(fabric.Object, {
stateProperties: stateProperties,
type: "rect",
@ -7681,11 +7741,14 @@ fabric.util.object.extend(fabric.Object.prototype, {
fabric.warn("fabric.Polyline is already defined");
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push("points");
fabric.Polyline = fabric.util.createClass(fabric.Object, {
type: "polyline",
points: null,
minX: 0,
minY: 0,
cacheProperties: cacheProperties,
initialize: function(points, options) {
return fabric.Polygon.prototype.initialize.call(this, points, options);
},
@ -7741,11 +7804,14 @@ fabric.util.object.extend(fabric.Object.prototype, {
fabric.warn("fabric.Polygon is already defined");
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push("points");
fabric.Polygon = fabric.util.createClass(fabric.Object, {
type: "polygon",
points: null,
minX: 0,
minY: 0,
cacheProperties: cacheProperties,
initialize: function(points, options) {
options = options || {};
this.points = points || [];
@ -7796,16 +7862,15 @@ fabric.util.object.extend(fabric.Object.prototype, {
}
},
commonRender: function(ctx, noTransform) {
var point, len = this.points.length;
var point, len = this.points.length, x = noTransform ? 0 : this.pathOffset.x, y = noTransform ? 0 : this.pathOffset.y;
if (!len || isNaN(this.points[len - 1].y)) {
return false;
}
noTransform || ctx.translate(-this.pathOffset.x, -this.pathOffset.y);
ctx.beginPath();
ctx.moveTo(this.points[0].x, this.points[0].y);
ctx.moveTo(this.points[0].x - x, this.points[0].y - y);
for (var i = 0; i < len; i++) {
point = this.points[i];
ctx.lineTo(point.x, point.y);
ctx.lineTo(point.x - x, point.y - y);
}
return true;
},
@ -7853,11 +7918,14 @@ fabric.util.object.extend(fabric.Object.prototype, {
fabric.warn("fabric.Path is already defined");
return;
}
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push("path");
fabric.Path = fabric.util.createClass(fabric.Object, {
type: "path",
path: null,
minX: 0,
minY: 0,
cacheProperties: cacheProperties,
initialize: function(path, options) {
options = options || {};
if (options) {
@ -8366,7 +8434,7 @@ fabric.util.object.extend(fabric.Object.prototype, {
(function(global) {
"use strict";
var fabric = global.fabric || (global.fabric = {}), extend = fabric.util.object.extend, invoke = fabric.util.array.invoke, parentToObject = fabric.Object.prototype.toObject;
var fabric = global.fabric || (global.fabric = {}), extend = fabric.util.object.extend;
if (fabric.PathGroup) {
fabric.warn("fabric.PathGroup is already defined");
return;
@ -8462,8 +8530,15 @@ fabric.util.object.extend(fabric.Object.prototype, {
return this.callSuper("_set", prop, value);
},
toObject: function(propertiesToInclude) {
var o = extend(parentToObject.call(this, [ "sourcePath" ].concat(propertiesToInclude)), {
paths: invoke(this.getObjects(), "toObject", propertiesToInclude)
var pathsToObject = this.paths.map(function(path) {
var originalDefaults = path.includeDefaultValues;
path.includeDefaultValues = path.group.includeDefaultValues;
var obj = path.toObject(propertiesToInclude);
path.includeDefaultValues = originalDefaults;
return obj;
});
var o = extend(this.callSuper("toObject", [ "sourcePath" ].concat(propertiesToInclude)), {
paths: pathsToObject
});
return o;
},
@ -8526,7 +8601,7 @@ fabric.util.object.extend(fabric.Object.prototype, {
(function(global) {
"use strict";
var fabric = global.fabric || (global.fabric = {}), extend = fabric.util.object.extend, min = fabric.util.array.min, max = fabric.util.array.max, invoke = fabric.util.array.invoke;
var fabric = global.fabric || (global.fabric = {}), extend = fabric.util.object.extend, min = fabric.util.array.min, max = fabric.util.array.max;
if (fabric.Group) {
return;
}
@ -8656,8 +8731,15 @@ fabric.util.object.extend(fabric.Object.prototype, {
this.callSuper("_set", key, value);
},
toObject: function(propertiesToInclude) {
var objsToObject = this.getObjects().map(function(obj) {
var originalDefaults = obj.includeDefaultValues;
obj.includeDefaultValues = obj.group.includeDefaultValues;
var _obj = obj.toObject(propertiesToInclude);
obj.includeDefaultValues = originalDefaults;
return _obj;
});
return extend(this.callSuper("toObject", propertiesToInclude), {
objects: invoke(this._objects, "toObject", propertiesToInclude)
objects: objsToObject
});
},
render: function(ctx) {
@ -10091,6 +10173,12 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
toString: function() {
return "#<fabric.Text (" + this.complexity() + '): { "text": "' + this.text + '", "fontFamily": "' + this.fontFamily + '" }>';
},
_getCacheCanvasDimensions: function() {
var dim = this.callSuper("_getCacheCanvasDimensions");
dim.width += 2 * this.fontSize;
dim.height += 2 * this.fontSize;
return dim;
},
_render: function(ctx) {
this._setTextStyles(ctx);
if (this.group && this.group.type === "path-group") {
@ -10585,12 +10673,12 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
render: function(ctx, noTransform) {
this.clearContextTop();
this.callSuper("render", ctx, noTransform);
this.cursorOffsetCache = {};
this.renderCursorOrSelection();
},
_render: function(ctx) {
this.callSuper("_render", ctx);
this.ctx = ctx;
this.cursorOffsetCache = {};
this.renderCursorOrSelection();
},
clearContextTop: function() {
if (!this.active || !this.isEditing) {
@ -11455,7 +11543,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
}
for (var i = 0, len = _chars.length; i < len; i++) {
if (useCopiedStyle) {
style = fabric.copiedTextStyle[i];
style = fabric.util.object.clone(fabric.copiedTextStyle[i], true);
}
this.insertChar(_chars[i], i < len - 1, style);
}
@ -11813,7 +11901,12 @@ fabric.util.object.extend(fabric.IText.prototype, {
}
e.stopImmediatePropagation();
e.preventDefault();
this.canvas && this.canvas.renderAll();
if (e.keyCode >= 33 && e.keyCode <= 40) {
this.clearContextTop();
this.renderCursorOrSelection();
} else {
this.canvas && this.canvas.renderAll();
}
},
onKeyUp: function(e) {
if (!this.isEditing || this._copyDone) {
@ -11878,7 +11971,7 @@ fabric.util.object.extend(fabric.IText.prototype, {
clipboardData.setData("text", selectedText);
}
fabric.copiedText = selectedText;
fabric.copiedTextStyle = fabric.util.object.clone(this.getSelectionStyles(this.selectionStart, this.selectionEnd));
fabric.copiedTextStyle = this.getSelectionStyles(this.selectionStart, this.selectionEnd);
e.stopImmediatePropagation();
e.preventDefault();
this._copyDone = true;
@ -12306,6 +12399,7 @@ fabric.util.object.extend(fabric.IText.prototype, {
_splitTextIntoLines: function(ctx) {
ctx = ctx || this.ctx;
var originalAlign = this.textAlign;
this._styleMap = null;
ctx.save();
this._setTextStyles(ctx);
this.textAlign = "left";

View file

@ -2,7 +2,7 @@
"name": "fabric",
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
"homepage": "http://fabricjs.com/",
"version": "1.7.1",
"version": "1.7.2",
"author": "Juriy Zaytsev <kangax@gmail.com>",
"contributors": [
{

View file

@ -841,6 +841,32 @@
this._updateCacheCanvas();
},
/**
* Return the dimension and the zoom level needed to create a cache canvas
* big enough to host the object to be cached.
* @private
* @return {Object}.width width of canvas
* @return {Object}.height height of canvas
* @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
* @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache
*/
_getCacheCanvasDimensions: function() {
var zoom = this.canvas && this.canvas.getZoom() || 1,
objectScale = this.getObjectScaling(),
dim = this._getNonTransformedDimensions(),
retina = this.canvas && this.canvas._isRetinaScaling() ? fabric.devicePixelRatio : 1,
zoomX = objectScale.scaleX * zoom * retina,
zoomY = objectScale.scaleY * zoom * retina,
width = dim.x * zoomX,
height = dim.y * zoomY;
return {
width: width,
height: height,
zoomX: zoomX,
zoomY: zoomY
};
},
/**
* Update width and height of the canvas for cache
* returns true or false if canvas needed resize.
@ -854,14 +880,10 @@
return false;
}
}
var zoom = this.getViewportTransform()[0],
objectScale = this.getObjectScaling(),
dim = this._getNonTransformedDimensions(),
retina = this.canvas && this.canvas._isRetinaScaling() ? fabric.devicePixelRatio : 1,
zoomX = objectScale.scaleX * zoom * retina,
zoomY = objectScale.scaleY * zoom * retina,
width = dim.x * zoomX,
height = dim.y * zoomY;
var dims = this._getCacheCanvasDimensions(),
width = dims.width, height = dims.height,
zoomX = dims.zoomX, zoomY = dims.zoomY;
if (width !== this.cacheWidth || height !== this.cacheHeight) {
this._cacheCanvas.width = width;
this._cacheCanvas.height = height;

View file

@ -393,6 +393,22 @@
'): { "text": "' + this.text + '", "fontFamily": "' + this.fontFamily + '" }>';
},
/**
* Return the dimension and the zoom level needed to create a cache canvas
* big enough to host the object to be cached.
* @private
* @return {Object}.width width of canvas
* @return {Object}.height height of canvas
* @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
* @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache
*/
_getCacheCanvasDimensions: function() {
var dim = this.callSuper('_getCacheCanvasDimensions');
dim.width += 2 * this.fontSize;
dim.height += 2 * this.fontSize;
return dim;
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on