first pass at new drawing caching mechanism

This commit is contained in:
Eric Rowell
2013-12-28 13:25:15 -08:00
parent 4866acdf93
commit b5165fa8fb
4 changed files with 149 additions and 124 deletions

View File

@@ -223,7 +223,7 @@
this.setAttr('lineJoin', lineJoin); this.setAttr('lineJoin', lineJoin);
} }
}, },
_applyAncestorTransforms: function(shape) { _applyTransform: function(shape) {
var transformsEnabled = shape.getTransformsEnabled(), var transformsEnabled = shape.getTransformsEnabled(),
m; m;
@@ -235,7 +235,6 @@
// best performance for position only transforms // best performance for position only transforms
this.translate(shape.getX(), shape.getY()); this.translate(shape.getX(), shape.getY());
} }
}, },
_clip: function(container) { _clip: function(container) {
var clipX = container.getClipX(), var clipX = container.getClipX(),
@@ -244,7 +243,7 @@
clipHeight = container.getClipHeight(); clipHeight = container.getClipHeight();
this.save(); this.save();
this._applyAncestorTransforms(container); this._applyTransform(container);
this.beginPath(); this.beginPath();
this.rect(clipX, clipY, clipWidth, clipHeight); this.rect(clipX, clipY, clipWidth, clipHeight);
this.clip(); this.clip();

View File

@@ -109,22 +109,39 @@
* node.cache(); * node.cache();
*/ */
cache: function(box) { cache: function(box) {
var sceneCanvasCache = new Kinetic.SceneCanvas({ var x = box.x || 0,
y = box.y || 0,
sceneCanvasCache = new Kinetic.SceneCanvas({
pixelRatio: 1, pixelRatio: 1,
width: box.width, width: box.width,
height: box.height height: box.height
}); }),
var hitCanvasCache = new Kinetic.HitCanvas({ hitCanvasCache = new Kinetic.HitCanvas({
pixelRatio: 1, pixelRatio: 1,
width: box.width, width: box.width,
height: box.height height: box.height
}); }),
origTransEnabled = this.transformsEnabled(),
origX = this.x(),
origY = this.y();
this.transformsEnabled('position');
this.x(x * -1);
this.y(y * -1);
this.drawScene(sceneCanvasCache); this.drawScene(sceneCanvasCache);
this.drawHit(hitCanvasCache); this.drawHit(hitCanvasCache);
this._cache.sceneCanvas = sceneCanvasCache; this.x(origX);
this._cache.hitCanvas = hitCanvasCache; this.y(origY);
this.transformsEnabled(origTransEnabled);
this._cache.canvas = {
scene: sceneCanvasCache,
hit: hitCanvasCache,
x: x,
y: y
};
}, },
/* /*
* the default isDraggable method returns false. * the default isDraggable method returns false.
@@ -599,14 +616,14 @@
* @returns {Kinetic.Node} * @returns {Kinetic.Node}
*/ */
setAbsolutePosition: function(pos) { setAbsolutePosition: function(pos) {
var trans = this._clearTransform(), var origTrans = this._clearTransform(),
it; it;
// don't clear translation // don't clear translation
this.attrs.x = trans.x; this.attrs.x = origTrans.x;
this.attrs.y = trans.y; this.attrs.y = origTrans.y;
delete trans.x; delete origTrans.x;
delete trans.y; delete origTrans.y;
// unravel transform // unravel transform
it = this.getAbsoluteTransform(); it = this.getAbsoluteTransform();
@@ -619,7 +636,8 @@
}; };
this.setPosition({x:pos.x, y:pos.y}); this.setPosition({x:pos.x, y:pos.y});
this._setTransform(trans); this._setTransform(origTrans);
return this; return this;
}, },
_setTransform: function(trans) { _setTransform: function(trans) {
@@ -658,6 +676,7 @@
this._clearCache(TRANSFORM); this._clearCache(TRANSFORM);
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM); this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
// return original transform
return trans; return trans;
}, },
/** /**
@@ -984,6 +1003,7 @@
}, },
_getTransform: function() { _getTransform: function() {
var m = new Kinetic.Transform(), var m = new Kinetic.Transform(),
cachedCanvas = this._cache.canvas,
x = this.getX(), x = this.getX(),
y = this.getY(), y = this.getY(),
rotation = this.getRotation(), rotation = this.getRotation(),
@@ -994,6 +1014,16 @@
offsetX = this.getOffsetX(), offsetX = this.getOffsetX(),
offsetY = this.getOffsetY(); offsetY = this.getOffsetY();
// NOTE: the cached canvas offsets must be handled in this method
// because there are situations where we need to access the original
// offset positions, i.e. setAbsolutePosition() and drag and drop
if (cachedCanvas) {
offsetX -= cachedCanvas.x;
}
if (cachedCanvas) {
offsetY -= cachedCanvas.y;
}
if(x !== 0 || y !== 0) { if(x !== 0 || y !== 0) {
m.translate(x, y); m.translate(x, y);
} }
@@ -1649,61 +1679,36 @@
Kinetic.Factory.addPointGetterSetter(Kinetic.Node, 'offset', 0); Kinetic.Factory.addPointGetterSetter(Kinetic.Node, 'offset', 0);
/** /**
* set offset. A node's offset defines the position and rotation point * get/set offset. A node's offset defines the position and rotation point
* @name setOffset
* @method * @method
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
* @param {Number} x * @param {Object} offset
* @param {Number} y * @param {Number} offset.x
* @returns {Kinetic.Node} * @param {Number} offset.y
* @returns {Object}
* @example * @example
* // set x and y <br> * // set x and y <br>
* shape.setOffset({<br> * shape.offset({<br>
* x: 20<br> * x: 20<br>
* y: 10<br> * y: 10<br>
* });<br><br> * });<br><br>
*/ */
/** /**
* get offset * get/set offset x
* @name getOffset * @name offsetX
* @method
* @memberof Kinetic.Node.prototype
* @returns {Object}
*/
/**
* set offset x
* @name setOffsetX
* @method
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
* @param {Number} x * @param {Number} x
* @returns {Kinetic.Node} * @returns {Integer}
*/ */
/** /**
* get offset x * get/set offset y
* @name getOffsetX * @name offsetY
* @method
* @memberof Kinetic.Node.prototype
* @returns {Number}
*/
/**
* set offset y
* @name setOffsetY
* @method * @method
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
* @param {Number} y * @param {Number} y
* @returns {Kinetic.Node} * @returns {Integer}
*/
/**
* get offset y
* @name getOffsetY
* @method
* @memberof Kinetic.Node.prototype
* @returns {Number}
*/ */
Kinetic.Factory.addSetter(Kinetic.Node, 'width', 0); Kinetic.Factory.addSetter(Kinetic.Node, 'width', 0);

View File

@@ -235,28 +235,20 @@
drawScene: function(can) { drawScene: function(can) {
var canvas = can || this.getLayer().getCanvas(), var canvas = can || this.getLayer().getCanvas(),
context = canvas.getContext(), context = canvas.getContext(),
cachedCanvas = this._cache.canvas,
cachedSceneCanvas = cachedCanvas && cachedCanvas.scene,
drawFunc = this.getDrawFunc(), 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(), hasShadow = this.hasShadow(),
stage, bufferCanvas, bufferContext; stage, bufferCanvas, bufferContext;
if(this.isVisible()) {
context.save();
if (cachedSceneCanvas) {
context._applyTransform(this);
context.drawImage(cachedSceneCanvas._canvas, 0, 0);
}
else if (drawFunc) {
// if buffer canvas is needed // if buffer canvas is needed
if (this._useBufferCanvas()) { if (this._useBufferCanvas()) {
stage = this.getStage(); stage = this.getStage();
@@ -265,7 +257,8 @@
bufferContext.clear(); bufferContext.clear();
bufferContext.save(); bufferContext.save();
bufferContext._applyLineJoin(this); bufferContext._applyLineJoin(this);
bufferContext._applyAncestorTransforms(this); bufferContext._applyTransform(this);
drawFunc.call(this, bufferContext); drawFunc.call(this, bufferContext);
bufferContext.restore(); bufferContext.restore();
@@ -282,7 +275,7 @@
// if buffer canvas is not needed // if buffer canvas is not needed
else { else {
context._applyLineJoin(this); context._applyLineJoin(this);
context._applyAncestorTransforms(this); context._applyTransform(this);
if (hasShadow) { if (hasShadow) {
context.save(); context.save();
@@ -294,21 +287,30 @@
context._applyOpacity(this); context._applyOpacity(this);
drawFunc.call(this, context); drawFunc.call(this, context);
} }
}
context.restore();
}
return this;
}, },
drawHit: function(can) { drawHit: function(can) {
var canvas = can || this.getLayer().hitCanvas, var canvas = can || this.getLayer().hitCanvas,
context = canvas.getContext(), context = canvas.getContext(),
drawFunc = this.getDrawHitFunc() || this.getDrawFunc(), drawFunc = this.getDrawHitFunc() || this.getDrawFunc(),
cachedHitCanvas = this._cache.hitCanvas; cachedCanvas = this._cache.canvas,
cachedHitCanvas = cachedCanvas && cachedCanvas.hit;
if(this.shouldDrawHit()) { if(this.shouldDrawHit()) {
context.save(); context.save();
if (cachedHitCanvas) { if (cachedHitCanvas) {
context._applyTransform(this);
context.drawImage(cachedHitCanvas._canvas, 0, 0); context.drawImage(cachedHitCanvas._canvas, 0, 0);
} }
else if (drawFunc) { else if (drawFunc) {
context._applyLineJoin(this); context._applyLineJoin(this);
context._applyAncestorTransforms(this); context._applyTransform(this);
drawFunc.call(this, context); drawFunc.call(this, context);
} }
context.restore(); context.restore();

View File

@@ -2897,8 +2897,8 @@ suite('Node', function() {
var layer = new Kinetic.Layer(); var layer = new Kinetic.Layer();
var group = new Kinetic.Group(); var group = new Kinetic.Group();
var circle = new Kinetic.Circle({ var circle = new Kinetic.Circle({
x: 100, x: 70,
y: 100, y: 70,
radius: 70, radius: 70,
fill: 'green', fill: 'green',
stroke: 'black', stroke: 'black',
@@ -2911,29 +2911,48 @@ suite('Node', function() {
layer.add(group); layer.add(group);
stage.add(layer); stage.add(layer);
console.log('-- before cache') assert.equal(circle._cache.canvas, undefined);
assert.equal(circle._cache['sceneCanvas'], undefined);
assert.equal(circle._cache['hitCanvas'], undefined);
circle.cache({ circle.cache({
width: 100, x: -74,
height: 100 y: -74,
width: 148,
height: 148
}); });
assert.notEqual(circle._cache['sceneCanvas'], undefined); assert.notEqual(circle._cache.canvas.scene, undefined);
assert.notEqual(circle._cache['hitCanvas'], undefined); assert.notEqual(circle._cache.canvas.hit, undefined);
assert.equal(circle._cache.canvas.x, -74);
assert.equal(circle._cache.canvas.y, -74);
layer.draw(); layer.draw();
document.body.appendChild(circle._cache.sceneCanvas._canvas); //document.body.appendChild(circle._cache.canvas.scene._canvas);
document.body.appendChild(circle._cache.hitCanvas._canvas); // document.body.appendChild(circle._cache.canvas.hit._canvas);
showHit(layer)
console.log(layer.getContext().getTrace()); //console.log(layer.getContext().getTrace());
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,70,70);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,-4,-4);drawImage([object HTMLCanvasElement],0,0);restore();');
// setTimeout(function() {
// console.log('draw')
// layer.draw();
// }, 1000)
// setTimeout(function() {
// console.log('draw')
// layer.draw();
// }, 2000)
// setTimeout(function() {
// console.log('draw')
// layer.draw();
// }, 3000)
}); });
}); });