further decoupled scene, hit, and buffer graph drawing. To define a custom hit draw function, you now need to set the drawHitFunc attr.

This commit is contained in:
Eric Rowell
2012-11-18 19:50:50 -08:00
parent e04b979063
commit 27d5031665
14 changed files with 197 additions and 113 deletions

View File

@@ -233,11 +233,34 @@ Kinetic.Container.prototype = {
children[n].index = n;
}
},
draw: function(canvas) {
if(Kinetic.Node.prototype._shouldDraw.call(this, canvas)) {
/*
* draw both scene and hit graphs
*/
draw: function() {
this.drawScene();
this.drawHit();
},
drawScene: function() {
if(this.isVisible()) {
var children = this.children, len = children.length;
for(var n = 0; n < len; n++) {
children[n].draw(canvas);
children[n].drawScene();
}
}
},
drawHit: function() {
if(this.isVisible() && this.isListening()) {
var children = this.children, len = children.length;
for(var n = 0; n < len; n++) {
children[n].drawHit();
}
}
},
drawBuffer: function(canvas) {
if(this.isVisible()) {
var children = this.children, len = children.length;
for(var n = 0; n < len; n++) {
children[n].drawBuffer(canvas);
}
}
}

View File

@@ -39,7 +39,7 @@ Kinetic.Layer.prototype = {
this.afterDrawFunc = undefined;
this.canvas = new Kinetic.Canvas();
this.canvas.getElement().style.position = 'absolute';
this.bufferCanvas = new Kinetic.Canvas(0, 0, true);
this.hitCanvas = new Kinetic.Canvas(0, 0, true);
// call super constructor
Kinetic.Container.call(this, config);
@@ -50,31 +50,13 @@ Kinetic.Layer.prototype = {
* @name draw
* @methodOf Kinetic.Layer.prototype
*/
draw: function(canvas) {
draw: function() {
// before draw handler
if(this.beforeDrawFunc !== undefined) {
this.beforeDrawFunc.call(this);
}
var canvases = [];
if(canvas) {
canvases.push(canvas);
}
else {
canvases.push(this.getCanvas());
canvases.push(this.bufferCanvas);
}
var length = canvases.length;
for(var n = 0; n < length; n++) {
var canvas = canvases[n];
if(Kinetic.Node.prototype._shouldDraw.call(this, canvas)) {
if(this.attrs.clearBeforeDraw) {
canvas.clear();
}
Kinetic.Container.prototype.draw.call(this, canvas);
}
}
Kinetic.Container.prototype.draw.call(this);
// after draw handler
if(this.afterDrawFunc !== undefined) {
@@ -82,22 +64,27 @@ Kinetic.Layer.prototype = {
}
},
/**
* draw children nodes on buffer. this includes any groups
* draw children nodes on hit. this includes any groups
* or shapes
* @name drawBuffer
* @name drawHit
* @methodOf Kinetic.Layer.prototype
*/
drawBuffer: function() {
this.draw(this.bufferCanvas);
drawHit: function() {
this.hitCanvas.clear();
Kinetic.Container.prototype.drawHit.call(this);
},
/**
* draw children nodes on scene. this includes any groups
* or shapes
* @name drawScene
* @methodOf Kinetic.Layer.prototype
* @param {Kinetic.Canvas} [canvas]
*/
drawScene: function() {
this.draw(this.getCanvas());
if(this.attrs.clearBeforeDraw) {
this.getCanvas().clear();
}
Kinetic.Container.prototype.drawScene.call(this);
},
/**
* set before draw handler
@@ -146,11 +133,11 @@ Kinetic.Layer.prototype = {
Kinetic.Node.prototype.setVisible.call(this, visible);
if(visible) {
this.canvas.element.style.display = 'block';
this.bufferCanvas.element.style.display = 'block';
this.hitCanvas.element.style.display = 'block';
}
else {
this.canvas.element.style.display = 'none';
this.bufferCanvas.element.style.display = 'none';
this.hitCanvas.element.style.display = 'none';
}
},
setZIndex: function(index) {

View File

@@ -734,16 +734,16 @@ Kinetic.Node = (function() {
var quality = config && config.quality ? config.quality : null;
var canvas;
//if width and height are defined, create new canvas to draw on, else reuse stage buffer canvas
//if width and height are defined, create new canvas to draw on, else reuse stage hit canvas
if(config && config.width && config.height) {
canvas = new Kinetic.Canvas(config.width, config.height);
}
else {
canvas = this.getStage().canvas;
canvas = this.getStage().bufferCanvas;
canvas.clear();
}
this.draw(canvas);
this.drawBuffer(canvas);
return canvas.toDataURL(mimeType, quality);
},
/**
@@ -953,9 +953,6 @@ Kinetic.Node = (function() {
for(var i = 0; i < len; i++) {
events[i].handler.apply(this, [evt]);
}
},
_shouldDraw: function(canvas) {
return (this.isVisible() && (!canvas || canvas.getContext().type === 'scene' || this.getListening()));
}
};

View File

@@ -108,8 +108,8 @@ Kinetic.Shape = (function() {
if(context.type === 'scene') {
this._strokeScene(context);
}
else if(context.type === 'buffer') {
this._strokeBuffer(context);
else if(context.type === 'hit') {
this._strokeHit(context);
}
},
_strokeScene: function(context) {
@@ -132,7 +132,7 @@ Kinetic.Shape = (function() {
}
}
},
_strokeBuffer: function(context) {
_strokeHit: function(context) {
var strokeWidth = this.getStrokeWidth(), stroke = this.colorKey;
if(stroke || strokeWidth) {
context.save();
@@ -172,8 +172,8 @@ Kinetic.Shape = (function() {
if(context.type === 'scene') {
this._fillScene(context);
}
else if(context.type === 'buffer') {
this._fillBuffer(context);
else if(context.type === 'hit') {
this._fillHit(context);
}
},
_fillScene: function(context) {
@@ -240,7 +240,7 @@ Kinetic.Shape = (function() {
this.fill(context);
}
},
_fillBuffer: function(context) {
_fillHit: function(context) {
context.save();
context.fillStyle = this.colorKey;
context.fill(context);
@@ -418,21 +418,21 @@ Kinetic.Shape = (function() {
intersects: function() {
var pos = Kinetic.Type._getXY(Array.prototype.slice.call(arguments));
var stage = this.getStage();
var bufferCanvas = stage.bufferCanvas;
bufferCanvas.clear();
this.draw(bufferCanvas);
var p = bufferCanvas.context.getImageData(Math.round(pos.x), Math.round(pos.y), 1, 1).data;
var hitCanvas = stage.hitCanvas;
hitCanvas.clear();
this.drawBuffer(hitCanvas);
var p = hitCanvas.context.getImageData(Math.round(pos.x), Math.round(pos.y), 1, 1).data;
return p[3] > 0;
},
remove: function() {
Kinetic.Node.prototype.remove.call(this);
delete Kinetic.Global.shapes[this.colorKey];
},
draw: function(canvas) {
var attrs = this.attrs, drawFunc = attrs.drawFunc, drawBufferFunc = attrs.drawBufferFunc;
drawBuffer: function(canvas) {
var attrs = this.attrs, drawFunc = attrs.drawFunc, context = canvas.getContext();
if(drawFunc && Kinetic.Node.prototype._shouldDraw.call(this, canvas)) {
var stage = this.getStage(), context = canvas.getContext(), family = [], parent = this.parent, type = canvas.getContext().type;
if(drawFunc && this.isVisible()) {
var stage = this.getStage(), family = [], parent = this.parent;
family.unshift(this);
while(parent) {
@@ -447,17 +447,67 @@ Kinetic.Shape = (function() {
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
if(type === 'scene') {
this.applyOpacity(context);
}
this.applyOpacity(context);
this.applyLineJoin(context);
this.applyLineCap(context);
this.appliedShadow = false;
var func = (type === 'buffer' && drawBufferFunc) ? drawBufferFunc : drawFunc;
drawFunc.call(this, context);
context.restore();
}
},
drawScene: function() {
var attrs = this.attrs, drawFunc = attrs.drawFunc, context = this.getLayer().getCanvas().getContext();
func.call(this, canvas.getContext());
if(drawFunc && this.isVisible()) {
var stage = this.getStage(), family = [], parent = this.parent;
family.unshift(this);
while(parent) {
family.unshift(parent);
parent = parent.parent;
}
context.save();
var len = family.length;
for(var n = 0; n < len; n++) {
var node = family[n], t = node.getTransform(), m = t.getMatrix();
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
this.applyOpacity(context);
this.applyLineJoin(context);
this.applyLineCap(context);
this.appliedShadow = false;
drawFunc.call(this, context);
context.restore();
}
},
drawHit: function() {
var attrs = this.attrs, drawFunc = attrs.drawHitFunc || attrs.drawFunc, context = this.getLayer().hitCanvas.getContext();
if(drawFunc && this.isVisible() && this.isListening()) {
var stage = this.getStage(), family = [], parent = this.parent;
family.unshift(this);
while(parent) {
family.unshift(parent);
parent = parent.parent;
}
context.save();
var len = family.length;
for(var n = 0; n < len; n++) {
var node = family[n], t = node.getTransform(), m = t.getMatrix();
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
// don't draw shadows on hit context
this.applyLineJoin(context);
this.applyLineCap(context);
drawFunc.call(this, context);
context.restore();
}
}
@@ -465,7 +515,7 @@ Kinetic.Shape = (function() {
Kinetic.Global.extend(Shape, Kinetic.Node);
// add getters and setters
Kinetic.Node.addGettersSetters(Shape, ['stroke', 'lineJoin', 'strokeWidth', 'drawFunc', 'drawBufferFunc', 'cornerRadius']);
Kinetic.Node.addGettersSetters(Shape, ['stroke', 'lineJoin', 'strokeWidth', 'drawFunc', 'drawHitFunc', 'cornerRadius']);
Kinetic.Node.addGetters(Shape, ['shadow', 'fill']);
/**
@@ -498,10 +548,10 @@ Kinetic.Shape = (function() {
*/
/**
* set draw buffer function used for hit detection
* @name setDrawBufferFunc
* set draw hit function used for hit detection
* @name setDrawHitFunc
* @methodOf Kinetic.Shape.prototype
* @param {Function} drawBufferFunc drawing function used for hit detection
* @param {Function} drawHitFunc drawing function used for hit detection
*/
/**
@@ -549,8 +599,8 @@ Kinetic.Shape = (function() {
*/
/**
* get draw buffer function
* @name getDrawBufferFunc
* get draw hit function
* @name getDrawHitFunc
* @methodOf Kinetic.Shape.prototype
*/

View File

@@ -60,11 +60,17 @@ Kinetic.Stage.prototype = {
this.setAttr('container', container);
},
/**
* draw layers
* draw layer scenes
* @name draw
* @methodOf Kinetic.Stage.prototype
*/
/**
* draw layer hits
* @name drawHit
* @methodOf Kinetic.Stage.prototype
*/
/**
* set height
* @name setHeight
@@ -178,9 +184,9 @@ Kinetic.Stage.prototype = {
var mimeType = config && config.mimeType ? config.mimeType : null;
var quality = config && config.quality ? config.quality : null;
/*
* need to create a canvas element rather than using the buffer canvas
* need to create a canvas element rather than using the hit canvas
* because this method is asynchonous which means that other parts of the
* code could modify the buffer canvas before it's finished
* code could modify the hit canvas before it's finished
*/
var width = config && config.width ? config.width : this.attrs.width;
var height = config && config.height ? config.height : this.attrs.height;
@@ -248,8 +254,8 @@ Kinetic.Stage.prototype = {
for(var n = layers.length - 1; n >= 0; n--) {
var layer = layers[n];
if(layer.isVisible() && layer.isListening()) {
var p = layer.bufferCanvas.context.getImageData(Math.round(pos.x), Math.round(pos.y), 1, 1).data;
// this indicates that a buffer pixel may have been found
var p = layer.hitCanvas.context.getImageData(Math.round(pos.x), Math.round(pos.y), 1, 1).data;
// this indicates that a hit pixel may have been found
if(p[3] === 255) {
var colorKey = Kinetic.Type._rgbToHex(p[0], p[1], p[2]);
shape = Kinetic.Global.shapes[colorKey];
@@ -284,14 +290,14 @@ 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);
this.hitCanvas.setSize(width, height);
// set user defined layer dimensions
var layers = this.children;
for(var n = 0; n < layers.length; n++) {
var layer = layers[n];
layer.getCanvas().setSize(width, height);
layer.bufferCanvas.setSize(width, height);
layer.hitCanvas.setSize(width, height);
layer.draw();
}
}
@@ -303,7 +309,7 @@ Kinetic.Stage.prototype = {
add: function(layer) {
Kinetic.Container.prototype.add.call(this, layer);
layer.canvas.setSize(this.attrs.width, this.attrs.height);
layer.bufferCanvas.setSize(this.attrs.width, this.attrs.height);
layer.hitCanvas.setSize(this.attrs.width, this.attrs.height);
// draw layer and append canvas to container
layer.draw();
@@ -562,8 +568,8 @@ Kinetic.Stage.prototype = {
this.content.className = 'kineticjs-content';
this.attrs.container.appendChild(this.content);
this.canvas = new Kinetic.Canvas();
this.bufferCanvas = new Kinetic.Canvas(0, 0, true);
this.bufferCanvas = new Kinetic.Canvas();
this.hitCanvas = new Kinetic.Canvas(0, 0, true);
this._resizeDOM();
},

View File

@@ -20,8 +20,8 @@ Kinetic.Image.prototype = {
this.shapeType = "Image";
config.drawFunc = this.drawFunc;
if(!config.drawBufferFunc) {
config.drawBufferFunc = this.drawBufferFunc;
if(!config.drawHitFunc) {
config.drawHitFunc = this.drawHitFunc;
}
// call super constructor
@@ -58,13 +58,19 @@ Kinetic.Image.prototype = {
}
}
},
drawBufferFunc: function(context) {
var width = this.getWidth(), height = this.getHeight();
drawHitFunc: function(context) {
var width = this.getWidth(), height = this.getHeight(), imageBuffer = this.imageBuffer;
context.beginPath();
context.rect(0, 0, width, height);
context.closePath();
this.fill(context);
if(imageBuffer) {
this.drawImage(context, this.imageBuffer, 0, 0, width, height);
}
else {
this.fill(context);
}
this.stroke(context);
},
/**
@@ -73,7 +79,7 @@ Kinetic.Image.prototype = {
* @methodOf Kinetic.Image.prototype
* @param {Object} config
* @param {Function} filter filter function
* @param {Object} [config] optional config object used to configure filter
* @param {Object} [config] optional config object used to configure filter
* @param {Function} [callback] callback function to be called once
* filter has been applied
*/

View File

@@ -20,8 +20,8 @@ Kinetic.Sprite.prototype = {
this.shapeType = "Sprite";
config.drawFunc = this.drawFunc;
if(!config.drawBufferFunc) {
config.drawBufferFunc = this.drawBufferFunc;
if(!config.drawHitFunc) {
config.drawHitFunc = this.drawHitFunc;
}
// call super constructor
@@ -53,7 +53,7 @@ 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) {
drawHitFunc: function(context) {
var anim = this.attrs.animation;
var index = this.attrs.index;
var f = this.attrs.animations[anim][index];

View File

@@ -4,7 +4,7 @@
* @param {Number} width
* @param {Number} height
*/
Kinetic.Canvas = function(width, height, isBuffer) {
Kinetic.Canvas = function(width, height, isHit) {
this.element = document.createElement('canvas');
this.context = this.element.getContext('2d');
@@ -13,7 +13,7 @@ Kinetic.Canvas = function(width, height, isBuffer) {
this.element.height = height || 0;
// set type
this.context.type = isBuffer ? 'buffer' : 'scene';
this.context.type = isHit ? 'hit' : 'scene';
};
Kinetic.Canvas.prototype = {