mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 15:23:44 +08:00
fix #149 began decoupling scene graph draw and buffer graph draw logic. This will enable more flexibility for developers to define custom buffer draw functions, and it also improves draw performance for both the scene and buffer graphs, because each function can be optimized for its purpose. Also moved text drawing logic to the Text shape
This commit is contained in:
parent
a30d6730fe
commit
5be1802729
@ -39,8 +39,9 @@ Kinetic.Layer.prototype = {
|
||||
this.afterDrawFunc = undefined;
|
||||
this.canvas = new Kinetic.Canvas();
|
||||
this.canvas.getElement().style.position = 'absolute';
|
||||
this.canvas.getContext().type = 'scene';
|
||||
this.bufferCanvas = new Kinetic.Canvas();
|
||||
this.bufferCanvas.name = 'buffer';
|
||||
this.bufferCanvas.getContext().type = 'buffer';
|
||||
|
||||
// call super constructor
|
||||
Kinetic.Container.call(this, config);
|
||||
@ -266,7 +267,8 @@ Kinetic.Layer.prototype = {
|
||||
*/
|
||||
try {
|
||||
this.getStage().content.removeChild(this.canvas.element);
|
||||
} catch(e) {
|
||||
}
|
||||
catch(e) {
|
||||
Kinetic.Global.warn('unable to remove layer scene canvas element from the document');
|
||||
}
|
||||
}
|
||||
|
@ -946,7 +946,7 @@ Kinetic.Node.prototype = {
|
||||
}
|
||||
},
|
||||
_shouldDraw: function(canvas) {
|
||||
return (this.isVisible() && (!canvas || canvas.name !== 'buffer' || this.getListening()));
|
||||
return (this.isVisible() && (!canvas || canvas.getContext().type === 'scene' || this.getListening()));
|
||||
}
|
||||
};
|
||||
|
||||
|
143
src/Shape.js
143
src/Shape.js
@ -97,16 +97,21 @@ Kinetic.Shape.prototype = {
|
||||
return this.getLayer().getCanvas();
|
||||
},
|
||||
/**
|
||||
* helper method to stroke the shape and apply
|
||||
* shadows if needed
|
||||
* helper method to stroke the shape
|
||||
* @name stroke
|
||||
* @methodOf Kinetic.Shape.prototype
|
||||
*/
|
||||
stroke: function(context) {
|
||||
var strokeWidth = this.getStrokeWidth();
|
||||
var stroke = this.getStroke();
|
||||
if(context.type === 'scene') {
|
||||
this._strokeScene(context);
|
||||
}
|
||||
else if(context.type === 'buffer') {
|
||||
this._strokeBuffer(context);
|
||||
}
|
||||
},
|
||||
_strokeScene: function(context) {
|
||||
var strokeWidth = this.getStrokeWidth(), stroke = this.getStroke();
|
||||
if(stroke || strokeWidth) {
|
||||
var go = Kinetic.Global;
|
||||
var appliedShadow = false;
|
||||
|
||||
context.save();
|
||||
@ -124,6 +129,16 @@ Kinetic.Shape.prototype = {
|
||||
}
|
||||
}
|
||||
},
|
||||
_strokeBuffer: function(context) {
|
||||
var strokeWidth = this.getStrokeWidth(), stroke = this.colorKey;
|
||||
if(stroke || strokeWidth) {
|
||||
context.save();
|
||||
context.lineWidth = strokeWidth || 2;
|
||||
context.strokeStyle = stroke || 'black';
|
||||
context.stroke(context);
|
||||
context.restore();
|
||||
}
|
||||
},
|
||||
_getFillType: function(fill) {
|
||||
if(!fill) {
|
||||
return undefined;
|
||||
@ -145,15 +160,20 @@ Kinetic.Shape.prototype = {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* helper method to fill the shape with a color, linear gradient,
|
||||
* radial gradient, or pattern, and also apply shadows if needed
|
||||
* helper method to fill the shape
|
||||
* @name fill
|
||||
* @methodOf Kinetic.Shape.prototype
|
||||
* */
|
||||
fill: function(context) {
|
||||
var appliedShadow = false;
|
||||
var fill = this.getFill();
|
||||
var fillType = this._getFillType(fill);
|
||||
if(context.type === 'scene') {
|
||||
this._fillScene(context);
|
||||
}
|
||||
else if(context.type === 'buffer') {
|
||||
this._fillBuffer(context);
|
||||
}
|
||||
},
|
||||
_fillScene: function(context) {
|
||||
var appliedShadow = false, fill = this.getFill(), fillType = this._getFillType(fill);
|
||||
if(fill) {
|
||||
context.save();
|
||||
if(this.attrs.shadow && !this.appliedShadow) {
|
||||
@ -216,54 +236,11 @@ Kinetic.Shape.prototype = {
|
||||
this.fill(context);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* helper method to fill text and appy shadows if needed
|
||||
* @param {String} text
|
||||
* @name fillText
|
||||
* @methodOf Kinetic.Shape.prototype
|
||||
*/
|
||||
fillText: function(context, text) {
|
||||
var appliedShadow = false;
|
||||
if(this.attrs.textFill) {
|
||||
_fillBuffer: function(context) {
|
||||
context.save();
|
||||
if(this.attrs.shadow && !this.appliedShadow) {
|
||||
appliedShadow = this._applyShadow(context);
|
||||
}
|
||||
context.fillStyle = this.attrs.textFill;
|
||||
context.fillText(text, 0, 0);
|
||||
context.fillStyle = this.colorKey;
|
||||
context.fill(context);
|
||||
context.restore();
|
||||
}
|
||||
if(appliedShadow) {
|
||||
this.fillText(context, text, 0, 0);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* helper method to stroke text and apply shadows
|
||||
* if needed
|
||||
* @name strokeText
|
||||
* @methodOf Kinetic.Shape.prototype
|
||||
* @param {String} text
|
||||
*/
|
||||
strokeText: function(context, text) {
|
||||
var appliedShadow = false;
|
||||
|
||||
if(this.attrs.textStroke || this.attrs.textStrokeWidth) {
|
||||
context.save();
|
||||
if(this.attrs.shadow && !this.appliedShadow) {
|
||||
appliedShadow = this._applyShadow(context);
|
||||
}
|
||||
// defaults
|
||||
var textStroke = this.attrs.textStroke ? this.attrs.textStroke : 'black';
|
||||
var textStrokeWidth = this.attrs.textStrokeWidth ? this.attrs.textStrokeWidth : 2;
|
||||
context.lineWidth = textStrokeWidth;
|
||||
context.strokeStyle = textStroke;
|
||||
context.strokeText(text, 0, 0);
|
||||
context.restore();
|
||||
}
|
||||
|
||||
if(appliedShadow) {
|
||||
this.strokeText(context, text, 0, 0);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* helper method to draw an image and apply
|
||||
@ -464,61 +441,21 @@ Kinetic.Shape.prototype = {
|
||||
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
|
||||
var type = canvas.getContext().type;
|
||||
|
||||
if(type === 'scene') {
|
||||
this.applyOpacity(context);
|
||||
}
|
||||
|
||||
this.applyLineJoin(context);
|
||||
this.applyLineCap(context);
|
||||
|
||||
// draw the shape
|
||||
this.appliedShadow = false;
|
||||
|
||||
var wl = Kinetic.Global.BUFFER_WHITELIST;
|
||||
var bl = Kinetic.Global.BUFFER_BLACKLIST;
|
||||
var attrs = {};
|
||||
|
||||
if(canvas.name === 'buffer') {
|
||||
for(var n = 0; n < wl.length; n++) {
|
||||
var key = wl[n];
|
||||
attrs[key] = this.attrs[key];
|
||||
if(this.attrs[key] || (key === 'fill' && !this.attrs.stroke && !('image' in this.attrs))) {
|
||||
this.attrs[key] = '#' + this.colorKey;
|
||||
}
|
||||
}
|
||||
|
||||
for(var n = 0; n < bl.length; n++) {
|
||||
var key = bl[n];
|
||||
attrs[key] = this.attrs[key];
|
||||
this.attrs[key] = '';
|
||||
}
|
||||
|
||||
// image is a special case
|
||||
if('image' in this.attrs) {
|
||||
attrs.image = this.attrs.image;
|
||||
|
||||
if(this.imageBuffer) {
|
||||
this.attrs.image = this.imageBuffer;
|
||||
}
|
||||
else {
|
||||
this.attrs.image = null;
|
||||
this.attrs.fill = '#' + this.colorKey;
|
||||
}
|
||||
}
|
||||
|
||||
context.globalAlpha = 1;
|
||||
}
|
||||
|
||||
this.attrs.drawFunc.call(this, canvas.getContext());
|
||||
|
||||
if(canvas.name === 'buffer') {
|
||||
var bothLists = wl.concat(bl);
|
||||
for(var n = 0; n < bothLists.length; n++) {
|
||||
var key = bothLists[n];
|
||||
this.attrs[key] = attrs[key];
|
||||
}
|
||||
|
||||
// image is a special case
|
||||
this.attrs.image = attrs.image;
|
||||
}
|
||||
var drawFunc = (type === 'buffer' && this.attrs.drawBufferFunc) ? this.attrs.drawBufferFunc : this.attrs.drawFunc;
|
||||
|
||||
drawFunc.call(this, canvas.getContext());
|
||||
context.restore();
|
||||
}
|
||||
}
|
||||
|
@ -284,6 +284,7 @@ Kinetic.Stage.prototype = {
|
||||
this.content.style.width = width + 'px';
|
||||
this.content.style.height = height + 'px';
|
||||
|
||||
this.canvas.setSize(width, height);
|
||||
this.bufferCanvas.setSize(width, height);
|
||||
// set user defined layer dimensions
|
||||
var layers = this.children;
|
||||
@ -561,7 +562,10 @@ Kinetic.Stage.prototype = {
|
||||
this.content.className = 'kineticjs-content';
|
||||
this.attrs.container.appendChild(this.content);
|
||||
|
||||
this.canvas = new Kinetic.Canvas();
|
||||
this.canvas.getContext().type = 'scene';
|
||||
this.bufferCanvas = new Kinetic.Canvas();
|
||||
this.bufferCanvas.getContext().type = 'buffer';
|
||||
|
||||
this._resizeDOM();
|
||||
},
|
||||
|
@ -19,6 +19,7 @@ Kinetic.Image.prototype = {
|
||||
_initImage: function(config) {
|
||||
this.shapeType = "Image";
|
||||
config.drawFunc = this.drawFunc;
|
||||
config.drawBufferFunc = this.drawBufferFunc;
|
||||
// call super constructor
|
||||
Kinetic.Shape.call(this, config);
|
||||
|
||||
@ -30,8 +31,7 @@ Kinetic.Image.prototype = {
|
||||
this._syncSize();
|
||||
},
|
||||
drawFunc: function(context) {
|
||||
var width = this.getWidth();
|
||||
var height = this.getHeight();
|
||||
var width = this.getWidth(), height = this.getHeight();
|
||||
|
||||
context.beginPath();
|
||||
context.rect(0, 0, width, height);
|
||||
@ -54,6 +54,15 @@ Kinetic.Image.prototype = {
|
||||
}
|
||||
}
|
||||
},
|
||||
drawBufferFunc: function(context) {
|
||||
var width = this.getWidth(), height = this.getHeight();
|
||||
|
||||
context.beginPath();
|
||||
context.rect(0, 0, width, height);
|
||||
context.closePath();
|
||||
this.fill(context);
|
||||
this.stroke(context);
|
||||
},
|
||||
/**
|
||||
* apply filter
|
||||
* @name applyFilter
|
||||
@ -78,7 +87,8 @@ Kinetic.Image.prototype = {
|
||||
config.callback();
|
||||
}
|
||||
});
|
||||
} catch(e) {
|
||||
}
|
||||
catch(e) {
|
||||
Kinetic.Global.warn('Unable to apply filter. ' + e.message);
|
||||
}
|
||||
},
|
||||
@ -130,7 +140,8 @@ Kinetic.Image.prototype = {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
} catch(e) {
|
||||
}
|
||||
catch(e) {
|
||||
Kinetic.Global.warn('Unable to create image buffer.');
|
||||
}
|
||||
},
|
||||
|
@ -19,6 +19,7 @@ Kinetic.Sprite.prototype = {
|
||||
});
|
||||
this.shapeType = "Sprite";
|
||||
config.drawFunc = this.drawFunc;
|
||||
config.drawBufferFunc = this.drawBufferFunc;
|
||||
// call super constructor
|
||||
Kinetic.Shape.call(this, config);
|
||||
this.anim = new Kinetic.Animation();
|
||||
@ -48,6 +49,17 @@ Kinetic.Sprite.prototype = {
|
||||
this.drawImage(context, this.attrs.image, f.x, f.y, f.width, f.height, 0, 0, f.width, f.height);
|
||||
}
|
||||
},
|
||||
drawBufferFunc: function(context) {
|
||||
var anim = this.attrs.animation;
|
||||
var index = this.attrs.index;
|
||||
var f = this.attrs.animations[anim][index];
|
||||
|
||||
context.beginPath();
|
||||
context.rect(0, 0, f.width, f.height);
|
||||
context.closePath();
|
||||
this.fill(context);
|
||||
this.stroke(context);
|
||||
},
|
||||
/**
|
||||
* start sprite animation
|
||||
* @name start
|
||||
|
@ -166,6 +166,91 @@ Kinetic.Text.prototype = {
|
||||
height: parseInt(this.attrs.fontSize, 10)
|
||||
};
|
||||
},
|
||||
/**
|
||||
* helper method to fill text
|
||||
* @param {String} text
|
||||
* @name fillText
|
||||
* @methodOf Kinetic.Text.prototype
|
||||
*/
|
||||
fillText: function(context, text) {
|
||||
if(context.type === 'scene') {
|
||||
this._fillTextScene(context, text);
|
||||
}
|
||||
else if(context.type === 'buffer') {
|
||||
this._fillTextBuffer(context, text);
|
||||
}
|
||||
},
|
||||
_fillTextScene: function(context, text) {
|
||||
var appliedShadow = false;
|
||||
if(this.attrs.textFill) {
|
||||
context.save();
|
||||
if(this.attrs.shadow && !this.appliedShadow) {
|
||||
appliedShadow = this._applyShadow(context);
|
||||
}
|
||||
context.fillStyle = this.attrs.textFill;
|
||||
context.fillText(text, 0, 0);
|
||||
context.restore();
|
||||
}
|
||||
if(appliedShadow) {
|
||||
this.fillText(context, text, 0, 0);
|
||||
}
|
||||
},
|
||||
_fillTextBuffer: function(context, text) {
|
||||
if(this.attrs.textFill) {
|
||||
context.save();
|
||||
context.fillStyle = this.colorKey;
|
||||
context.fillText(text, 0, 0);
|
||||
context.restore();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* helper method to stroke text
|
||||
* if needed
|
||||
* @name strokeText
|
||||
* @methodOf Kinetic.Shape.prototype
|
||||
* @param {String} text
|
||||
*/
|
||||
strokeText: function(context, text) {
|
||||
if(context.type === 'scene') {
|
||||
this._strokeTextScene(context, text);
|
||||
}
|
||||
else if(context.type === 'buffer') {
|
||||
this._strokeTextBuffer(context, text);
|
||||
}
|
||||
},
|
||||
_strokeTextScene: function(context, text) {
|
||||
var appliedShadow = false;
|
||||
|
||||
if(this.attrs.textStroke || this.attrs.textStrokeWidth) {
|
||||
context.save();
|
||||
if(this.attrs.shadow && !this.appliedShadow) {
|
||||
appliedShadow = this._applyShadow(context);
|
||||
}
|
||||
// defaults
|
||||
var textStroke = this.attrs.textStroke ? this.attrs.textStroke : 'black';
|
||||
var textStrokeWidth = this.attrs.textStrokeWidth ? this.attrs.textStrokeWidth : 2;
|
||||
context.lineWidth = textStrokeWidth;
|
||||
context.strokeStyle = textStroke;
|
||||
context.strokeText(text, 0, 0);
|
||||
context.restore();
|
||||
}
|
||||
|
||||
if(appliedShadow) {
|
||||
this.strokeText(context, text, 0, 0);
|
||||
}
|
||||
},
|
||||
_strokeTextBuffer: function(context, text) {
|
||||
if(this.attrs.textStroke || this.attrs.textStrokeWidth) {
|
||||
context.save();
|
||||
// defaults
|
||||
var textStroke = this.colorKey ? this.colorKey : 'black';
|
||||
var textStrokeWidth = this.attrs.textStrokeWidth ? this.attrs.textStrokeWidth : 2;
|
||||
context.lineWidth = textStrokeWidth;
|
||||
context.strokeStyle = textStroke;
|
||||
context.strokeText(text, 0, 0);
|
||||
context.restore();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* set text data. wrap logic and width and height setting occurs
|
||||
* here
|
||||
|
@ -306,6 +306,13 @@ Kinetic.Global.extend(Kinetic.TextPath, Kinetic.Shape);
|
||||
Kinetic.Node.addGettersSetters(Kinetic.TextPath, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth']);
|
||||
Kinetic.Node.addGetters(Kinetic.TextPath, ['text']);
|
||||
|
||||
// create reference to Text methods
|
||||
Kinetic.TextPath.prototype.fillText = Kinetic.Text.prototype.fillText;
|
||||
Kinetic.TextPath.prototype._fillTextScene = Kinetic.Text.prototype._fillTextScene;
|
||||
Kinetic.TextPath.prototype._fillTextBuffer = Kinetic.Text.prototype._fillTextBuffer;
|
||||
Kinetic.TextPath.prototype.strokeText = Kinetic.Text.prototype.strokeText;
|
||||
Kinetic.TextPath.prototype._strokeTextScene = Kinetic.Text.prototype._strokeTextScene;
|
||||
Kinetic.TextPath.prototype._strokeTextBuffer = Kinetic.Text.prototype._strokeTextBuffer;
|
||||
/**
|
||||
* set font family
|
||||
* @name setFontFamily
|
||||
|
@ -327,6 +327,8 @@ Test.prototype.tests = {
|
||||
|
||||
layer.add(darth);
|
||||
stage.add(layer);
|
||||
|
||||
//document.body.appendChild(layer.bufferCanvas.element)
|
||||
};
|
||||
imageObj.src = '../assets/darth-vader.jpg';
|
||||
},
|
||||
@ -713,7 +715,7 @@ Test.prototype.tests = {
|
||||
layer.add(redCircle);
|
||||
stage.add(layer);
|
||||
},
|
||||
'*DRAG AND DROP - drag and drop elastic star with shadow': function(containerId) {
|
||||
'DRAG AND DROP - drag and drop elastic star with shadow': function(containerId) {
|
||||
var stage = new Kinetic.Stage({
|
||||
container: containerId,
|
||||
width: 578,
|
||||
|
@ -3067,6 +3067,8 @@ Test.prototype.tests = {
|
||||
|
||||
//layer.setListening(false);
|
||||
layer.drawBuffer();
|
||||
|
||||
|
||||
},
|
||||
'SHAPE - test size setters and getters': function(containerId) {
|
||||
var stage = new Kinetic.Stage({
|
||||
|
Loading…
Reference in New Issue
Block a user