From d902e6dca001cac2b9615c151c12f7147de30c16 Mon Sep 17 00:00:00 2001 From: Eric Rowell Date: Sat, 10 Aug 2013 00:58:53 -0700 Subject: [PATCH] absolute transforms are now cached --- src/Canvas.js | 5 ++ src/Node.js | 120 ++++++++++++++++++++++++------------- src/Shape.js | 16 +++-- tests/js/unit/nodeTests.js | 16 ++++- 4 files changed, 109 insertions(+), 48 deletions(-) diff --git a/src/Canvas.js b/src/Canvas.js index aed38d0d..a9d39645 100644 --- a/src/Canvas.js +++ b/src/Canvas.js @@ -218,6 +218,10 @@ } }, _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, t, m; @@ -226,6 +230,7 @@ m = t.getMatrix(); context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); }, true); + */ }, _clip: function(container) { var context = this.getContext(); diff --git a/src/Node.js b/src/Node.js index 4f84ae67..161d0392 100644 --- a/src/Node.js +++ b/src/Node.js @@ -1,6 +1,7 @@ (function() { // CONSTANTS - var ADD = 'add', + var ABSOLUTE_TRANSFORM = 'absoluteTransform', + ADD = 'add', B = 'b', BEFORE = 'before', BLACK = 'black', @@ -34,20 +35,29 @@ UPPER_Y = 'Y', VISIBLE = 'visible', X = 'x', - Y = 'y', + Y = 'y'; - CACHE_MAP = { - x: TRANSFORM, - y: TRANSFORM, - rotation: TRANSFORM, - rotationDeg: TRANSFORM, - scaleX: TRANSFORM, - scaleY: TRANSFORM, - skewX: TRANSFORM, - skewY: TRANSFORM, + function _clearTransformCacheEachChild(node) { + _clearTransformCache.call(node); + } + function _clearTransformCache() { + this._clearCache(TRANSFORM); + this._clearCache(ABSOLUTE_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, { _init: function(config) { @@ -58,10 +68,7 @@ this.setAttrs(config); }, _clearCache: function(attr){ - var cacheAttr = CACHE_MAP[attr]; - if (cacheAttr) { - delete this.cache[cacheAttr]; - } + delete this.cache[attr]; }, _getCache: function(attr, privateGetter){ var cache = this.cache[attr]; @@ -234,6 +241,26 @@ 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 * @method @@ -758,6 +785,9 @@ * @memberof Kinetic.Node.prototype */ getAbsoluteTransform: function() { + return this._getCache(ABSOLUTE_TRANSFORM, this._getAbsoluteTransform); + }, + _getAbsoluteTransform: function() { // absolute transform var am = new Kinetic.Transform(), m; @@ -1153,28 +1183,28 @@ }); // getter setter adders - Kinetic.Node.addGetterSetter = function(constructor, attr, def) { + Kinetic.Node.addGetterSetter = function(constructor, attr, def, before) { this.addGetter(constructor, attr, def); - this.addSetter(constructor, attr); + this.addSetter(constructor, attr, before); }; - Kinetic.Node.addPointGetterSetter = function(constructor, attr, def) { - this.addPointGetter(constructor, attr); - this.addPointSetter(constructor, attr); + Kinetic.Node.addPointGetterSetter = function(constructor, attr, def, before) { + this.addPointGetter(constructor, attr, def); + this.addPointSetter(constructor, attr, before); // add invdividual component getters and setters this.addGetter(constructor, attr + UPPER_X, def); this.addGetter(constructor, attr + UPPER_Y, def); - this.addSetter(constructor, attr + UPPER_X); - this.addSetter(constructor, attr + UPPER_Y); + this.addSetter(constructor, attr + UPPER_X, before); + this.addSetter(constructor, attr + UPPER_Y, before); }; Kinetic.Node.addPointsGetterSetter = function(constructor, attr) { this.addPointsGetter(constructor, attr); this.addPointsSetter(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.addRotationSetter(constructor, attr); + this.addRotationSetter(constructor, attr, before); }; Kinetic.Node.addColorGetterSetter = function(constructor, attr) { this.addGetter(constructor, attr); @@ -1289,16 +1319,17 @@ this._setAttr('points', points); }; }; - Kinetic.Node.addSetter = function(constructor, attr) { - var that = this, - method = SET + Kinetic.Util._capitalize(attr); + Kinetic.Node.addSetter = function(constructor, attr, before) { + var method = SET + Kinetic.Util._capitalize(attr); constructor.prototype[method] = function(val) { - this._clearCache(attr); + if (before) { + before.call(this); + } this._setAttr(attr, val); }; }; - Kinetic.Node.addPointSetter = function(constructor, attr) { + Kinetic.Node.addPointSetter = function(constructor, attr, before) { var that = this, baseMethod = SET + Kinetic.Util._capitalize(attr); @@ -1313,6 +1344,9 @@ y = pos.y; this._fireBeforeChangeEvent(attr, oldVal, pos); + if (before) { + before.call(this); + } if (x !== undefined) { 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, method = SET + Kinetic.Util._capitalize(attr); // radians constructor.prototype[method] = function(val) { - this._clearCache(attr); + if (before) { + before.call(this); + } this._setAttr(attr, val); }; // degrees constructor.prototype[method + DEG] = function(deg) { - this._clearCache(attr); + if (before) { + before.call(this); + } this._setAttr(attr, Kinetic.Util._degToRad(deg)); }; }; @@ -1394,7 +1432,7 @@ }; // add getters setters - Kinetic.Node.addGetterSetter(Kinetic.Node, 'x', 0); + Kinetic.Node.addGetterSetter(Kinetic.Node, 'x', 0, _clearTransformCache); /** * set x position @@ -1411,7 +1449,7 @@ * @memberof Kinetic.Node.prototype */ - Kinetic.Node.addGetterSetter(Kinetic.Node, 'y', 0); + Kinetic.Node.addGetterSetter(Kinetic.Node, 'y', 0, _clearTransformCache); /** * set y position @@ -1465,7 +1503,7 @@ * @memberof Kinetic.Node.prototype */ - Kinetic.Node.addRotationGetterSetter(Kinetic.Node, 'rotation', 0); + Kinetic.Node.addRotationGetterSetter(Kinetic.Node, 'rotation', 0, _clearTransformCache); /** * set rotation in radians @@ -1497,7 +1535,7 @@ * @memberof Kinetic.Node.prototype */ - Kinetic.Node.addPointGetterSetter(Kinetic.Node, 'scale', 1); + Kinetic.Node.addPointGetterSetter(Kinetic.Node, 'scale', 1, _clearTransformCache); /** * set scale @@ -1558,7 +1596,7 @@ * @memberof Kinetic.Node.prototype */ - Kinetic.Node.addPointGetterSetter(Kinetic.Node, 'skew', 0); + Kinetic.Node.addPointGetterSetter(Kinetic.Node, 'skew', 0, _clearTransformCache); /** * set skew @@ -1620,7 +1658,7 @@ * @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 @@ -1719,7 +1757,7 @@ * @memberof Kinetic.Node.prototype */ - Kinetic.Node.addSetter(Kinetic.Node, 'visible'); + Kinetic.Node.addSetter(Kinetic.Node, 'visible', _clearVisibleCache); /** * set visible diff --git a/src/Shape.js b/src/Shape.js index bbc7d6a5..450dffc7 100644 --- a/src/Shape.js +++ b/src/Shape.js @@ -1,4 +1,6 @@ (function() { + var HAS_SHADOW = 'hasShadow'; + function _fillFunc(context) { context.fill(); } @@ -11,6 +13,9 @@ function _strokeFuncHit(context) { context.stroke(); } + function _clearHasShadowCache() { + this._clearCache(HAS_SHADOW); + } Kinetic.Util.addMethods(Kinetic.Shape, { __init: function(config) { @@ -67,6 +72,9 @@ * @memberof Kinetic.Shape.prototype */ hasShadow: function() { + return this._getCache(HAS_SHADOW, this._hasShadow); + }, + _hasShadow: function() { return (this.getShadowOpacity() !== 0 && !!(this.getShadowColor() || this.getShadowBlur() || this.getShadowOffsetX() || this.getShadowOffsetY())); }, /** @@ -433,7 +441,7 @@ * @memberof Kinetic.Shape.prototype */ - Kinetic.Node.addColorGetterSetter(Kinetic.Shape, 'shadowColor'); + Kinetic.Node.addColorGetterSetter(Kinetic.Shape, 'shadowColor', undefined, _clearHasShadowCache); /** * set shadow color @@ -516,7 +524,7 @@ * @memberof Kinetic.Shape.prototype */ - Kinetic.Node.addGetterSetter(Kinetic.Shape, 'shadowBlur'); + Kinetic.Node.addGetterSetter(Kinetic.Shape, 'shadowBlur', undefined, _clearHasShadowCache); /** * set shadow blur @@ -533,7 +541,7 @@ * @memberof Kinetic.Shape.prototype */ - Kinetic.Node.addGetterSetter(Kinetic.Shape, 'shadowOpacity'); + Kinetic.Node.addGetterSetter(Kinetic.Shape, 'shadowOpacity', undefined, _clearHasShadowCache); /** * set shadow opacity @@ -1239,7 +1247,7 @@ * @memberof Kinetic.Shape.prototype */ - Kinetic.Node.addPointGetterSetter(Kinetic.Shape, 'shadowOffset', 0); + Kinetic.Node.addPointGetterSetter(Kinetic.Shape, 'shadowOffset', 0, _clearHasShadowCache); /** * set shadow offset diff --git a/tests/js/unit/nodeTests.js b/tests/js/unit/nodeTests.js index 4a8f7a5b..4ed9b31d 100644 --- a/tests/js/unit/nodeTests.js +++ b/tests/js/unit/nodeTests.js @@ -89,7 +89,7 @@ Test.Modules.NODE = { test(circle.getAbsoluteOpacity() === 0.25, 'abs opacity should be 0.25'); test(layer.getAbsoluteOpacity() === 0.5, 'abs opacity should be 0.5'); }, - '*caching': function(containerId) { + 'caching': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, width: 578, @@ -105,7 +105,7 @@ Test.Modules.NODE = { 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); stage.add(layer); @@ -113,7 +113,7 @@ Test.Modules.NODE = { // transform cache test(circle.cache.transform, '2) circle transform cache should be primed'); 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(); test(circle.cache.transform, '4) circle transform cache should be primed'); @@ -128,6 +128,16 @@ Test.Modules.NODE = { stage.draw(); 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) { var stage = new Kinetic.Stage({