Fix some caching in group issues related to deep checks (#4032)

* backport
* better fix
This commit is contained in:
Andrea Bogazzi 2017-06-24 23:10:55 +02:00 committed by GitHub
parent efc2a216c7
commit 7a88afb2d3
5 changed files with 54 additions and 26 deletions

View file

@ -15,35 +15,34 @@
}
function _isEqual(origValue, currentValue, firstPass) {
if (!fabric.isLikelyNode && origValue instanceof Element) {
// avoid checking deep html elements
return origValue === currentValue;
if (origValue === currentValue) {
// if the objects are identical, return
return true;
}
else if (origValue instanceof Array) {
else if (Array.isArray(origValue)) {
if (origValue.length !== currentValue.length) {
return false;
}
for (var i = 0, len = origValue.length; i < len; i++) {
if (origValue[i] !== currentValue[i]) {
if (!_isEqual(origValue[i], currentValue[i])) {
return false;
}
}
return true;
}
else if (origValue && typeof origValue === 'object') {
if (!firstPass && Object.keys(origValue).length !== Object.keys(currentValue).length) {
var keys = Object.keys(origValue), key;
if (!firstPass && keys.length !== Object.keys(currentValue).length) {
return false;
}
for (var key in origValue) {
for (var i = 0, len = keys.length; i < len; i++) {
key = keys[i];
if (!_isEqual(origValue[key], currentValue[key])) {
return false;
}
}
return true;
}
else {
return origValue === currentValue;
}
}
@ -56,11 +55,11 @@
*/
hasStateChanged: function(propertySet) {
propertySet = propertySet || originalSet;
propertySet = '_' + propertySet;
if (!Object.keys(this[propertySet]).length) {
var dashedPropertySet = '_' + propertySet;
if (Object.keys(this[dashedPropertySet]).length < this[propertySet].length) {
return true;
}
return !_isEqual(this[propertySet], this, true);
return !_isEqual(this[dashedPropertySet], this, true);
},
/**

View file

@ -54,6 +54,13 @@
*/
subTargetCheck: false,
/**
* Groups are container, do not render anything on theyr own, ence no cache properties
* @type Boolean
* @default
*/
cacheProperties: [],
/**
* Constructor
* @param {Object} objects Group objects
@ -373,8 +380,11 @@
}
for (var i = 0, len = this._objects.length; i < len; i++) {
if (this._objects[i].isCacheDirty(true)) {
var dim = this._getNonTransformedDimensions();
this._cacheContext.clearRect(-dim.x / 2, -dim.y / 2, dim.x, dim.y);
if (this._cacheCanvas) {
// if this group has not a cache canvas there is nothing to clean
var x = this.cacheWidth / this.zoomX, y = this.cacheHeight / this.zoomY;
this._cacheContext.clearRect(-x / 2, -y / 2, x, y);
}
return true;
}
}

View file

@ -806,8 +806,8 @@
stateProperties: (
'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' +
'angle opacity fill fillRule globalCompositeOperation shadow clipTo visible backgroundColor ' +
'skewX skewY'
'angle opacity fill globalCompositeOperation shadow clipTo visible backgroundColor ' +
'skewX skewY fillRule'
).split(' '),
/**
@ -815,8 +815,8 @@
* @type Array
*/
cacheProperties: (
'fill stroke strokeWidth strokeDashArray width height stroke strokeWidth strokeDashArray' +
' strokeLineCap strokeLineJoin strokeMiterLimit fillRule backgroundColor'
'fill stroke strokeWidth strokeDashArray width height' +
' strokeLineCap strokeLineJoin strokeMiterLimit backgroundColor'
).split(' '),
/**
@ -1108,7 +1108,7 @@
* Retrieves viewportTransform from Object's canvas if possible
* @method getViewportTransform
* @memberOf fabric.Object.prototype
* @return {Boolean} flipY value // TODO
* @return {Boolean}
*/
getViewportTransform: function() {
if (this.canvas && this.canvas.viewportTransform) {
@ -1117,13 +1117,23 @@
return fabric.iMatrix.concat();
},
/*
* @private
* return if the object would be visible in rendering
* @memberOf fabric.Object.prototype
* @return {Boolean}
*/
isNotVisible: function() {
return this.opacity === 0 || (this.width === 0 && this.height === 0) || !this.visible;
},
/**
* Renders an object on a specified context
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
render: function(ctx) {
// do not render if width/height are zeros or object is not visible
if ((this.width === 0 && this.height === 0) || !this.visible) {
if (this.isNotVisible()) {
return;
}
if (this.canvas && this.canvas.skipOffscreen && !this.group && !this.isOnScreen()) {
@ -1152,6 +1162,7 @@
this.drawCacheOnCanvas(ctx);
}
else {
this.dirty = false;
this.drawObject(ctx);
if (this.objectCaching && this.statefullCache) {
this.saveState({ propertySet: 'cacheProperties' });
@ -1221,13 +1232,16 @@
* on parent canvas.
*/
isCacheDirty: function(skipCanvas) {
if (!skipCanvas && this._updateCacheCanvas()) {
if (this.isNotVisible()) {
return false;
}
if (this._cacheCanvas && !skipCanvas && this._updateCacheCanvas()) {
// in this case the context is already cleared.
return true;
}
else {
if (this.dirty || (this.statefullCache && this.hasStateChanged('cacheProperties'))) {
if (!skipCanvas) {
if (this._cacheCanvas && !skipCanvas) {
var width = this.cacheWidth / this.zoomX;
var height = this.cacheHeight / this.zoomY;
this._cacheContext.clearRect(-width / 2, -height / 2, width, height);

View file

@ -29,8 +29,11 @@
return;
}
var stateProperties = fabric.Object.prototype.stateProperties.concat();
stateProperties.push('path');
var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
cacheProperties.push('path');
cacheProperties.push('path', 'fillRule');
/**
* Path class
@ -71,6 +74,8 @@
cacheProperties: cacheProperties,
stateProperties: stateProperties,
/**
* Constructor
* @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)

View file

@ -1156,7 +1156,7 @@
});
test('isCacheDirty statefullCache disabled', function() {
var object = new fabric.Object({ scaleX: 3, scaleY: 2});
var object = new fabric.Object({ scaleX: 3, scaleY: 2, width: 1, height: 2});
equal(object.dirty, true, 'object is dirty after creation');
object.cacheProperties = ['propA', 'propB'];
object.dirty = false;
@ -1168,7 +1168,7 @@
});
test('isCacheDirty statefullCache enabled', function() {
var object = new fabric.Object({ scaleX: 3, scaleY: 2});
var object = new fabric.Object({ scaleX: 3, scaleY: 2, width: 1, height: 2});
object.cacheProperties = ['propA', 'propB'];
object.dirty = false;
object.statefullCache = true;