absolute transforms are now cached

This commit is contained in:
Eric Rowell
2013-08-10 00:58:53 -07:00
parent a0a2d9a676
commit d902e6dca0
4 changed files with 109 additions and 48 deletions

View File

@@ -218,6 +218,10 @@
} }
}, },
_applyAncestorTransforms: function(node) { _applyAncestorTransforms: function(node) {
var m = node.getAbsoluteTransform().getMatrix();
this.context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
/*
var context = this.context, var context = this.context,
t, m; t, m;
@@ -226,6 +230,7 @@
m = t.getMatrix(); m = t.getMatrix();
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}, true); }, true);
*/
}, },
_clip: function(container) { _clip: function(container) {
var context = this.getContext(); var context = this.getContext();

View File

@@ -1,6 +1,7 @@
(function() { (function() {
// CONSTANTS // CONSTANTS
var ADD = 'add', var ABSOLUTE_TRANSFORM = 'absoluteTransform',
ADD = 'add',
B = 'b', B = 'b',
BEFORE = 'before', BEFORE = 'before',
BLACK = 'black', BLACK = 'black',
@@ -34,20 +35,29 @@
UPPER_Y = 'Y', UPPER_Y = 'Y',
VISIBLE = 'visible', VISIBLE = 'visible',
X = 'x', X = 'x',
Y = 'y', Y = 'y';
CACHE_MAP = { function _clearTransformCacheEachChild(node) {
x: TRANSFORM, _clearTransformCache.call(node);
y: TRANSFORM, }
rotation: TRANSFORM, function _clearTransformCache() {
rotationDeg: TRANSFORM, this._clearCache(TRANSFORM);
scaleX: TRANSFORM, this._clearCache(ABSOLUTE_TRANSFORM);
scaleY: TRANSFORM,
skewX: TRANSFORM,
skewY: TRANSFORM,
visible: VISIBLE if (this.children) {
}; this.getChildren().each(_clearTransformCacheEachChild);
}
}
function _clearVisibleCacheEachChild(node) {
_clearVisibleCache.call(node);
}
function _clearVisibleCache() {
this._clearCache(VISIBLE);
if (this.children) {
this.getChildren().each(_clearVisibleCacheEachChild);
}
}
Kinetic.Util.addMethods(Kinetic.Node, { Kinetic.Util.addMethods(Kinetic.Node, {
_init: function(config) { _init: function(config) {
@@ -58,10 +68,7 @@
this.setAttrs(config); this.setAttrs(config);
}, },
_clearCache: function(attr){ _clearCache: function(attr){
var cacheAttr = CACHE_MAP[attr]; delete this.cache[attr];
if (cacheAttr) {
delete this.cache[cacheAttr];
}
}, },
_getCache: function(attr, privateGetter){ _getCache: function(attr, privateGetter){
var cache = this.cache[attr]; var cache = this.cache[attr];
@@ -234,6 +241,26 @@
return this.attrs[attr]; return this.attrs[attr];
} }
}, },
/**
* get ancestors
* @method
* @memberof Kinetic.Node.prototype
* @example
* shape.getAncestors().each(function(node) {
* console.log(node.getId());
* })
*/
getAncestors: function() {
var parent = this.getParent(),
ancestors = new Kinetic.Collection();
while (parent) {
ancestors.push(parent);
parent = parent.getParent();
}
return ancestors;
},
/** /**
* set attr * set attr
* @method * @method
@@ -758,6 +785,9 @@
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
*/ */
getAbsoluteTransform: function() { getAbsoluteTransform: function() {
return this._getCache(ABSOLUTE_TRANSFORM, this._getAbsoluteTransform);
},
_getAbsoluteTransform: function() {
// absolute transform // absolute transform
var am = new Kinetic.Transform(), var am = new Kinetic.Transform(),
m; m;
@@ -1153,28 +1183,28 @@
}); });
// getter setter adders // getter setter adders
Kinetic.Node.addGetterSetter = function(constructor, attr, def) { Kinetic.Node.addGetterSetter = function(constructor, attr, def, before) {
this.addGetter(constructor, attr, def); this.addGetter(constructor, attr, def);
this.addSetter(constructor, attr); this.addSetter(constructor, attr, before);
}; };
Kinetic.Node.addPointGetterSetter = function(constructor, attr, def) { Kinetic.Node.addPointGetterSetter = function(constructor, attr, def, before) {
this.addPointGetter(constructor, attr); this.addPointGetter(constructor, attr, def);
this.addPointSetter(constructor, attr); this.addPointSetter(constructor, attr, before);
// add invdividual component getters and setters // add invdividual component getters and setters
this.addGetter(constructor, attr + UPPER_X, def); this.addGetter(constructor, attr + UPPER_X, def);
this.addGetter(constructor, attr + UPPER_Y, def); this.addGetter(constructor, attr + UPPER_Y, def);
this.addSetter(constructor, attr + UPPER_X); this.addSetter(constructor, attr + UPPER_X, before);
this.addSetter(constructor, attr + UPPER_Y); this.addSetter(constructor, attr + UPPER_Y, before);
}; };
Kinetic.Node.addPointsGetterSetter = function(constructor, attr) { Kinetic.Node.addPointsGetterSetter = function(constructor, attr) {
this.addPointsGetter(constructor, attr); this.addPointsGetter(constructor, attr);
this.addPointsSetter(constructor, attr); this.addPointsSetter(constructor, attr);
this.addPointAdder(constructor, attr); this.addPointAdder(constructor, attr);
}; };
Kinetic.Node.addRotationGetterSetter = function(constructor, attr, def) { Kinetic.Node.addRotationGetterSetter = function(constructor, attr, def, before) {
this.addRotationGetter(constructor, attr, def); this.addRotationGetter(constructor, attr, def);
this.addRotationSetter(constructor, attr); this.addRotationSetter(constructor, attr, before);
}; };
Kinetic.Node.addColorGetterSetter = function(constructor, attr) { Kinetic.Node.addColorGetterSetter = function(constructor, attr) {
this.addGetter(constructor, attr); this.addGetter(constructor, attr);
@@ -1289,16 +1319,17 @@
this._setAttr('points', points); this._setAttr('points', points);
}; };
}; };
Kinetic.Node.addSetter = function(constructor, attr) { Kinetic.Node.addSetter = function(constructor, attr, before) {
var that = this, var method = SET + Kinetic.Util._capitalize(attr);
method = SET + Kinetic.Util._capitalize(attr);
constructor.prototype[method] = function(val) { constructor.prototype[method] = function(val) {
this._clearCache(attr); if (before) {
before.call(this);
}
this._setAttr(attr, val); this._setAttr(attr, val);
}; };
}; };
Kinetic.Node.addPointSetter = function(constructor, attr) { Kinetic.Node.addPointSetter = function(constructor, attr, before) {
var that = this, var that = this,
baseMethod = SET + Kinetic.Util._capitalize(attr); baseMethod = SET + Kinetic.Util._capitalize(attr);
@@ -1313,6 +1344,9 @@
y = pos.y; y = pos.y;
this._fireBeforeChangeEvent(attr, oldVal, pos); this._fireBeforeChangeEvent(attr, oldVal, pos);
if (before) {
before.call(this);
}
if (x !== undefined) { if (x !== undefined) {
this[baseMethod + UPPER_X](x); this[baseMethod + UPPER_X](x);
} }
@@ -1323,18 +1357,22 @@
} }
}; };
}; };
Kinetic.Node.addRotationSetter = function(constructor, attr) { Kinetic.Node.addRotationSetter = function(constructor, attr, before) {
var that = this, var that = this,
method = SET + Kinetic.Util._capitalize(attr); method = SET + Kinetic.Util._capitalize(attr);
// radians // radians
constructor.prototype[method] = function(val) { constructor.prototype[method] = function(val) {
this._clearCache(attr); if (before) {
before.call(this);
}
this._setAttr(attr, val); this._setAttr(attr, val);
}; };
// degrees // degrees
constructor.prototype[method + DEG] = function(deg) { constructor.prototype[method + DEG] = function(deg) {
this._clearCache(attr); if (before) {
before.call(this);
}
this._setAttr(attr, Kinetic.Util._degToRad(deg)); this._setAttr(attr, Kinetic.Util._degToRad(deg));
}; };
}; };
@@ -1394,7 +1432,7 @@
}; };
// add getters setters // add getters setters
Kinetic.Node.addGetterSetter(Kinetic.Node, 'x', 0); Kinetic.Node.addGetterSetter(Kinetic.Node, 'x', 0, _clearTransformCache);
/** /**
* set x position * set x position
@@ -1411,7 +1449,7 @@
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
*/ */
Kinetic.Node.addGetterSetter(Kinetic.Node, 'y', 0); Kinetic.Node.addGetterSetter(Kinetic.Node, 'y', 0, _clearTransformCache);
/** /**
* set y position * set y position
@@ -1465,7 +1503,7 @@
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
*/ */
Kinetic.Node.addRotationGetterSetter(Kinetic.Node, 'rotation', 0); Kinetic.Node.addRotationGetterSetter(Kinetic.Node, 'rotation', 0, _clearTransformCache);
/** /**
* set rotation in radians * set rotation in radians
@@ -1497,7 +1535,7 @@
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
*/ */
Kinetic.Node.addPointGetterSetter(Kinetic.Node, 'scale', 1); Kinetic.Node.addPointGetterSetter(Kinetic.Node, 'scale', 1, _clearTransformCache);
/** /**
* set scale * set scale
@@ -1558,7 +1596,7 @@
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
*/ */
Kinetic.Node.addPointGetterSetter(Kinetic.Node, 'skew', 0); Kinetic.Node.addPointGetterSetter(Kinetic.Node, 'skew', 0, _clearTransformCache);
/** /**
* set skew * set skew
@@ -1620,7 +1658,7 @@
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
*/ */
Kinetic.Node.addPointGetterSetter(Kinetic.Node, 'offset', 0); Kinetic.Node.addPointGetterSetter(Kinetic.Node, 'offset', 0, _clearTransformCache);
/** /**
* set offset. A node's offset defines the position and rotation point * set offset. A node's offset defines the position and rotation point
@@ -1719,7 +1757,7 @@
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
*/ */
Kinetic.Node.addSetter(Kinetic.Node, 'visible'); Kinetic.Node.addSetter(Kinetic.Node, 'visible', _clearVisibleCache);
/** /**
* set visible * set visible

View File

@@ -1,4 +1,6 @@
(function() { (function() {
var HAS_SHADOW = 'hasShadow';
function _fillFunc(context) { function _fillFunc(context) {
context.fill(); context.fill();
} }
@@ -11,6 +13,9 @@
function _strokeFuncHit(context) { function _strokeFuncHit(context) {
context.stroke(); context.stroke();
} }
function _clearHasShadowCache() {
this._clearCache(HAS_SHADOW);
}
Kinetic.Util.addMethods(Kinetic.Shape, { Kinetic.Util.addMethods(Kinetic.Shape, {
__init: function(config) { __init: function(config) {
@@ -67,6 +72,9 @@
* @memberof Kinetic.Shape.prototype * @memberof Kinetic.Shape.prototype
*/ */
hasShadow: function() { hasShadow: function() {
return this._getCache(HAS_SHADOW, this._hasShadow);
},
_hasShadow: function() {
return (this.getShadowOpacity() !== 0 && !!(this.getShadowColor() || this.getShadowBlur() || this.getShadowOffsetX() || this.getShadowOffsetY())); return (this.getShadowOpacity() !== 0 && !!(this.getShadowColor() || this.getShadowBlur() || this.getShadowOffsetX() || this.getShadowOffsetY()));
}, },
/** /**
@@ -433,7 +441,7 @@
* @memberof Kinetic.Shape.prototype * @memberof Kinetic.Shape.prototype
*/ */
Kinetic.Node.addColorGetterSetter(Kinetic.Shape, 'shadowColor'); Kinetic.Node.addColorGetterSetter(Kinetic.Shape, 'shadowColor', undefined, _clearHasShadowCache);
/** /**
* set shadow color * set shadow color
@@ -516,7 +524,7 @@
* @memberof Kinetic.Shape.prototype * @memberof Kinetic.Shape.prototype
*/ */
Kinetic.Node.addGetterSetter(Kinetic.Shape, 'shadowBlur'); Kinetic.Node.addGetterSetter(Kinetic.Shape, 'shadowBlur', undefined, _clearHasShadowCache);
/** /**
* set shadow blur * set shadow blur
@@ -533,7 +541,7 @@
* @memberof Kinetic.Shape.prototype * @memberof Kinetic.Shape.prototype
*/ */
Kinetic.Node.addGetterSetter(Kinetic.Shape, 'shadowOpacity'); Kinetic.Node.addGetterSetter(Kinetic.Shape, 'shadowOpacity', undefined, _clearHasShadowCache);
/** /**
* set shadow opacity * set shadow opacity
@@ -1239,7 +1247,7 @@
* @memberof Kinetic.Shape.prototype * @memberof Kinetic.Shape.prototype
*/ */
Kinetic.Node.addPointGetterSetter(Kinetic.Shape, 'shadowOffset', 0); Kinetic.Node.addPointGetterSetter(Kinetic.Shape, 'shadowOffset', 0, _clearHasShadowCache);
/** /**
* set shadow offset * set shadow offset

View File

@@ -89,7 +89,7 @@ Test.Modules.NODE = {
test(circle.getAbsoluteOpacity() === 0.25, 'abs opacity should be 0.25'); test(circle.getAbsoluteOpacity() === 0.25, 'abs opacity should be 0.25');
test(layer.getAbsoluteOpacity() === 0.5, 'abs opacity should be 0.5'); test(layer.getAbsoluteOpacity() === 0.5, 'abs opacity should be 0.5');
}, },
'*caching': function(containerId) { 'caching': function(containerId) {
var stage = new Kinetic.Stage({ var stage = new Kinetic.Stage({
container: containerId, container: containerId,
width: 578, width: 578,
@@ -105,7 +105,7 @@ Test.Modules.NODE = {
strokeWidth: 4 strokeWidth: 4
}); });
test(!circle.cache.transform, '1) circle transform cache should be empty'); test(circle.cache.transform === undefined, '1) circle transform cache should be empty');
layer.add(circle); layer.add(circle);
stage.add(layer); stage.add(layer);
@@ -113,7 +113,7 @@ Test.Modules.NODE = {
// transform cache // transform cache
test(circle.cache.transform, '2) circle transform cache should be primed'); test(circle.cache.transform, '2) circle transform cache should be primed');
circle.setX(100); circle.setX(100);
test(!circle.cache.transform, '3) circle transform cache should be empty'); test(circle.cache.transform === undefined, '3) circle transform cache should be empty');
layer.draw(); layer.draw();
test(circle.cache.transform, '4) circle transform cache should be primed'); test(circle.cache.transform, '4) circle transform cache should be primed');
@@ -128,6 +128,16 @@ Test.Modules.NODE = {
stage.draw(); stage.draw();
test(circle.cache.visible === true, '9) circle visible cache should be primed'); test(circle.cache.visible === true, '9) circle visible cache should be primed');
// shadow cache
test(circle.cache.hasShadow === false, '10) circle shadow cache should be primed');
circle.setShadowColor('red');
circle.setShadowOffset(10);
test(circle.cache.hasShadow === undefined, '11) circle shadow cache should be empty');
layer.draw();
test(circle.cache.hasShadow === true, '12) circle shadow cache should be primed');
layer.draw();
test(circle.cache.hasShadow === true, '13) circle shadow cache should still be primed after redraw');
}, },
'test pixel ratio toDataURL': function(containerId) { 'test pixel ratio toDataURL': function(containerId) {
var stage = new Kinetic.Stage({ var stage = new Kinetic.Stage({