reworked pixel detection. I now require the dev to use shape.save() to save the pixel data for performance reasons

This commit is contained in:
Eric Rowell 2012-04-01 19:38:30 -07:00
parent 72c0555d0b
commit 6ce9d5489c
6 changed files with 121 additions and 135 deletions

112
dist/kinetic-core.js vendored
View File

@ -1228,11 +1228,11 @@ Kinetic.Stage.prototype = {
* @param {String} typesStr * @param {String} typesStr
* @param {function} handler * @param {function} handler
*/ */
onContainer: function(typesStr, handler) { onContent: function(typesStr, handler) {
var types = typesStr.split(' '); var types = typesStr.split(' ');
for(var n = 0; n < types.length; n++) { for(var n = 0; n < types.length; n++) {
var baseEvent = types[n]; var baseEvent = types[n];
this.container.addEventListener(baseEvent, handler, false); this.content.addEventListener(baseEvent, handler, false);
} }
}, },
/** /**
@ -1278,6 +1278,12 @@ Kinetic.Stage.prototype = {
getContainer: function() { getContainer: function() {
return this.container; return this.container;
}, },
/**
* get content DOM element
*/
getContent: function() {
return this.content;
},
/** /**
* get stage * get stage
*/ */
@ -1466,8 +1472,7 @@ Kinetic.Stage.prototype = {
this._setMousePosition(evt); this._setMousePosition(evt);
this._setTouchPosition(evt); this._setTouchPosition(evt);
this.pathLayer.clear();
this._clearDefaultLayers();
/* /*
* loop through layers. If at any point an event * loop through layers. If at any point an event
@ -1496,13 +1501,6 @@ Kinetic.Stage.prototype = {
} }
}, },
/**
* clear default layers
*/
_clearDefaultLayers: function() {
this.bufferLayer.clear();
this.pathLayer.clear();
},
/** /**
* begin listening for events by adding event handlers * begin listening for events by adding event handlers
* to the container * to the container
@ -1511,18 +1509,18 @@ Kinetic.Stage.prototype = {
var that = this; var that = this;
// desktop events // desktop events
this.container.addEventListener('mousedown', function(evt) { this.content.addEventListener('mousedown', function(evt) {
that.mouseDown = true; that.mouseDown = true;
that._handleStageEvent(evt); that._handleStageEvent(evt);
}, false); }, false);
this.container.addEventListener('mousemove', function(evt) { this.content.addEventListener('mousemove', function(evt) {
that.mouseUp = false; that.mouseUp = false;
that.mouseDown = false; that.mouseDown = false;
that._handleStageEvent(evt); that._handleStageEvent(evt);
}, false); }, false);
this.container.addEventListener('mouseup', function(evt) { this.content.addEventListener('mouseup', function(evt) {
that.mouseUp = true; that.mouseUp = true;
that.mouseDown = false; that.mouseDown = false;
that._handleStageEvent(evt); that._handleStageEvent(evt);
@ -1530,11 +1528,11 @@ Kinetic.Stage.prototype = {
that.clickStart = false; that.clickStart = false;
}, false); }, false);
this.container.addEventListener('mouseover', function(evt) { this.content.addEventListener('mouseover', function(evt) {
that._handleStageEvent(evt); that._handleStageEvent(evt);
}, false); }, false);
this.container.addEventListener('mouseout', function(evt) { this.content.addEventListener('mouseout', function(evt) {
// if there's a current target shape, run mouseout handlers // if there's a current target shape, run mouseout handlers
var targetShape = that.targetShape; var targetShape = that.targetShape;
if(targetShape) { if(targetShape) {
@ -1543,18 +1541,18 @@ Kinetic.Stage.prototype = {
that.mousePos = undefined; that.mousePos = undefined;
}, false); }, false);
// mobile events // mobile events
this.container.addEventListener('touchstart', function(evt) { this.content.addEventListener('touchstart', function(evt) {
evt.preventDefault(); evt.preventDefault();
that.touchStart = true; that.touchStart = true;
that._handleStageEvent(evt); that._handleStageEvent(evt);
}, false); }, false);
this.container.addEventListener('touchmove', function(evt) { this.content.addEventListener('touchmove', function(evt) {
evt.preventDefault(); evt.preventDefault();
that._handleStageEvent(evt); that._handleStageEvent(evt);
}, false); }, false);
this.container.addEventListener('touchend', function(evt) { this.content.addEventListener('touchend', function(evt) {
evt.preventDefault(); evt.preventDefault();
that.touchEnd = true; that.touchEnd = true;
that._handleStageEvent(evt); that._handleStageEvent(evt);
@ -1565,8 +1563,8 @@ Kinetic.Stage.prototype = {
* @param {Event} evt * @param {Event} evt
*/ */
_setMousePosition: function(evt) { _setMousePosition: function(evt) {
var mouseX = evt.offsetX || (evt.clientX - this._getContainerPosition().left + window.pageXOffset); var mouseX = evt.offsetX || (evt.clientX - this._getContentPosition().left + window.pageXOffset);
var mouseY = evt.offsetY || (evt.clientY - this._getContainerPosition().top + window.pageYOffset); var mouseY = evt.offsetY || (evt.clientY - this._getContentPosition().top + window.pageYOffset);
this.mousePos = { this.mousePos = {
x: mouseX, x: mouseX,
y: mouseY y: mouseY
@ -1581,8 +1579,8 @@ Kinetic.Stage.prototype = {
// one finger // one finger
var touch = evt.touches[0]; var touch = evt.touches[0];
// Get the information for finger #1 // Get the information for finger #1
var touchX = touch.clientX - this._getContainerPosition().left + window.pageXOffset; var touchX = touch.clientX - this._getContentPosition().left + window.pageXOffset;
var touchY = touch.clientY - this._getContainerPosition().top + window.pageYOffset; var touchY = touch.clientY - this._getContentPosition().top + window.pageYOffset;
this.touchPos = { this.touchPos = {
x: touchX, x: touchX,
@ -1593,8 +1591,8 @@ Kinetic.Stage.prototype = {
/** /**
* get container position * get container position
*/ */
_getContainerPosition: function() { _getContentPosition: function() {
var obj = this.container; var obj = this.content;
var top = 0; var top = 0;
var left = 0; var left = 0;
while(obj && obj.tagName !== 'BODY') { while(obj && obj.tagName !== 'BODY') {
@ -1648,7 +1646,7 @@ Kinetic.Stage.prototype = {
_prepareDrag: function() { _prepareDrag: function() {
var that = this; var that = this;
this.onContainer('mousemove touchmove', function(evt) { this.onContent('mousemove touchmove', function(evt) {
var go = Kinetic.GlobalObject; var go = Kinetic.GlobalObject;
var node = go.drag.node; var node = go.drag.node;
if(node) { if(node) {
@ -1730,7 +1728,7 @@ Kinetic.Stage.prototype = {
} }
}, false); }, false);
this.onContainer('mouseup touchend mouseout', function(evt) { this.onContent('mouseup touchend mouseout', function(evt) {
that._endDrag(evt); that._endDrag(evt);
}); });
}, },
@ -1747,8 +1745,12 @@ Kinetic.Stage.prototype = {
this.container.appendChild(this.content); this.container.appendChild(this.content);
// default layers // default layers
this.bufferLayer = new Kinetic.Layer(); this.bufferLayer = new Kinetic.Layer({
this.pathLayer = new Kinetic.Layer(); name: 'bufferLayer'
});
this.pathLayer = new Kinetic.Layer({
name: 'pathLayer'
});
// set parents // set parents
this.bufferLayer.parent = this; this.bufferLayer.parent = this;
@ -1933,6 +1935,7 @@ Kinetic.GlobalObject.extend(Kinetic.Group, Kinetic.Node);
*/ */
Kinetic.Shape = function(config) { Kinetic.Shape = function(config) {
this.className = 'Shape'; this.className = 'Shape';
this.data = [];
// defaults // defaults
if(config.stroke !== undefined || config.strokeWidth !== undefined) { if(config.stroke !== undefined || config.strokeWidth !== undefined) {
@ -2060,6 +2063,23 @@ Kinetic.Shape.prototype = {
setDrawFunc: function(func) { setDrawFunc: function(func) {
this.drawFunc = func; this.drawFunc = func;
}, },
/**
* save shape data when using pixel detection.
*/
save: function() {
var stage = this.getStage();
var w = stage.width;
var h = stage.height;
var bufferLayer = stage.bufferLayer;
var bufferLayerContext = bufferLayer.getContext();
bufferLayer.clear();
this._draw(bufferLayer);
var imageData = bufferLayerContext.getImageData(0, 0, w, h);
this.data = imageData.data;
},
/** /**
* draw shape * draw shape
* @param {Layer} layer Layer that the shape will be drawn on * @param {Layer} layer Layer that the shape will be drawn on
@ -2108,40 +2128,10 @@ Kinetic.Shape.prototype = {
return pathLayerContext.isPointInPath(pos.x, pos.y); return pathLayerContext.isPointInPath(pos.x, pos.y);
} }
else { else {
var ax = this.getAbsolutePosition().x; var w = stage.width;
var ay = this.getAbsolutePosition().y; var alpha = this.data[((w * pos.y) + pos.x) * 4 + 3];
var aw = this.getWidth();
var ah = this.getHeight();
/*
* TODO: need to also take into account absolute
* rotation, absolute scale, and center offsets
* to calculate the correct aw, ah, ax, and ay values. Also need
* to implement getHeight and getWidth methods for each Shape
* object
*/
// only check pixels if it's possibly in range
if(pos.x >= ax && pos.x <= (ax + aw) && pos.y >= ay && pos.y <= ay + ah) {
var bufferLayer = stage.bufferLayer;
var bufferLayerContext = bufferLayer.getContext();
this._draw(bufferLayer);
var px = pos.x - ax;
var py = pos.y - ay;
// only get the image data for possible area
var imageData = bufferLayerContext.getImageData(ax, ay, aw, ah);
var data = imageData.data;
var alpha = data[((aw * py) + px) * 4 + 3];
return (alpha !== undefined && alpha !== 0); return (alpha !== undefined && alpha !== 0);
} }
else {
return false;
}
}
} }
}; };
// extend Node // extend Node

File diff suppressed because one or more lines are too long

View File

@ -17,6 +17,7 @@
*/ */
Kinetic.Shape = function(config) { Kinetic.Shape = function(config) {
this.className = 'Shape'; this.className = 'Shape';
this.data = [];
// defaults // defaults
if(config.stroke !== undefined || config.strokeWidth !== undefined) { if(config.stroke !== undefined || config.strokeWidth !== undefined) {
@ -144,6 +145,23 @@ Kinetic.Shape.prototype = {
setDrawFunc: function(func) { setDrawFunc: function(func) {
this.drawFunc = func; this.drawFunc = func;
}, },
/**
* save shape data when using pixel detection.
*/
save: function() {
var stage = this.getStage();
var w = stage.width;
var h = stage.height;
var bufferLayer = stage.bufferLayer;
var bufferLayerContext = bufferLayer.getContext();
bufferLayer.clear();
this._draw(bufferLayer);
var imageData = bufferLayerContext.getImageData(0, 0, w, h);
this.data = imageData.data;
},
/** /**
* draw shape * draw shape
* @param {Layer} layer Layer that the shape will be drawn on * @param {Layer} layer Layer that the shape will be drawn on
@ -192,40 +210,10 @@ Kinetic.Shape.prototype = {
return pathLayerContext.isPointInPath(pos.x, pos.y); return pathLayerContext.isPointInPath(pos.x, pos.y);
} }
else { else {
var ax = this.getAbsolutePosition().x; var w = stage.width;
var ay = this.getAbsolutePosition().y; var alpha = this.data[((w * pos.y) + pos.x) * 4 + 3];
var aw = this.getWidth();
var ah = this.getHeight();
/*
* TODO: need to also take into account absolute
* rotation, absolute scale, and center offsets
* to calculate the correct aw, ah, ax, and ay values. Also need
* to implement getHeight and getWidth methods for each Shape
* object
*/
// only check pixels if it's possibly in range
if(pos.x >= ax && pos.x <= (ax + aw) && pos.y >= ay && pos.y <= ay + ah) {
var bufferLayer = stage.bufferLayer;
var bufferLayerContext = bufferLayer.getContext();
this._draw(bufferLayer);
var px = pos.x - ax;
var py = pos.y - ay;
// only get the image data for possible area
var imageData = bufferLayerContext.getImageData(ax, ay, aw, ah);
var data = imageData.data;
var alpha = data[((aw * py) + px) * 4 + 3];
return (alpha !== undefined && alpha !== 0); return (alpha !== undefined && alpha !== 0);
} }
else {
return false;
}
}
} }
}; };
// extend Node // extend Node

View File

@ -198,11 +198,11 @@ Kinetic.Stage.prototype = {
* @param {String} typesStr * @param {String} typesStr
* @param {function} handler * @param {function} handler
*/ */
onContainer: function(typesStr, handler) { onContent: function(typesStr, handler) {
var types = typesStr.split(' '); var types = typesStr.split(' ');
for(var n = 0; n < types.length; n++) { for(var n = 0; n < types.length; n++) {
var baseEvent = types[n]; var baseEvent = types[n];
this.container.addEventListener(baseEvent, handler, false); this.content.addEventListener(baseEvent, handler, false);
} }
}, },
/** /**
@ -248,6 +248,12 @@ Kinetic.Stage.prototype = {
getContainer: function() { getContainer: function() {
return this.container; return this.container;
}, },
/**
* get content DOM element
*/
getContent: function() {
return this.content;
},
/** /**
* get stage * get stage
*/ */
@ -436,8 +442,7 @@ Kinetic.Stage.prototype = {
this._setMousePosition(evt); this._setMousePosition(evt);
this._setTouchPosition(evt); this._setTouchPosition(evt);
this.pathLayer.clear();
this._clearDefaultLayers();
/* /*
* loop through layers. If at any point an event * loop through layers. If at any point an event
@ -466,13 +471,6 @@ Kinetic.Stage.prototype = {
} }
}, },
/**
* clear default layers
*/
_clearDefaultLayers: function() {
this.bufferLayer.clear();
this.pathLayer.clear();
},
/** /**
* begin listening for events by adding event handlers * begin listening for events by adding event handlers
* to the container * to the container
@ -481,18 +479,18 @@ Kinetic.Stage.prototype = {
var that = this; var that = this;
// desktop events // desktop events
this.container.addEventListener('mousedown', function(evt) { this.content.addEventListener('mousedown', function(evt) {
that.mouseDown = true; that.mouseDown = true;
that._handleStageEvent(evt); that._handleStageEvent(evt);
}, false); }, false);
this.container.addEventListener('mousemove', function(evt) { this.content.addEventListener('mousemove', function(evt) {
that.mouseUp = false; that.mouseUp = false;
that.mouseDown = false; that.mouseDown = false;
that._handleStageEvent(evt); that._handleStageEvent(evt);
}, false); }, false);
this.container.addEventListener('mouseup', function(evt) { this.content.addEventListener('mouseup', function(evt) {
that.mouseUp = true; that.mouseUp = true;
that.mouseDown = false; that.mouseDown = false;
that._handleStageEvent(evt); that._handleStageEvent(evt);
@ -500,11 +498,11 @@ Kinetic.Stage.prototype = {
that.clickStart = false; that.clickStart = false;
}, false); }, false);
this.container.addEventListener('mouseover', function(evt) { this.content.addEventListener('mouseover', function(evt) {
that._handleStageEvent(evt); that._handleStageEvent(evt);
}, false); }, false);
this.container.addEventListener('mouseout', function(evt) { this.content.addEventListener('mouseout', function(evt) {
// if there's a current target shape, run mouseout handlers // if there's a current target shape, run mouseout handlers
var targetShape = that.targetShape; var targetShape = that.targetShape;
if(targetShape) { if(targetShape) {
@ -513,18 +511,18 @@ Kinetic.Stage.prototype = {
that.mousePos = undefined; that.mousePos = undefined;
}, false); }, false);
// mobile events // mobile events
this.container.addEventListener('touchstart', function(evt) { this.content.addEventListener('touchstart', function(evt) {
evt.preventDefault(); evt.preventDefault();
that.touchStart = true; that.touchStart = true;
that._handleStageEvent(evt); that._handleStageEvent(evt);
}, false); }, false);
this.container.addEventListener('touchmove', function(evt) { this.content.addEventListener('touchmove', function(evt) {
evt.preventDefault(); evt.preventDefault();
that._handleStageEvent(evt); that._handleStageEvent(evt);
}, false); }, false);
this.container.addEventListener('touchend', function(evt) { this.content.addEventListener('touchend', function(evt) {
evt.preventDefault(); evt.preventDefault();
that.touchEnd = true; that.touchEnd = true;
that._handleStageEvent(evt); that._handleStageEvent(evt);
@ -535,8 +533,8 @@ Kinetic.Stage.prototype = {
* @param {Event} evt * @param {Event} evt
*/ */
_setMousePosition: function(evt) { _setMousePosition: function(evt) {
var mouseX = evt.offsetX || (evt.clientX - this._getContainerPosition().left + window.pageXOffset); var mouseX = evt.offsetX || (evt.clientX - this._getContentPosition().left + window.pageXOffset);
var mouseY = evt.offsetY || (evt.clientY - this._getContainerPosition().top + window.pageYOffset); var mouseY = evt.offsetY || (evt.clientY - this._getContentPosition().top + window.pageYOffset);
this.mousePos = { this.mousePos = {
x: mouseX, x: mouseX,
y: mouseY y: mouseY
@ -551,8 +549,8 @@ Kinetic.Stage.prototype = {
// one finger // one finger
var touch = evt.touches[0]; var touch = evt.touches[0];
// Get the information for finger #1 // Get the information for finger #1
var touchX = touch.clientX - this._getContainerPosition().left + window.pageXOffset; var touchX = touch.clientX - this._getContentPosition().left + window.pageXOffset;
var touchY = touch.clientY - this._getContainerPosition().top + window.pageYOffset; var touchY = touch.clientY - this._getContentPosition().top + window.pageYOffset;
this.touchPos = { this.touchPos = {
x: touchX, x: touchX,
@ -563,8 +561,8 @@ Kinetic.Stage.prototype = {
/** /**
* get container position * get container position
*/ */
_getContainerPosition: function() { _getContentPosition: function() {
var obj = this.container; var obj = this.content;
var top = 0; var top = 0;
var left = 0; var left = 0;
while(obj && obj.tagName !== 'BODY') { while(obj && obj.tagName !== 'BODY') {
@ -618,7 +616,7 @@ Kinetic.Stage.prototype = {
_prepareDrag: function() { _prepareDrag: function() {
var that = this; var that = this;
this.onContainer('mousemove touchmove', function(evt) { this.onContent('mousemove touchmove', function(evt) {
var go = Kinetic.GlobalObject; var go = Kinetic.GlobalObject;
var node = go.drag.node; var node = go.drag.node;
if(node) { if(node) {
@ -700,7 +698,7 @@ Kinetic.Stage.prototype = {
} }
}, false); }, false);
this.onContainer('mouseup touchend mouseout', function(evt) { this.onContent('mouseup touchend mouseout', function(evt) {
that._endDrag(evt); that._endDrag(evt);
}); });
}, },
@ -717,8 +715,12 @@ Kinetic.Stage.prototype = {
this.container.appendChild(this.content); this.container.appendChild(this.content);
// default layers // default layers
this.bufferLayer = new Kinetic.Layer(); this.bufferLayer = new Kinetic.Layer({
this.pathLayer = new Kinetic.Layer(); name: 'bufferLayer'
});
this.pathLayer = new Kinetic.Layer({
name: 'pathLayer'
});
// set parents // set parents
this.bufferLayer.parent = this; this.bufferLayer.parent = this;

View File

@ -548,16 +548,17 @@ Test.prototype.tests = {
log('mouseout'); log('mouseout');
}); });
darth.on('dragend', function() {
this.save();
});
layer.add(darth); layer.add(darth);
stage.add(layer); stage.add(layer);
//darth.save();
}; };
imageObj.src = '../lion.png'; imageObj.src = '../lion.png';
}, },
/*
* TODO: need to implement area x, y, width and height
* in order to support other shape pixel detection
*/
/*
'EVENTS - star pixel detection': function(containerId) { 'EVENTS - star pixel detection': function(containerId) {
var imageObj = new Image(); var imageObj = new Image();
imageObj.onload = function() { imageObj.onload = function() {
@ -591,12 +592,17 @@ Test.prototype.tests = {
log('mouseout'); log('mouseout');
}); });
star.on('dragend', function() {
this.save();
});
layer.add(star); layer.add(star);
stage.add(layer); stage.add(layer);
star.save();
}; };
imageObj.src = '../lion.png'; imageObj.src = '../lion.png';
}, },
*/
'EVENTS - drag events click': function(containerId) { 'EVENTS - drag events click': function(containerId) {
var stage = new Kinetic.Stage({ var stage = new Kinetic.Stage({
container: containerId, container: containerId,