rewrote drag and drop logic. removed draggableX and draggableY. added new dragConstraint property and new dragBounds property

This commit is contained in:
Eric Rowell 2012-03-13 22:11:22 -07:00
parent a0c385261e
commit 039c9059a0
6 changed files with 350 additions and 156 deletions

153
dist/kinetic-core.js vendored
View File

@ -228,10 +228,9 @@ Kinetic.Node = function(config) {
y: 0 y: 0
}; };
this.eventListeners = {}; this.eventListeners = {};
this.drag = { this.dragConstraint = "none";
x: false, this.dragBounds = {};
y: false this._draggable = false;
};
// set properties from config // set properties from config
if(config) { if(config) {
@ -241,12 +240,6 @@ Kinetic.Node = function(config) {
case "draggable": case "draggable":
this.draggable(config[key]); this.draggable(config[key]);
break; break;
case "draggableX":
this.draggableX(config[key]);
break;
case "draggableY":
this.draggableY(config[key]);
break;
case "listen": case "listen":
this.listen(config[key]); this.listen(config[key]);
break; break;
@ -552,56 +545,17 @@ Kinetic.Node.prototype = {
}, },
/** /**
* enable or disable drag and drop * enable or disable drag and drop
* @param {Boolean} setDraggable * @param {Boolean} isDraggable
*/ */
draggable: function(setDraggable) { draggable: function(isDraggable) {
if(setDraggable) { if(this.draggable !== isDraggable) {
var needInit = !this.drag.x && !this.drag.y; if(isDraggable) {
this.drag.x = true;
this.drag.y = true;
if(needInit) {
this._initDrag(); this._initDrag();
} }
} else {
else { this._dragCleanup();
this.drag.x = false;
this.drag.y = false;
this._dragCleanup();
}
},
/**
* enable or disable horizontal drag and drop
* @param {Boolean} setDraggable
*/
draggableX: function(setDraggable) {
if(setDraggable) {
var needInit = !this.drag.x && !this.drag.y;
this.drag.x = true;
if(needInit) {
this._initDrag();
} }
} this._draggable = isDraggable;
else {
this.drag.x = false;
this._dragCleanup();
}
},
/**
* enable or disable vertical drag and drop
* @param {Boolean} setDraggable
*/
draggableY: function(setDraggable) {
if(setDraggable) {
var needInit = !this.drag.x && !this.drag.y;
this.drag.y = true;
if(needInit) {
this._initDrag();
}
}
else {
this.drag.y = false;
this._dragCleanup();
} }
}, },
/** /**
@ -720,6 +674,36 @@ Kinetic.Node.prototype = {
changes: changes changes: changes
}); });
}, },
/**
* set drag constraint
* @param {String} constraint
*/
setDragConstraint: function(constraint) {
this.dragConstraint = constraint;
},
/**
* get drag constraint
*/
getDragConstraint: function() {
return this.dragConstraint;
},
/**
* set drag bounds
* @param {Object} bounds
* @config {Number} [left] left bounds position
* @config {Number} [top] top bounds position
* @config {Number} [right] right bounds position
* @config {Number} [bottom] bottom bounds position
*/
setDragBounds: function(bounds) {
this.dragBounds = bounds;
},
/**
* get drag bounds
*/
getDragBounds: function() {
return this.dragBounds;
},
/** /**
* initialize drag and drop * initialize drag and drop
*/ */
@ -741,10 +725,8 @@ Kinetic.Node.prototype = {
* remove drag and drop event listener * remove drag and drop event listener
*/ */
_dragCleanup: function() { _dragCleanup: function() {
if(!this.drag.x && !this.drag.y) { this.off("mousedown.initdrag");
this.off("mousedown.initdrag"); this.off("touchstart.initdrag");
this.off("touchstart.initdrag");
}
}, },
/** /**
* handle node events * handle node events
@ -1029,7 +1011,8 @@ Kinetic.Stage.prototype = {
if(scaleY) { if(scaleY) {
this.scale.x = scaleX; this.scale.x = scaleX;
this.scale.y = scaleY; this.scale.y = scaleY;
} else { }
else {
this.scale.x = scaleX; this.scale.x = scaleX;
this.scale.y = scaleX; this.scale.y = scaleX;
} }
@ -1049,7 +1032,6 @@ Kinetic.Stage.prototype = {
} }
} }
} }
scaleChildren(layers); scaleChildren(layers);
}, },
/** /**
@ -1084,14 +1066,14 @@ Kinetic.Stage.prototype = {
n++; n++;
if(n < layers.length) { if(n < layers.length) {
addLayer(n); addLayer(n);
} else { }
else {
callback(bufferLayer.getCanvas().toDataURL()); callback(bufferLayer.getCanvas().toDataURL());
} }
}; };
imageObj.src = dataURL; imageObj.src = dataURL;
} }
bufferLayer.clear(); bufferLayer.clear();
addLayer(0); addLayer(0);
}, },
@ -1194,7 +1176,8 @@ Kinetic.Stage.prototype = {
return true; return true;
} }
// handle onmouseup & onclick // handle onmouseup & onclick
else if(this.mouseUp) { else
if(this.mouseUp) {
this.mouseUp = false; this.mouseUp = false;
shape._handleEvents("onmouseup", evt); shape._handleEvents("onmouseup", evt);
@ -1220,7 +1203,8 @@ Kinetic.Stage.prototype = {
} }
// handle touchstart // handle touchstart
else if(this.touchStart) { else
if(this.touchStart) {
this.touchStart = false; this.touchStart = false;
shape._handleEvents("touchstart", evt); shape._handleEvents("touchstart", evt);
@ -1240,20 +1224,23 @@ Kinetic.Stage.prototype = {
} }
// handle touchend // handle touchend
else if(this.touchEnd) { else
if(this.touchEnd) {
this.touchEnd = false; this.touchEnd = false;
shape._handleEvents("touchend", evt); shape._handleEvents("touchend", evt);
return true; return true;
} }
// handle touchmove // handle touchmove
else if(!isDragging && el.touchmove) { else
if(!isDragging && el.touchmove) {
shape._handleEvents("touchmove", evt); shape._handleEvents("touchmove", evt);
return true; return true;
} }
//this condition is used to identify a new target shape. //this condition is used to identify a new target shape.
else if(!isDragging && (!this.targetShape || (!this.targetFound && shape.id !== this.targetShape.id))) { else
if(!isDragging && (!this.targetShape || (!this.targetFound && shape.id !== this.targetShape.id))) {
/* /*
* check if old target has an onmouseout event listener * check if old target has an onmouseout event listener
*/ */
@ -1274,13 +1261,15 @@ Kinetic.Stage.prototype = {
} }
// handle onmousemove // handle onmousemove
else if(!isDragging) { else
if(!isDragging) {
shape._handleEvents("onmousemove", evt); shape._handleEvents("onmousemove", evt);
return true; return true;
} }
} }
// 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) {
this.targetShape = undefined; this.targetShape = undefined;
shape._handleEvents("onmouseout", evt); shape._handleEvents("onmouseout", evt);
return true; return true;
@ -1302,7 +1291,8 @@ Kinetic.Stage.prototype = {
if(exit) { if(exit) {
return true; return true;
} }
} else { }
else {
this._traverseChildren(child); this._traverseChildren(child);
} }
} }
@ -1483,13 +1473,22 @@ Kinetic.Stage.prototype = {
this.on("mousemove touchmove", function(evt) { this.on("mousemove touchmove", function(evt) {
var go = Kinetic.GlobalObject; var go = Kinetic.GlobalObject;
if(go.drag.node) { var node = go.drag.node;
if(node) {
var pos = that.getUserPosition(); var pos = that.getUserPosition();
if(go.drag.node.drag.x) { var ds = node.dragConstraint;
go.drag.node.x = pos.x - go.drag.offset.x; var db = node.dragBounds;
if(ds === "none" || ds === "horizontal") {
var newX = pos.x - go.drag.offset.x;
if((db.left === undefined || db.left < newX) && (db.right === undefined || db.right > newX)) {
node.x = newX;
}
} }
if(go.drag.node.drag.y) { if(ds === "none" || ds === "vertical") {
go.drag.node.y = pos.y - go.drag.offset.y; var newY = pos.y - go.drag.offset.y;
if((db.top === undefined || db.top < newY) && (db.bottom === undefined || db.bottom > newY)) {
node.y = newY;
}
} }
go.drag.node.getLayer().draw(); go.drag.node.getLayer().draw();

File diff suppressed because one or more lines are too long

View File

@ -25,10 +25,9 @@ Kinetic.Node = function(config) {
y: 0 y: 0
}; };
this.eventListeners = {}; this.eventListeners = {};
this.drag = { this.dragConstraint = "none";
x: false, this.dragBounds = {};
y: false this._draggable = false;
};
// set properties from config // set properties from config
if(config) { if(config) {
@ -38,12 +37,6 @@ Kinetic.Node = function(config) {
case "draggable": case "draggable":
this.draggable(config[key]); this.draggable(config[key]);
break; break;
case "draggableX":
this.draggableX(config[key]);
break;
case "draggableY":
this.draggableY(config[key]);
break;
case "listen": case "listen":
this.listen(config[key]); this.listen(config[key]);
break; break;
@ -349,56 +342,17 @@ Kinetic.Node.prototype = {
}, },
/** /**
* enable or disable drag and drop * enable or disable drag and drop
* @param {Boolean} setDraggable * @param {Boolean} isDraggable
*/ */
draggable: function(setDraggable) { draggable: function(isDraggable) {
if(setDraggable) { if(this.draggable !== isDraggable) {
var needInit = !this.drag.x && !this.drag.y; if(isDraggable) {
this.drag.x = true;
this.drag.y = true;
if(needInit) {
this._initDrag(); this._initDrag();
} }
} else {
else { this._dragCleanup();
this.drag.x = false;
this.drag.y = false;
this._dragCleanup();
}
},
/**
* enable or disable horizontal drag and drop
* @param {Boolean} setDraggable
*/
draggableX: function(setDraggable) {
if(setDraggable) {
var needInit = !this.drag.x && !this.drag.y;
this.drag.x = true;
if(needInit) {
this._initDrag();
} }
} this._draggable = isDraggable;
else {
this.drag.x = false;
this._dragCleanup();
}
},
/**
* enable or disable vertical drag and drop
* @param {Boolean} setDraggable
*/
draggableY: function(setDraggable) {
if(setDraggable) {
var needInit = !this.drag.x && !this.drag.y;
this.drag.y = true;
if(needInit) {
this._initDrag();
}
}
else {
this.drag.y = false;
this._dragCleanup();
} }
}, },
/** /**
@ -517,6 +471,36 @@ Kinetic.Node.prototype = {
changes: changes changes: changes
}); });
}, },
/**
* set drag constraint
* @param {String} constraint
*/
setDragConstraint: function(constraint) {
this.dragConstraint = constraint;
},
/**
* get drag constraint
*/
getDragConstraint: function() {
return this.dragConstraint;
},
/**
* set drag bounds
* @param {Object} bounds
* @config {Number} [left] left bounds position
* @config {Number} [top] top bounds position
* @config {Number} [right] right bounds position
* @config {Number} [bottom] bottom bounds position
*/
setDragBounds: function(bounds) {
this.dragBounds = bounds;
},
/**
* get drag bounds
*/
getDragBounds: function() {
return this.dragBounds;
},
/** /**
* initialize drag and drop * initialize drag and drop
*/ */
@ -538,10 +522,8 @@ Kinetic.Node.prototype = {
* remove drag and drop event listener * remove drag and drop event listener
*/ */
_dragCleanup: function() { _dragCleanup: function() {
if(!this.drag.x && !this.drag.y) { this.off("mousedown.initdrag");
this.off("mousedown.initdrag"); this.off("touchstart.initdrag");
this.off("touchstart.initdrag");
}
}, },
/** /**
* handle node events * handle node events

View File

@ -147,7 +147,8 @@ Kinetic.Stage.prototype = {
if(scaleY) { if(scaleY) {
this.scale.x = scaleX; this.scale.x = scaleX;
this.scale.y = scaleY; this.scale.y = scaleY;
} else { }
else {
this.scale.x = scaleX; this.scale.x = scaleX;
this.scale.y = scaleX; this.scale.y = scaleX;
} }
@ -167,7 +168,6 @@ Kinetic.Stage.prototype = {
} }
} }
} }
scaleChildren(layers); scaleChildren(layers);
}, },
/** /**
@ -202,14 +202,14 @@ Kinetic.Stage.prototype = {
n++; n++;
if(n < layers.length) { if(n < layers.length) {
addLayer(n); addLayer(n);
} else { }
else {
callback(bufferLayer.getCanvas().toDataURL()); callback(bufferLayer.getCanvas().toDataURL());
} }
}; };
imageObj.src = dataURL; imageObj.src = dataURL;
} }
bufferLayer.clear(); bufferLayer.clear();
addLayer(0); addLayer(0);
}, },
@ -312,7 +312,8 @@ Kinetic.Stage.prototype = {
return true; return true;
} }
// handle onmouseup & onclick // handle onmouseup & onclick
else if(this.mouseUp) { else
if(this.mouseUp) {
this.mouseUp = false; this.mouseUp = false;
shape._handleEvents("onmouseup", evt); shape._handleEvents("onmouseup", evt);
@ -338,7 +339,8 @@ Kinetic.Stage.prototype = {
} }
// handle touchstart // handle touchstart
else if(this.touchStart) { else
if(this.touchStart) {
this.touchStart = false; this.touchStart = false;
shape._handleEvents("touchstart", evt); shape._handleEvents("touchstart", evt);
@ -358,20 +360,23 @@ Kinetic.Stage.prototype = {
} }
// handle touchend // handle touchend
else if(this.touchEnd) { else
if(this.touchEnd) {
this.touchEnd = false; this.touchEnd = false;
shape._handleEvents("touchend", evt); shape._handleEvents("touchend", evt);
return true; return true;
} }
// handle touchmove // handle touchmove
else if(!isDragging && el.touchmove) { else
if(!isDragging && el.touchmove) {
shape._handleEvents("touchmove", evt); shape._handleEvents("touchmove", evt);
return true; return true;
} }
//this condition is used to identify a new target shape. //this condition is used to identify a new target shape.
else if(!isDragging && (!this.targetShape || (!this.targetFound && shape.id !== this.targetShape.id))) { else
if(!isDragging && (!this.targetShape || (!this.targetFound && shape.id !== this.targetShape.id))) {
/* /*
* check if old target has an onmouseout event listener * check if old target has an onmouseout event listener
*/ */
@ -392,13 +397,15 @@ Kinetic.Stage.prototype = {
} }
// handle onmousemove // handle onmousemove
else if(!isDragging) { else
if(!isDragging) {
shape._handleEvents("onmousemove", evt); shape._handleEvents("onmousemove", evt);
return true; return true;
} }
} }
// 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) {
this.targetShape = undefined; this.targetShape = undefined;
shape._handleEvents("onmouseout", evt); shape._handleEvents("onmouseout", evt);
return true; return true;
@ -420,7 +427,8 @@ Kinetic.Stage.prototype = {
if(exit) { if(exit) {
return true; return true;
} }
} else { }
else {
this._traverseChildren(child); this._traverseChildren(child);
} }
} }
@ -601,13 +609,22 @@ Kinetic.Stage.prototype = {
this.on("mousemove touchmove", function(evt) { this.on("mousemove touchmove", function(evt) {
var go = Kinetic.GlobalObject; var go = Kinetic.GlobalObject;
if(go.drag.node) { var node = go.drag.node;
if(node) {
var pos = that.getUserPosition(); var pos = that.getUserPosition();
if(go.drag.node.drag.x) { var ds = node.dragConstraint;
go.drag.node.x = pos.x - go.drag.offset.x; var db = node.dragBounds;
if(ds === "none" || ds === "horizontal") {
var newX = pos.x - go.drag.offset.x;
if((db.left === undefined || db.left < newX) && (db.right === undefined || db.right > newX)) {
node.x = newX;
}
} }
if(go.drag.node.drag.y) { if(ds === "none" || ds === "vertical") {
go.drag.node.y = pos.y - go.drag.offset.y; var newY = pos.y - go.drag.offset.y;
if((db.top === undefined || db.top < newY) && (db.bottom === undefined || db.bottom > newY)) {
node.y = newY;
}
} }
go.drag.node.getLayer().draw(); go.drag.node.getLayer().draw();

View File

@ -558,6 +558,155 @@ function Test() {
layer.add(circle1); layer.add(circle1);
layer.add(circle2); layer.add(circle2);
stage.add(layer); stage.add(layer);
} },
"DRAG AND DROP - drag and drop constrianed horiztonally": function(containerId) {
var stage = new Kinetic.Stage(containerId, 578, 200);
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.width / 2,
y: stage.height / 2,
radius: 70,
fill: "red",
stroke: "black",
strokeWidth: 4,
draggable: true,
dragConstraint: "horizontal"
});
layer.add(circle);
stage.add(layer);
},
"DRAG AND DROP - drag and drop constrianed vertically": function(containerId) {
var stage = new Kinetic.Stage(containerId, 578, 200);
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.width / 2,
y: stage.height / 2,
radius: 70,
fill: "red",
stroke: "black",
strokeWidth: 4,
draggable: true,
dragConstraint: "vertical"
});
layer.add(circle);
stage.add(layer);
},
"DRAG AND DROP - drag and drop with explicit no constraint": function(containerId) {
var stage = new Kinetic.Stage(containerId, 578, 200);
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.width / 2,
y: stage.height / 2,
radius: 70,
fill: "red",
stroke: "black",
strokeWidth: 4,
draggable: true,
dragConstraint: "none"
});
layer.add(circle);
stage.add(layer);
},
"DRAG AND DROP - drag and drop with left bounds": function(containerId) {
var stage = new Kinetic.Stage(containerId, 578, 200);
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.width / 2,
y: stage.height / 2,
radius: 70,
fill: "red",
stroke: "black",
strokeWidth: 4,
draggable: true,
dragBounds: {
left: 150
}
});
layer.add(circle);
stage.add(layer);
},
"DRAG AND DROP - drag and drop with right bounds": function(containerId) {
var stage = new Kinetic.Stage(containerId, 578, 200);
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.width / 2,
y: stage.height / 2,
radius: 70,
fill: "red",
stroke: "black",
strokeWidth: 4,
draggable: true,
dragBounds: {
right: 400
}
});
layer.add(circle);
stage.add(layer);
},
"DRAG AND DROP - drag and drop with top bounds": function(containerId) {
var stage = new Kinetic.Stage(containerId, 578, 200);
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.width / 2,
y: stage.height / 2,
radius: 70,
fill: "red",
stroke: "black",
strokeWidth: 4,
draggable: true,
dragBounds: {
top: 80
}
});
layer.add(circle);
stage.add(layer);
},
"DRAG AND DROP - drag and drop with bottom bounds": function(containerId) {
var stage = new Kinetic.Stage(containerId, 578, 200);
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.width / 2,
y: stage.height / 2,
radius: 70,
fill: "red",
stroke: "black",
strokeWidth: 4,
draggable: true,
dragBounds: {
bottom: 120
}
});
layer.add(circle);
stage.add(layer);
},
"DRAG AND DROP - drag and drop with full rect bounds": function(containerId) {
var stage = new Kinetic.Stage(containerId, 578, 200);
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.width / 2,
y: stage.height / 2,
radius: 70,
fill: "red",
stroke: "black",
strokeWidth: 4,
draggable: true,
dragBounds: {
top: 80,
bottom: 120,
left: 150,
right: 578 - 150
}
});
layer.add(circle);
stage.add(layer);
},
}; };
} }

View File

@ -716,6 +716,53 @@ function Test() {
layer.draw(); layer.draw();
}, },
"NODE - test drag and drop properties and methods": function(containerId) {
var stage = new Kinetic.Stage(containerId, 578, 200);
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.width / 2,
y: stage.height / 2,
radius: 70,
fill: "green",
stroke: "black",
strokeWidth: 4,
name: "myCircle"
});
stage.add(layer);
layer.add(circle);
layer.draw();
// test defaults
test(circle._draggable === false, "draggable should be false");
test(circle.dragConstraint === "none", "drag constraint should be none");
test(circle.dragBounds.left === undefined, "drag left should be undefined");
test(circle.dragBounds.top === undefined, "drag top should be undefined");
test(circle.dragBounds.right === undefined, "drag right should be undefined");
test(circle.dragBounds.bottom === undefined, "drag bottom should be undefined");
test(circle.getDragConstraint() === "none", "drag constraint should be none");
test(circle.getDragBounds().bottom === undefined, "drag bottom should be undefined");
//change properties
circle.draggable(true);
circle.setDragConstraint("vertical");
circle.setDragBounds({
left: 50,
top: 100,
right: 150,
bottom: 200
});
// test new properties
test(circle._draggable === true, "draggable should be true");
test(circle.dragConstraint === "vertical", "drag constraint should be vertical");
test(circle.dragBounds.left === 50, "drag left should be 50");
test(circle.dragBounds.top === 100, "drag top should be 100");
test(circle.dragBounds.right === 150, "drag right should be 150");
test(circle.dragBounds.bottom === 200, "drag bottom should be 200");
test(circle.getDragConstraint() === "vertical", "drag constraint should be vertical");
test(circle.getDragBounds().bottom === 200, "drag bottom should be 200");
},
"STAGE - add layer then shape": function(containerId) { "STAGE - add layer then shape": function(containerId) {
var stage = new Kinetic.Stage(containerId, 578, 200); var stage = new Kinetic.Stage(containerId, 578, 200);
var layer = new Kinetic.Layer(); var layer = new Kinetic.Layer();