From 59a571c6356295cd9d66f09fb4821053dc3294e6 Mon Sep 17 00:00:00 2001 From: Eric Rowell Date: Sat, 22 Mar 2014 00:13:05 -0700 Subject: [PATCH] refactored cache method to work better with containers and also fast layers --- src/Container.js | 12 +++---- src/FastLayer.js | 8 +++-- src/Layer.js | 12 +++---- src/Node.js | 66 +++++++++++++++++++++++-------------- src/Shape.js | 10 +++--- test/runner.html | 1 + test/unit/FastLayer-test.js | 34 +++++++++++++++++++ test/unit/Group-test.js | 50 ++++++++++++++++++++++++++++ test/unit/Node-test.js | 2 +- 9 files changed, 150 insertions(+), 45 deletions(-) create mode 100644 test/unit/Group-test.js diff --git a/src/Container.js b/src/Container.js index 3f96be54..4da676ac 100644 --- a/src/Container.js +++ b/src/Container.js @@ -282,7 +282,7 @@ child.index = n; }); }, - drawScene: function(can) { + drawScene: function(can, top) { var layer = this.getLayer(), canvas = can || (layer && layer.getCanvas()), context = canvas && canvas.getContext(), @@ -294,12 +294,12 @@ this._drawCachedSceneCanvas(context); } else { - this._drawChildren(canvas, 'drawScene'); + this._drawChildren(canvas, 'drawScene', top); } } return this; }, - drawHit: function(can) { + drawHit: function(can, top) { var layer = this.getLayer(), canvas = can || (layer && layer.hitCanvas), context = canvas && canvas.getContext(), @@ -311,12 +311,12 @@ this._drawCachedHitCanvas(context); } else { - this._drawChildren(canvas, 'drawHit'); + this._drawChildren(canvas, 'drawHit', top); } } return this; }, - _drawChildren: function(canvas, drawMethod) { + _drawChildren: function(canvas, drawMethod, top) { var layer = this.getLayer(), context = canvas && canvas.getContext(), clipWidth = this.getClipWidth(), @@ -337,7 +337,7 @@ } this.children.each(function(child) { - child[drawMethod](canvas); + child[drawMethod](canvas, top); }); if (hasClip) { diff --git a/src/FastLayer.js b/src/FastLayer.js index c60cfaf8..0ed93f38 100644 --- a/src/FastLayer.js +++ b/src/FastLayer.js @@ -41,9 +41,11 @@ // the apply transform method is handled by the Layer and FastLayer class // because it is up to the layer to decide if an absolute or relative transform // should be used - _applyTransform: function(shape, context) { - var m = shape.getTransform().getMatrix(); - context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); + _applyTransform: function(shape, context, top) { + if (!top || top._id !== this._id) { + var m = shape.getTransform().getMatrix(); + context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); + } }, draw: function() { this.drawScene(); diff --git a/src/Layer.js b/src/Layer.js index 44e8c99f..cb80670b 100644 --- a/src/Layer.js +++ b/src/Layer.js @@ -100,7 +100,7 @@ return {}; } }, - drawScene: function(can) { + drawScene: function(can, top) { var layer = this.getLayer(), canvas = can || (layer && layer.getCanvas()); @@ -112,7 +112,7 @@ canvas.getContext().clear(); } - Kinetic.Container.prototype.drawScene.call(this, canvas); + Kinetic.Container.prototype.drawScene.call(this, canvas, top); this._fire(DRAW, { node: this @@ -123,11 +123,11 @@ // the apply transform method is handled by the Layer and FastLayer class // because it is up to the layer to decide if an absolute or relative transform // should be used - _applyTransform: function(shape, context) { - var m = shape.getAbsoluteTransform().getMatrix(); + _applyTransform: function(shape, context, top) { + var m = shape.getAbsoluteTransform(top).getMatrix(); context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); }, - drawHit: function(can) { + drawHit: function(can, top) { var layer = this.getLayer(), canvas = can || (layer && layer.hitCanvas); @@ -135,7 +135,7 @@ layer.getHitCanvas().getContext().clear(); } - Kinetic.Container.prototype.drawHit.call(this, canvas); + Kinetic.Container.prototype.drawHit.call(this, canvas, top); return this; }, /** diff --git a/src/Node.js b/src/Node.js index 33384079..85d262e7 100644 --- a/src/Node.js +++ b/src/Node.js @@ -172,22 +172,25 @@ origTransEnabled = this.transformsEnabled(), origX = this.x(), origY = this.y(), - sceneContext; + sceneContext = cachedSceneCanvas.getContext(), + hitContext = cachedHitCanvas.getContext(); this.clearCache(); + + sceneContext.save(); + hitContext.save(); - var layerApplyTrans = layer._applyTransform; - layer._applyTransform = function(shape, context) { - context.translate(x * -1, y * -1); - }; + sceneContext.translate(x * -1, y * -1); + hitContext.translate(x * -1, y * -1); - this.drawScene(cachedSceneCanvas); - this.drawHit(cachedHitCanvas); + if (this.nodeType === 'Shape') { + sceneContext.translate(this.x() * -1, this.y() * -1); + hitContext.translate(this.x() * -1, this.y() * -1); + } // this will draw a red border around the cached box for // debugging purposes - if (drawBorder) { - sceneContext = cachedSceneCanvas.getContext(); + if (drawBorder) { sceneContext.save(); sceneContext.beginPath(); sceneContext.rect(0, 0, width, height); @@ -197,10 +200,12 @@ sceneContext.stroke(); sceneContext.restore(); } - - // set the _applyTransform method back to the original - layer._applyTransform = layerApplyTrans; + this.drawScene(cachedSceneCanvas, this); + this.drawHit(cachedHitCanvas, this); + + sceneContext.restore(); + hitContext.restore(); this._cache.canvas = { scene: cachedSceneCanvas, @@ -848,16 +853,22 @@ this.setPosition({x:x, y:y}); return this; }, - _eachAncestorReverse: function(func, includeSelf) { + _eachAncestorReverse: function(func, top) { var family = [], parent = this.getParent(), len, n; - // build family by traversing ancestors - if(includeSelf) { - family.unshift(this); + // if top node is defined, and this node is top node, + // there's no need to build a family tree. just execute + // func with this because it will be the only node + if (top && top._id === this._id) { + func(this); + return true; } - while(parent) { + + family.unshift(this); + + while(parent && (!top || parent._id !== top._id)) { family.unshift(parent); parent = parent.parent; } @@ -1126,10 +1137,17 @@ * @memberof Kinetic.Node.prototype * @returns {Kinetic.Transform} */ - getAbsoluteTransform: function() { - return this._getCache(ABSOLUTE_TRANSFORM, this._getAbsoluteTransform); + getAbsoluteTransform: function(top) { + // if using an argument, we can't cache the result. + if (top) { + return this._getAbsoluteTransform(top); + } + // if no argument, we can cache the result + else { + return this._getCache(ABSOLUTE_TRANSFORM, this._getAbsoluteTransform); + } }, - _getAbsoluteTransform: function() { + _getAbsoluteTransform: function(top) { var at = new Kinetic.Transform(), transformsEnabled, trans; @@ -1144,7 +1162,7 @@ else if (transformsEnabled === 'position') { at.translate(node.x(), node.y()); } - }, true); + }, top); return at; }, /** @@ -1516,9 +1534,9 @@ * @memberof Kinetic.Node.prototype * @returns {Kinetic.Node} */ - draw: function() { - this.drawScene(); - this.drawHit(); + draw: function(top) { + this.drawScene(top); + this.drawHit(top); return this; } }); diff --git a/src/Shape.js b/src/Shape.js index d136a4d7..b4f19855 100644 --- a/src/Shape.js +++ b/src/Shape.js @@ -132,7 +132,7 @@ _useBufferCanvas: function() { return (this.hasShadow() || this.getAbsoluteOpacity() !== 1) && this.hasFill() && this.hasStroke() && this.getStage(); }, - drawScene: function(can) { + drawScene: function(can, top) { var layer = this.getLayer(), canvas = can || layer.getCanvas(), context = canvas.getContext(), @@ -155,7 +155,7 @@ bufferContext.clear(); bufferContext.save(); bufferContext._applyLineJoin(this); - layer._applyTransform(this, bufferContext); + layer._applyTransform(this, bufferContext, top); drawFunc.call(this, bufferContext); bufferContext.restore(); @@ -173,7 +173,7 @@ // if buffer canvas is not needed else { context._applyLineJoin(this); - layer._applyTransform(this, context); + layer._applyTransform(this, context, top); if (hasShadow) { context.save(); @@ -191,7 +191,7 @@ return this; }, - drawHit: function(can) { + drawHit: function(can, top) { var layer = this.getLayer(), canvas = can || layer.hitCanvas, context = canvas.getContext(), @@ -207,7 +207,7 @@ else if (drawFunc) { context.save(); context._applyLineJoin(this); - layer._applyTransform(this, context); + layer._applyTransform(this, context, top); drawFunc.call(this, context); context.restore(); diff --git a/test/runner.html b/test/runner.html index 7ac1db81..b0e44a2a 100644 --- a/test/runner.html +++ b/test/runner.html @@ -49,6 +49,7 @@ + diff --git a/test/unit/FastLayer-test.js b/test/unit/FastLayer-test.js index ab6af43d..44930b4b 100644 --- a/test/unit/FastLayer-test.js +++ b/test/unit/FastLayer-test.js @@ -21,4 +21,38 @@ suite('FastLayer', function() { }); +test('cache shape on fast layer', function(){ + var stage = addStage(); + var layer = new Kinetic.FastLayer(); + + var circle = new Kinetic.Circle({ + x: 74, + y: 74, + radius: 70, + fill: 'green', + stroke: 'black', + strokeWidth: 4, + name: 'myCircle' + }); + + layer.add(circle); + stage.add(layer); + + + circle.cache({ + x: -74, + y: -74, + width: 148, + height: 148 + }).offset({ + x: 74, + y: 74 + }); + + layer.draw(); + + + }); + + }); \ No newline at end of file diff --git a/test/unit/Group-test.js b/test/unit/Group-test.js new file mode 100644 index 00000000..6b91947f --- /dev/null +++ b/test/unit/Group-test.js @@ -0,0 +1,50 @@ +suite('Group', function() { + + // ====================================================== + test('cache group with text', function() { + var stage = addStage(); + + var layer = new Kinetic.Layer(); + var group = new Kinetic.Group({ + draggable : true, + x: 100, + y: 40 + }); + var text = new Kinetic.Text({ + text : "some text", + fontSize: 20, + fill: "black", + y : 50 + }); + + var rect = new Kinetic.Rect({ + height : 100, + width : 100, + stroke : "#00B80C", + strokeWidth: 10, + cornerRadius: 1 + }); + group.add(text); + group.add(rect); + layer.add(group); + + stage.add(layer); + + group.cache({ + x: -5, + y: -5, + width : 110, + height : 110, + drawBorder: true + }).offsetX(5).offsetY(5); + + stage.draw(); + }); +}); + + + + + + + diff --git a/test/unit/Node-test.js b/test/unit/Node-test.js index 5e761706..ae939909 100644 --- a/test/unit/Node-test.js +++ b/test/unit/Node-test.js @@ -2860,7 +2860,7 @@ suite('Node', function() { showHit(layer) assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,124,124);beginPath();arc(0,0,70,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();clearRect(0,0,578,200);save();transform(1,0,0,1,50,50);drawImage([object HTMLCanvasElement],0,0);restore();'); - assert.equal(circle._cache.canvas.scene.getContext().getTrace(), 'save();translate(74,74);beginPath();arc(0,0,70,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();'); + assert.equal(circle._cache.canvas.scene.getContext().getTrace(), 'save();translate(74,74);translate(-74,-74);save();transform(1,0,0,1,74,74);beginPath();arc(0,0,70,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();restore();'); }); test('cache shape thats larger than stage', function(){