transformsEnabled property now accepts all, none, or position. Also started prelim work for new caching system

This commit is contained in:
Eric Rowell
2013-12-19 23:29:23 -08:00
parent d3eb9990bd
commit 4866acdf93
5 changed files with 196 additions and 155 deletions

View File

@@ -243,9 +243,9 @@
clip = this.getClipWidth() && this.getClipHeight(),
children, n, len;
if (!canvas && layer) {
canvas = layer.getCanvas();
}
// if (!canvas && layer) {
// canvas = layer.getCanvas();
// }
if(this.isVisible()) {
if (clip) {

View File

@@ -224,14 +224,15 @@
}
},
_applyAncestorTransforms: function(shape) {
var m;
var transformsEnabled = shape.getTransformsEnabled(),
m;
if (shape.isTransformsEnabled()) {
if (transformsEnabled === 'all') {
m = shape.getAbsoluteTransform().getMatrix();
this.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
else {
// best performance
else if (transformsEnabled === 'position') {
// best performance for position only transforms
this.translate(shape.getX(), shape.getY());
}

View File

@@ -42,7 +42,7 @@
this._id = Kinetic.idCounter++;
this.eventListeners = {};
this.attrs = {};
this.cache = {};
this._cache = {};
this.setAttrs(config);
// event bindings for cache handling
@@ -60,32 +60,18 @@
that._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
});
},
/**
* clear cached variables
* @method
* @memberof Kinetic.Node.prototype
* @returns {Kinetic.Node}
* @example
* node.clearCache();
*/
clearCache: function() {
this.cache = {};
return this;
},
_clearCache: function(attr){
delete this.cache[attr];
delete this._cache[attr];
},
_getCache: function(attr, privateGetter){
var cache = this.cache[attr];
var cache = this._cache[attr];
// if not cached, we need to set it using the private getter method.
if (cache === undefined) {
this.cache[attr] = privateGetter.call(this);
this._cache[attr] = privateGetter.call(this);
}
return this.cache[attr];
return this._cache[attr];
},
/*
* when the logic for a cached result depends on ancestor propagation, use this
@@ -100,6 +86,46 @@
});
}
},
/**
* clear cached canvas
* @method
* @memberof Kinetic.Node.prototype
* @returns {Kinetic.Node}
* @example
* node.clearCache();
*/
clearCache: function() {
this._cache = {};
return this;
},
/**
* cache node to improve drawing performance.
* NOTE: if you have filters applied to your node, your shape is already cached.
* explicitly calling cache() will override your filters.
* @method
* @memberof Kinetic.Node.prototype
* @returns {Kinetic.Node}
* @example
* node.cache();
*/
cache: function(box) {
var sceneCanvasCache = new Kinetic.SceneCanvas({
pixelRatio: 1,
width: box.width,
height: box.height
});
var hitCanvasCache = new Kinetic.HitCanvas({
pixelRatio: 1,
width: box.width,
height: box.height
});
this.drawScene(sceneCanvasCache);
this.drawHit(hitCanvasCache);
this._cache.sceneCanvas = sceneCanvasCache;
this._cache.hitCanvas = hitCanvasCache;
},
/*
* the default isDraggable method returns false.
* if the DragAndDrop module is included, this is overwritten
@@ -107,20 +133,6 @@
isDraggable: function() {
return false;
},
/*
* when the logic for a cached result depends on descendant propagation, use this
* method to clear self and ancestor cache
*/
/*
_clearSelfAndAncestorCache: function(attr) {
var parent = this.getParent();
this._clearCache(attr);
if (parent) {
parent._clearSelfAndAncestorCache(attr);
}
},
*/
/**
* bind events to the node. KineticJS supports mouseover, mousemove,
* mouseout, mouseenter, mouseleave, mousedown, mouseup, click, dblclick, touchstart, touchmove,
@@ -954,7 +966,7 @@
m;
this._eachAncestorReverse(function(node) {
if (node.isTransformsEnabled()) {
if (node.transformsEnabled() === 'all') {
m = node.getTransform();
am.multiply(m);
}
@@ -1339,26 +1351,6 @@
this.drawScene();
this.drawHit();
return this;
},
/**
* enable transforms
* @method
* @memberof Kinetic.Node.prototype
* @returns {Node}
*/
enableTransforms: function() {
this.setTransformsEnabled(true);
return this;
},
/**
* disable transforms
* @method
* @memberof Kinetic.Node.prototype
* @returns {Node}
*/
disableTransforms: function() {
this.setTransformsEnabled(false);
return this;
}
});
@@ -1777,27 +1769,18 @@
* @returns {Boolean}
*/
Kinetic.Factory.addGetterSetter(Kinetic.Node, 'transformsEnabled', true);
Kinetic.Factory.addGetterSetter(Kinetic.Node, 'transformsEnabled', 'all');
/**
* enable/disable transforms
* @name setTransformsEnabled
* get/set transforms that are enabled. Can be "all", "none", or "position". The default
* is "all"
* @name transformsEnabled
* @method
* @memberof Kinetic.Node.prototype
* @param {Boolean} enabled
* @returns {Node}
* @param {String} enabled
* @returns {String}
*/
/**
* determine if transforms are enabled
* @name getTransformsEnabled
* @method
* @memberof Kinetic.Node.prototype
* @returns {Boolean}
*/
Kinetic.Node.prototype.isTransformsEnabled = Kinetic.Node.prototype.getTransformsEnabled;
Kinetic.Collection.mapMethods([
'on',
'off',

View File

@@ -236,68 +236,84 @@
var canvas = can || this.getLayer().getCanvas(),
context = canvas.getContext(),
drawFunc = this.getDrawFunc(),
cachedSceneCanvas = this._cache.sceneCanvas;
if(this.isVisible()) {
context.save();
// if cache is available
if (cachedSceneCanvas) {
context.drawImage(cachedSceneCanvas._canvas, 0, 0);
}
else if (drawFunc) {
this._drawScene(context);
}
context.restore();
}
return this;
},
_drawScene: function(context) {
var drawFunc = this.getDrawFunc(),
hasShadow = this.hasShadow(),
stage, bufferCanvas, bufferContext;
if(drawFunc && this.isVisible()) {
// if buffer canvas is needed
if (this._useBufferCanvas()) {
stage = this.getStage();
bufferCanvas = stage.bufferCanvas;
bufferContext = bufferCanvas.getContext();
bufferContext.clear();
bufferContext.save();
bufferContext._applyLineJoin(this);
bufferContext._applyAncestorTransforms(this);
drawFunc.call(this, bufferContext);
bufferContext.restore();
// if buffer canvas is needed
if (this._useBufferCanvas()) {
stage = this.getStage();
bufferCanvas = stage.bufferCanvas;
bufferContext = bufferCanvas.getContext();
bufferContext.clear();
bufferContext.save();
bufferContext._applyLineJoin(this);
bufferContext._applyAncestorTransforms(this);
drawFunc.call(this, bufferContext);
bufferContext.restore();
if (hasShadow) {
context.save();
if (hasShadow) {
context.save();
context._applyShadow(this);
context.drawImage(bufferCanvas._canvas, 0, 0);
context.restore();
}
context._applyOpacity(this);
context.drawImage(bufferCanvas._canvas, 0, 0);
context._applyShadow(this);
context.drawImage(bufferCanvas._canvas, 0, 0);
context.restore();
}
// if buffer canvas is not needed
else {
context.save();
context._applyLineJoin(this);
context._applyAncestorTransforms(this);
if (hasShadow) {
context.save();
context._applyShadow(this);
drawFunc.call(this, context);
context.restore();
}
context._applyOpacity(this);
drawFunc.call(this, context);
context.restore();
}
context._applyOpacity(this);
context.drawImage(bufferCanvas._canvas, 0, 0);
}
return this;
},
drawHit: function() {
var attrs = this.getAttrs(),
drawFunc = attrs.drawHitFunc || attrs.drawFunc,
canvas = this.getLayer().hitCanvas,
context = canvas.getContext();
if(drawFunc && this.shouldDrawHit()) {
context.save();
// if buffer canvas is not needed
else {
context._applyLineJoin(this);
context._applyAncestorTransforms(this);
if (hasShadow) {
context.save();
context._applyShadow(this);
drawFunc.call(this, context);
context.restore();
}
context._applyOpacity(this);
drawFunc.call(this, context);
}
},
drawHit: function(can) {
var canvas = can || this.getLayer().hitCanvas,
context = canvas.getContext(),
drawFunc = this.getDrawHitFunc() || this.getDrawFunc(),
cachedHitCanvas = this._cache.hitCanvas;
if(this.shouldDrawHit()) {
context.save();
if (cachedHitCanvas) {
context.drawImage(cachedHitCanvas._canvas, 0, 0);
}
else if (drawFunc) {
context._applyLineJoin(this);
context._applyAncestorTransforms(this);
drawFunc.call(this, context);
}
context.restore();
}
return this;
}
});

View File

@@ -114,17 +114,17 @@ suite('Node', function() {
strokeWidth: 4
});
assert.equal(circle.cache.transform, undefined);
assert.equal(circle._cache.transform, undefined);
layer.add(circle);
stage.add(layer);
// transform cache
assert.notEqual(circle.cache.transform, undefined);
assert.notEqual(circle._cache.transform, undefined);
circle.setX(100);
assert.equal(circle.cache.transform, undefined);
assert.equal(circle._cache.transform, undefined);
layer.draw();
assert.notEqual(circle.cache.transform, undefined);
assert.notEqual(circle._cache.transform, undefined);
});
// ======================================================
@@ -143,15 +143,15 @@ suite('Node', function() {
stage.add(layer);
// visible cache
assert.equal(circle.cache.visible, true);
assert.equal(circle._cache.visible, true);
circle.hide();
assert.equal(circle.cache.visible, undefined);
assert.equal(circle._cache.visible, undefined);
stage.draw();
assert.equal(circle.cache.visible, false);
assert.equal(circle._cache.visible, false);
circle.show();
assert.equal(circle.cache.visible, undefined);
assert.equal(circle._cache.visible, undefined);
layer.draw();
assert.equal(circle.cache.visible, true);
assert.equal(circle._cache.visible, true);
});
@@ -171,14 +171,14 @@ suite('Node', function() {
stage.add(layer);
// shadow cache
assert.equal(circle.cache.hasShadow, false);
assert.equal(circle._cache.hasShadow, false);
circle.setShadowColor('red');
circle.setShadowOffset(10);
assert.equal(circle.cache.hasShadow, undefined);
assert.equal(circle._cache.hasShadow, undefined);
layer.draw();
assert.equal(circle.cache.hasShadow, true);
assert.equal(circle._cache.hasShadow, true);
layer.draw();
assert.equal(circle.cache.hasShadow, true);
assert.equal(circle._cache.hasShadow, true);
});
@@ -198,11 +198,11 @@ suite('Node', function() {
stage.add(layer);
// opacity cache
assert.equal(circle.cache.absoluteOpacity, 1);
assert.equal(circle._cache.absoluteOpacity, 1);
circle.setOpacity(0.5);
assert.equal(circle.cache.absoluteOpacity, undefined);
assert.equal(circle._cache.absoluteOpacity, undefined);
layer.draw();
assert.equal(circle.cache.absoluteOpacity, 0.5);
assert.equal(circle._cache.absoluteOpacity, 0.5);
});
@@ -226,11 +226,11 @@ suite('Node', function() {
// prime the cache
circle.isListening();
assert.equal(circle.cache.listening, true);
assert.equal(circle._cache.listening, true);
circle.setListening(false);
assert.equal(circle.cache.listening, undefined);
assert.equal(circle._cache.listening, undefined);
circle.isListening();
assert.equal(circle.cache.listening, false);
assert.equal(circle._cache.listening, false);
});
@@ -251,7 +251,7 @@ suite('Node', function() {
// stage cache
var st = circle.getStage();
assert.equal(circle.cache.stage._id, stage._id);
assert.equal(circle._cache.stage._id, stage._id);
});
@@ -2671,17 +2671,13 @@ suite('Node', function() {
layer.add(group);
stage.add(layer);
assert.equal(circle.isTransformsEnabled(), true);
assert.equal(circle.transformsEnabled(), 'all');
circle.disableTransforms();
assert.equal(circle.isTransformsEnabled(), false);
circle.transformsEnabled('position');
assert.equal(circle.transformsEnabled(), 'position');
layer.draw();
circle.enableTransforms();
assert.equal(circle.isTransformsEnabled(), true);
});
@@ -2714,23 +2710,23 @@ suite('Node', function() {
stage.add(layer);
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,400,100);beginPath();arc(0,0,40,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();');
stage.disableTransforms();
stage.transformsEnabled('none');
layer.getContext().clearTrace();
stage.draw();
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,300,100);beginPath();arc(0,0,40,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();');
layer.disableTransforms();
layer.transformsEnabled('none');
layer.getContext().clearTrace();
stage.draw();
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,200,100);beginPath();arc(0,0,40,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();');
group.disableTransforms();
group.transformsEnabled('none');
layer.getContext().clearTrace();
stage.draw();
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);beginPath();arc(0,0,40,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();');
// disabling a shape transform disables all transforms but x and y. In this case, the Kinetic.Context uses translate instead of transform
circle.disableTransforms();
circle.transformsEnabled('position');
layer.getContext().clearTrace();
stage.draw();
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();translate(100,100);beginPath();arc(0,0,40,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();');
@@ -2895,4 +2891,49 @@ suite('Node', function() {
assert.equal(circle.position().x, 6);
assert.equal(circle.position().y, 8);
});
test('cache shape', function(){
var stage = addStage();
var layer = new Kinetic.Layer();
var group = new Kinetic.Group();
var circle = new Kinetic.Circle({
x: 100,
y: 100,
radius: 70,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
name: 'myCircle',
draggable: true
});
group.add(circle);
layer.add(group);
stage.add(layer);
console.log('-- before cache')
assert.equal(circle._cache['sceneCanvas'], undefined);
assert.equal(circle._cache['hitCanvas'], undefined);
circle.cache({
width: 100,
height: 100
});
assert.notEqual(circle._cache['sceneCanvas'], undefined);
assert.notEqual(circle._cache['hitCanvas'], undefined);
layer.draw();
document.body.appendChild(circle._cache.sceneCanvas._canvas);
document.body.appendChild(circle._cache.hitCanvas._canvas);
console.log(layer.getContext().getTrace());
});
});