checking in first phase of new hit detection algo, which enables high performance event detections even if there are hundreds of thousands of shapes. This is a giant improvement over the previous algo, and is a fairly big architectural change. Since the Animation architecture and API is also changing, I feel that these changes are big enough to warrant a new major version, v4.0.0

This commit is contained in:
Eric Rowell 2012-08-11 16:22:01 -07:00
parent 0f1a424840
commit 468f8ef2d3
8 changed files with 93 additions and 125 deletions

59
dist/kinetic-core.js vendored
View File

@ -3,7 +3,7 @@
* http://www.kineticjs.com/ * http://www.kineticjs.com/
* Copyright 2012, Eric Rowell * Copyright 2012, Eric Rowell
* Licensed under the MIT or GPL Version 2 licenses. * Licensed under the MIT or GPL Version 2 licenses.
* Date: Aug 10 2012 * Date: Aug 11 2012
* *
* Copyright (C) 2011 - 2012 by Eric Rowell * Copyright (C) 2011 - 2012 by Eric Rowell
* *
@ -3086,15 +3086,13 @@ Kinetic.Stage = Kinetic.Container.extend({
this.content.style.width = width + 'px'; this.content.style.width = width + 'px';
this.content.style.height = height + 'px'; this.content.style.height = height + 'px';
// set buffer canvas and path canvas sizes
this.bufferCanvas.setSize(width, height); this.bufferCanvas.setSize(width, height);
this.pathCanvas.setSize(width, height);
// set user defined layer dimensions // set user defined layer dimensions
var layers = this.children; var layers = this.children;
for(var n = 0; n < layers.length; n++) { for(var n = 0; n < layers.length; n++) {
var layer = layers[n]; var layer = layers[n];
layer.getCanvas().setSize(width, height); layer.getCanvas().setSize(width, height);
layer.bufferCanvas.setSize(width, height);
layer.draw(); layer.draw();
} }
}, },
@ -3119,17 +3117,11 @@ Kinetic.Stage = Kinetic.Container.extend({
*/ */
_add: function(layer) { _add: function(layer) {
layer.canvas.setSize(this.attrs.width, this.attrs.height); layer.canvas.setSize(this.attrs.width, this.attrs.height);
layer.bufferCanvas.setSize(this.attrs.width, this.attrs.height);
// draw layer and append canvas to container // draw layer and append canvas to container
layer.draw(); layer.draw();
this.content.appendChild(layer.canvas.element); this.content.appendChild(layer.canvas.element);
/*
* set layer last draw time to zero
* so that throttling doesn't take into account
* the layer draws associated with adding a node
*/
layer.lastDrawTime = 0;
}, },
/** /**
* detect event * detect event
@ -3279,14 +3271,27 @@ Kinetic.Stage = Kinetic.Container.extend({
this.targetFound = false; this.targetFound = false;
var pos = this.getUserPosition(); var pos = this.getUserPosition();
var p = this.pathCanvas.context.getImageData(pos.x, pos.y, 1, 1).data; var shape;
var layers = this.getChildren();
/*
* traverse through layers from top to bottom and look
* for hit detection
*/
for(var n = layers.length - 1; n >= 0; n--) {
var layer = layers[n];
var p = layer.bufferCanvas.context.getImageData(pos.x, pos.y, 1, 1).data;
var colorKey = Kinetic.Type._rgbToHex(p[0], p[1], p[2]); var colorKey = Kinetic.Type._rgbToHex(p[0], p[1], p[2]);
var shape = Kinetic.Global.shapes[colorKey]; shape = Kinetic.Global.shapes[colorKey];
var isDragging = Kinetic.Global.drag.moving; var isDragging = Kinetic.Global.drag.moving;
if(shape) { if(shape) {
this._detectEvent(shape, evt); this._detectEvent(shape, evt);
break;
} }
}
// handle mouseout condition // handle mouseout condition
/* /*
else if(!isDragging && this.targetShape && this.targetShape._id === shape._id) { else if(!isDragging && this.targetShape && this.targetShape._id === shape._id) {
@ -3537,12 +3542,7 @@ Kinetic.Stage = Kinetic.Container.extend({
width: this.attrs.width, width: this.attrs.width,
height: this.attrs.height height: this.attrs.height
}); });
this.pathCanvas = new Kinetic.Canvas({
width: this.attrs.width,
height: this.attrs.height
});
this.pathCanvas.name = 'pathCanvas';
this.pathCanvas.strip();
this._resizeDOM(); this._resizeDOM();
}, },
_addId: function(node) { _addId: function(node) {
@ -3696,12 +3696,12 @@ Kinetic.Layer = Kinetic.Container.extend({
}); });
this.nodeType = 'Layer'; this.nodeType = 'Layer';
this.lastDrawTime = 0;
this.beforeDrawFunc = undefined; this.beforeDrawFunc = undefined;
this.afterDrawFunc = undefined; this.afterDrawFunc = undefined;
this.canvas = new Kinetic.Canvas(); this.canvas = new Kinetic.Canvas();
this.canvas.getElement().style.position = 'absolute'; this.canvas.getElement().style.position = 'absolute';
this.bufferCanvas = new Kinetic.Canvas();
this.bufferCanvas.name = 'buffer';
// call super constructor // call super constructor
this._super(config); this._super(config);
@ -3792,7 +3792,6 @@ Kinetic.Layer = Kinetic.Container.extend({
* private draw children * private draw children
*/ */
_draw: function(canvas) { _draw: function(canvas) {
var pathCanvas = this.getStage().pathCanvas;
/* /*
* if canvas is not defined, then use the canvas * if canvas is not defined, then use the canvas
* tied to the layer * tied to the layer
@ -3801,9 +3800,6 @@ Kinetic.Layer = Kinetic.Container.extend({
canvas = this.getCanvas(); canvas = this.getCanvas();
} }
var time = new Date().getTime();
this.lastDrawTime = time;
// before draw handler // before draw handler
if(this.beforeDrawFunc !== undefined) { if(this.beforeDrawFunc !== undefined) {
this.beforeDrawFunc.call(this); this.beforeDrawFunc.call(this);
@ -3811,7 +3807,7 @@ Kinetic.Layer = Kinetic.Container.extend({
if(this.attrs.clearBeforeDraw) { if(this.attrs.clearBeforeDraw) {
canvas.clear(); canvas.clear();
pathCanvas.clear(); this.bufferCanvas.clear();
} }
if(this.isVisible()) { if(this.isVisible()) {
@ -3823,7 +3819,7 @@ Kinetic.Layer = Kinetic.Container.extend({
// draw children on front canvas // draw children on front canvas
this._drawChildren(canvas); this._drawChildren(canvas);
// draw children on back canvas // draw children on back canvas
this._drawChildren(pathCanvas); this._drawChildren(this.bufferCanvas);
} }
// after draw handler // after draw handler
@ -4274,16 +4270,21 @@ Kinetic.Shape = Kinetic.Node.extend({
// draw the shape // draw the shape
this.appliedShadow = false; this.appliedShadow = false;
if(canvas.name === 'pathCanvas') { if(canvas.name === 'buffer') {
var fill = this.attrs.fill; var fill = this.attrs.fill;
var stroke = this.attrs.stroke; var stroke = this.attrs.stroke;
if(fill) {
this.attrs.fill = '#' + this.colorKey; this.attrs.fill = '#' + this.colorKey;
}
if(stroke) {
this.attrs.stroke = '#' + this.colorKey; this.attrs.stroke = '#' + this.colorKey;
} }
}
this.attrs.drawFunc.call(this, canvas.getContext()); this.attrs.drawFunc.call(this, canvas.getContext());
if(canvas.name === 'pathCanvas') { if(canvas.name === 'buffer') {
this.attrs.fill = fill; this.attrs.fill = fill;
this.attrs.stroke = stroke; this.attrs.stroke = stroke;
} }

File diff suppressed because one or more lines are too long

View File

@ -40,12 +40,12 @@ Kinetic.Layer = Kinetic.Container.extend({
}); });
this.nodeType = 'Layer'; this.nodeType = 'Layer';
this.lastDrawTime = 0;
this.beforeDrawFunc = undefined; this.beforeDrawFunc = undefined;
this.afterDrawFunc = undefined; this.afterDrawFunc = undefined;
this.canvas = new Kinetic.Canvas(); this.canvas = new Kinetic.Canvas();
this.canvas.getElement().style.position = 'absolute'; this.canvas.getElement().style.position = 'absolute';
this.bufferCanvas = new Kinetic.Canvas();
this.bufferCanvas.name = 'buffer';
// call super constructor // call super constructor
this._super(config); this._super(config);
@ -136,7 +136,6 @@ Kinetic.Layer = Kinetic.Container.extend({
* private draw children * private draw children
*/ */
_draw: function(canvas) { _draw: function(canvas) {
var pathCanvas = this.getStage().pathCanvas;
/* /*
* if canvas is not defined, then use the canvas * if canvas is not defined, then use the canvas
* tied to the layer * tied to the layer
@ -145,9 +144,6 @@ Kinetic.Layer = Kinetic.Container.extend({
canvas = this.getCanvas(); canvas = this.getCanvas();
} }
var time = new Date().getTime();
this.lastDrawTime = time;
// before draw handler // before draw handler
if(this.beforeDrawFunc !== undefined) { if(this.beforeDrawFunc !== undefined) {
this.beforeDrawFunc.call(this); this.beforeDrawFunc.call(this);
@ -155,7 +151,7 @@ Kinetic.Layer = Kinetic.Container.extend({
if(this.attrs.clearBeforeDraw) { if(this.attrs.clearBeforeDraw) {
canvas.clear(); canvas.clear();
pathCanvas.clear(); this.bufferCanvas.clear();
} }
if(this.isVisible()) { if(this.isVisible()) {
@ -167,7 +163,7 @@ Kinetic.Layer = Kinetic.Container.extend({
// draw children on front canvas // draw children on front canvas
this._drawChildren(canvas); this._drawChildren(canvas);
// draw children on back canvas // draw children on back canvas
this._drawChildren(pathCanvas); this._drawChildren(this.bufferCanvas);
} }
// after draw handler // after draw handler

View File

@ -373,16 +373,21 @@ Kinetic.Shape = Kinetic.Node.extend({
// draw the shape // draw the shape
this.appliedShadow = false; this.appliedShadow = false;
if(canvas.name === 'pathCanvas') { if(canvas.name === 'buffer') {
var fill = this.attrs.fill; var fill = this.attrs.fill;
var stroke = this.attrs.stroke; var stroke = this.attrs.stroke;
if(fill) {
this.attrs.fill = '#' + this.colorKey; this.attrs.fill = '#' + this.colorKey;
}
if(stroke) {
this.attrs.stroke = '#' + this.colorKey; this.attrs.stroke = '#' + this.colorKey;
} }
}
this.attrs.drawFunc.call(this, canvas.getContext()); this.attrs.drawFunc.call(this, canvas.getContext());
if(canvas.name === 'pathCanvas') { if(canvas.name === 'buffer') {
this.attrs.fill = fill; this.attrs.fill = fill;
this.attrs.stroke = stroke; this.attrs.stroke = stroke;
} }

View File

@ -352,15 +352,13 @@ Kinetic.Stage = Kinetic.Container.extend({
this.content.style.width = width + 'px'; this.content.style.width = width + 'px';
this.content.style.height = height + 'px'; this.content.style.height = height + 'px';
// set buffer canvas and path canvas sizes
this.bufferCanvas.setSize(width, height); this.bufferCanvas.setSize(width, height);
this.pathCanvas.setSize(width, height);
// set user defined layer dimensions // set user defined layer dimensions
var layers = this.children; var layers = this.children;
for(var n = 0; n < layers.length; n++) { for(var n = 0; n < layers.length; n++) {
var layer = layers[n]; var layer = layers[n];
layer.getCanvas().setSize(width, height); layer.getCanvas().setSize(width, height);
layer.bufferCanvas.setSize(width, height);
layer.draw(); layer.draw();
} }
}, },
@ -385,17 +383,11 @@ Kinetic.Stage = Kinetic.Container.extend({
*/ */
_add: function(layer) { _add: function(layer) {
layer.canvas.setSize(this.attrs.width, this.attrs.height); layer.canvas.setSize(this.attrs.width, this.attrs.height);
layer.bufferCanvas.setSize(this.attrs.width, this.attrs.height);
// draw layer and append canvas to container // draw layer and append canvas to container
layer.draw(); layer.draw();
this.content.appendChild(layer.canvas.element); this.content.appendChild(layer.canvas.element);
/*
* set layer last draw time to zero
* so that throttling doesn't take into account
* the layer draws associated with adding a node
*/
layer.lastDrawTime = 0;
}, },
/** /**
* detect event * detect event
@ -545,14 +537,27 @@ Kinetic.Stage = Kinetic.Container.extend({
this.targetFound = false; this.targetFound = false;
var pos = this.getUserPosition(); var pos = this.getUserPosition();
var p = this.pathCanvas.context.getImageData(pos.x, pos.y, 1, 1).data; var shape;
var layers = this.getChildren();
/*
* traverse through layers from top to bottom and look
* for hit detection
*/
for(var n = layers.length - 1; n >= 0; n--) {
var layer = layers[n];
var p = layer.bufferCanvas.context.getImageData(pos.x, pos.y, 1, 1).data;
var colorKey = Kinetic.Type._rgbToHex(p[0], p[1], p[2]); var colorKey = Kinetic.Type._rgbToHex(p[0], p[1], p[2]);
var shape = Kinetic.Global.shapes[colorKey]; shape = Kinetic.Global.shapes[colorKey];
var isDragging = Kinetic.Global.drag.moving; var isDragging = Kinetic.Global.drag.moving;
if(shape) { if(shape) {
this._detectEvent(shape, evt); this._detectEvent(shape, evt);
break;
} }
}
// handle mouseout condition // handle mouseout condition
/* /*
else if(!isDragging && this.targetShape && this.targetShape._id === shape._id) { else if(!isDragging && this.targetShape && this.targetShape._id === shape._id) {
@ -803,12 +808,7 @@ Kinetic.Stage = Kinetic.Container.extend({
width: this.attrs.width, width: this.attrs.width,
height: this.attrs.height height: this.attrs.height
}); });
this.pathCanvas = new Kinetic.Canvas({
width: this.attrs.width,
height: this.attrs.height
});
this.pathCanvas.name = 'pathCanvas';
this.pathCanvas.strip();
this._resizeDOM(); this._resizeDOM();
}, },
_addId: function(node) { _addId: function(node) {

View File

@ -68,7 +68,7 @@ Test.prototype.tests = {
layer.add(circle); layer.add(circle);
stage.add(layer); stage.add(layer);
//document.body.appendChild(stage.pathCanvas.element) document.body.appendChild(layer.bufferCanvas.element)
}, },
'TRANSITION - transition position and rotation': function(containerId) { 'TRANSITION - transition position and rotation': function(containerId) {
var stage = new Kinetic.Stage({ var stage = new Kinetic.Stage({

View File

@ -102,15 +102,15 @@ Test.prototype.tests = {
// update tooltip // update tooltip
console.log('mouseover') console.log('mouseover')
var mousePos = stage.getMousePosition(); var mousePos = stage.getMousePosition();
//tooltip.setPosition(mousePos.x + 5, mousePos.y + 5); tooltip.setPosition(mousePos.x + 5, mousePos.y + 5);
//tooltip.setText("node: " + i + ", color: " + color); tooltip.setText("node: " + i + ", color: " + color);
//tooltip.show(); tooltip.show();
//tooltipLayer.draw(); tooltipLayer.draw();
}); });
circle.on("mouseout", function() { circle.on("mouseout", function() {
//tooltip.hide(); tooltip.hide();
//tooltipLayer.draw(); tooltipLayer.draw();
}); });
circlesLayer.add(circle); circlesLayer.add(circle);
@ -132,9 +132,9 @@ Test.prototype.tests = {
stage.add(circlesLayer); stage.add(circlesLayer);
//stage.add(tooltipLayer); stage.add(tooltipLayer);
document.body.appendChild(stage.pathCanvas.element) document.body.appendChild(circlesLayer.bufferCanvas.element)
}, },
'DRAWING - draw rect vs image from image data': function(containerId) { 'DRAWING - draw rect vs image from image data': function(containerId) {

View File

@ -94,40 +94,6 @@ Test.prototype.tests = {
layer.add(group); layer.add(group);
layer.draw(); layer.draw();
}, },
'STAGE - test layer throttle': function(containerId) {
var stage = new Kinetic.Stage({
container: containerId,
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var group = new Kinetic.Group();
var circle = new Kinetic.Ellipse({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
radius: 70,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
name: 'myCircle'
});
group.add(circle);
layer.add(group);
stage.add(layer);
test(layer.lastDrawTime === 0, 'layer last draw time should be 0');
/*
* if throttling isn't working correctly, then the circle will
* flash green and then turn red
*/
circle.setFill('red');
layer.draw();
test(layer.lastDrawTime > 0, 'layer last draw time should be greather than 0');
},
'STAGE - add shape with linear gradient fill': function(containerId) { 'STAGE - add shape with linear gradient fill': function(containerId) {
var stage = new Kinetic.Stage({ var stage = new Kinetic.Stage({
container: containerId, container: containerId,