From 039c9059a0df4fda76ec877ebc53f339c3f805b7 Mon Sep 17 00:00:00 2001 From: Eric Rowell Date: Tue, 13 Mar 2012 22:11:22 -0700 Subject: [PATCH] rewrote drag and drop logic. removed draggableX and draggableY. added new dragConstraint property and new dragBounds property --- dist/kinetic-core.js | 153 +++++++++++++++++++-------------------- dist/kinetic-core.min.js | 2 +- src/Node.js | 102 +++++++++++--------------- src/Stage.js | 51 ++++++++----- tests/functionalTests.js | 151 +++++++++++++++++++++++++++++++++++++- tests/unitTests.js | 47 ++++++++++++ 6 files changed, 350 insertions(+), 156 deletions(-) diff --git a/dist/kinetic-core.js b/dist/kinetic-core.js index cb2a8fbe..b0d715e1 100644 --- a/dist/kinetic-core.js +++ b/dist/kinetic-core.js @@ -228,10 +228,9 @@ Kinetic.Node = function(config) { y: 0 }; this.eventListeners = {}; - this.drag = { - x: false, - y: false - }; + this.dragConstraint = "none"; + this.dragBounds = {}; + this._draggable = false; // set properties from config if(config) { @@ -241,12 +240,6 @@ Kinetic.Node = function(config) { case "draggable": this.draggable(config[key]); break; - case "draggableX": - this.draggableX(config[key]); - break; - case "draggableY": - this.draggableY(config[key]); - break; case "listen": this.listen(config[key]); break; @@ -552,56 +545,17 @@ Kinetic.Node.prototype = { }, /** * enable or disable drag and drop - * @param {Boolean} setDraggable + * @param {Boolean} isDraggable */ - draggable: function(setDraggable) { - if(setDraggable) { - var needInit = !this.drag.x && !this.drag.y; - this.drag.x = true; - this.drag.y = true; - - if(needInit) { + draggable: function(isDraggable) { + if(this.draggable !== isDraggable) { + if(isDraggable) { this._initDrag(); } - } - else { - 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(); + else { + this._dragCleanup(); } - } - 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(); + this._draggable = isDraggable; } }, /** @@ -720,6 +674,36 @@ Kinetic.Node.prototype = { 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 */ @@ -741,10 +725,8 @@ Kinetic.Node.prototype = { * remove drag and drop event listener */ _dragCleanup: function() { - if(!this.drag.x && !this.drag.y) { - this.off("mousedown.initdrag"); - this.off("touchstart.initdrag"); - } + this.off("mousedown.initdrag"); + this.off("touchstart.initdrag"); }, /** * handle node events @@ -1029,7 +1011,8 @@ Kinetic.Stage.prototype = { if(scaleY) { this.scale.x = scaleX; this.scale.y = scaleY; - } else { + } + else { this.scale.x = scaleX; this.scale.y = scaleX; } @@ -1049,7 +1032,6 @@ Kinetic.Stage.prototype = { } } } - scaleChildren(layers); }, /** @@ -1084,14 +1066,14 @@ Kinetic.Stage.prototype = { n++; if(n < layers.length) { addLayer(n); - } else { + } + else { callback(bufferLayer.getCanvas().toDataURL()); } }; imageObj.src = dataURL; } - bufferLayer.clear(); addLayer(0); }, @@ -1194,7 +1176,8 @@ Kinetic.Stage.prototype = { return true; } // handle onmouseup & onclick - else if(this.mouseUp) { + else + if(this.mouseUp) { this.mouseUp = false; shape._handleEvents("onmouseup", evt); @@ -1220,7 +1203,8 @@ Kinetic.Stage.prototype = { } // handle touchstart - else if(this.touchStart) { + else + if(this.touchStart) { this.touchStart = false; shape._handleEvents("touchstart", evt); @@ -1240,20 +1224,23 @@ Kinetic.Stage.prototype = { } // handle touchend - else if(this.touchEnd) { + else + if(this.touchEnd) { this.touchEnd = false; shape._handleEvents("touchend", evt); return true; } // handle touchmove - else if(!isDragging && el.touchmove) { + else + if(!isDragging && el.touchmove) { shape._handleEvents("touchmove", evt); return true; } //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 */ @@ -1274,13 +1261,15 @@ Kinetic.Stage.prototype = { } // handle onmousemove - else if(!isDragging) { + else + if(!isDragging) { shape._handleEvents("onmousemove", evt); return true; } } // 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; shape._handleEvents("onmouseout", evt); return true; @@ -1302,7 +1291,8 @@ Kinetic.Stage.prototype = { if(exit) { return true; } - } else { + } + else { this._traverseChildren(child); } } @@ -1483,13 +1473,22 @@ Kinetic.Stage.prototype = { this.on("mousemove touchmove", function(evt) { var go = Kinetic.GlobalObject; - if(go.drag.node) { + var node = go.drag.node; + if(node) { var pos = that.getUserPosition(); - if(go.drag.node.drag.x) { - go.drag.node.x = pos.x - go.drag.offset.x; + var ds = node.dragConstraint; + 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) { - go.drag.node.y = pos.y - go.drag.offset.y; + if(ds === "none" || ds === "vertical") { + 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(); diff --git a/dist/kinetic-core.min.js b/dist/kinetic-core.min.js index 43c07a89..813f1054 100644 --- a/dist/kinetic-core.min.js +++ b/dist/kinetic-core.min.js @@ -25,4 +25,4 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -var Kinetic={};Kinetic.GlobalObject={stages:[],idCounter:0,isAnimating:!1,frame:{time:0,timeDiff:0,lastTime:0},drag:{moving:!1,node:undefined,offset:{x:0,y:0}},extend:function(a,b){for(var c in b.prototype)b.prototype.hasOwnProperty(c)&&a.prototype[c]===undefined&&(a.prototype[c]=b.prototype[c])},_isaCanvasAnimating:function(){for(var a=0;a=h.config.duration*1e3?(this._endTransition.apply(h),this._removeTransition(h),h.config.callback!==undefined&&h.config.callback()):this._linearTransition.apply(h,[this.frame])}f&&e.draw()}}},_updateFrameObject:function(){var a=new Date,b=a.getTime();this.frame.lastTime===0?this.frame.lastTime=b:(this.frame.timeDiff=b-this.frame.lastTime,this.frame.lastTime=b,this.frame.time+=this.frame.timeDiff)},_animationLoop:function(){if(this.isAnimating){this._updateFrameObject(),this._runFrames();var a=this;requestAnimFrame(function(){a._animationLoop()})}},_handleAnimation:function(){var a=this;!this.isAnimating&&this._isaCanvasAnimating()?(this.isAnimating=!0,a._animationLoop()):this.isAnimating&&!this._isaCanvasAnimating()&&(this.isAnimating=!1,this.frame.lastTime=0)}},window.requestAnimFrame=function(a){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1e3/60)}}(),Kinetic.Node=function(a){this.visible=!0,this.isListening=!0,this.name=undefined,this.alpha=1,this.x=0,this.y=0,this.scale={x:1,y:1},this.rotation=0,this.centerOffset={x:0,y:0},this.eventListeners={},this.drag={x:!1,y:!1};if(a)for(var b in a)switch(b){case"draggable":this.draggable(a[b]);break;case"draggableX":this.draggableX(a[b]);break;case"draggableY":this.draggableY(a[b]);break;case"listen":this.listen(a[b]);break;case"rotationDeg":this.rotation=a[b]*Math.PI/180;break;default:this[b]=a[b]}this.centerOffset.x===undefined&&(this.centerOffset.x=0),this.centerOffset.y===undefined&&(this.centerOffset.y=0)},Kinetic.Node.prototype={on:function(a,b){var c=a.split(" ");for(var d=0;d1?g[1]:"";this.eventListeners[h]||(this.eventListeners[h]=[]),this.eventListeners[h].push({name:i,handler:b})}},off:function(a){var b=a.split(" ");for(var c=0;c1){var h=f[1];for(var i=0;i0&&(this.parent.children.splice(a,1),this.parent.children.splice(a-1,0,this),this.parent._setChildrenIndices())},moveToBottom:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.unshift(this),this.parent._setChildrenIndices()},setZIndex:function(a){var b=this.index;this.parent.children.splice(b,1),this.parent.children.splice(a,0,this),this.parent._setChildrenIndices()},setAlpha:function(a){this.alpha=a},getAlpha:function(){return this.alpha},getAbsoluteAlpha:function(){var a=1,b=this;while(b.className!=="Stage")a*=b.alpha,b=b.parent;return a},draggable:function(a){if(a){var b=!this.drag.x&&!this.drag.y;this.drag.x=!0,this.drag.y=!0,b&&this._initDrag()}else this.drag.x=!1,this.drag.y=!1,this._dragCleanup()},draggableX:function(a){if(a){var b=!this.drag.x&&!this.drag.y;this.drag.x=!0,b&&this._initDrag()}else this.drag.x=!1,this._dragCleanup()},draggableY:function(a){if(a){var b=!this.drag.x&&!this.drag.y;this.drag.y=!0,b&&this._initDrag()}else this.drag.y=!1,this._dragCleanup()},isDragging:function(){var a=Kinetic.GlobalObject;return a.drag.node!==undefined&&a.drag.node.id===this.id&&a.drag.moving},moveTo:function(a){var b=this.parent;b.children.splice(this.index,1),b._setChildrenIndices(),a.children.push(this),this.index=a.children.length-1,this.parent=a,a._setChildrenIndices(),this.name&&(b.childrenNames[this.name]=undefined,a.childrenNames[this.name]=this)},getParent:function(){return this.parent},getLayer:function(){return this.className==="Layer"?this:this.getParent().getLayer()},getStage:function(){return this.className==="Stage"?this:this.getParent().getStage()},getName:function(){return this.name},setCenterOffset:function(a,b){this.centerOffset.x=a,this.centerOffset.y=b},getCenterOffset:function(){return this.centerOffset},transitionTo:function(a){var b=this.getLayer(),c=this,d=a.duration*1e3,e={};for(var f in a)if(a.hasOwnProperty(f))if(a[f].x!==undefined||a[f].y!==undefined){e[f]={};var g=["x","y"];for(var h=0;h0)this.remove(this.children[0])},_remove:function(a){a.name!==undefined&&(this.childrenNames[a.name]=undefined),this.children.splice(a.index,1),this._setChildrenIndices(),a=undefined},_drawChildren:function(){var a=this.children;for(var b=0;b=0;d--){var e=c[d];if(e.className==="Shape"){var f=this._detectEvent(e,b);if(f)return!0}else this._traverseChildren(e)}return!1},_handleEvent:function(a){var b=Kinetic.GlobalObject;a||(a=window.event),this._setMousePosition(a),this._setTouchPosition(a);var c=this.backstageLayer;c.clear(),this.targetFound=!1;for(var d=this.children.length-1;d>=0;d--){var e=this.children[d];e.visible&&d>=0&&e.isListening&&this._traverseChildren(e,a)&&(d=-1)}},_listen:function(){var a=this;this.container.addEventListener("mousedown",function(b){a.mouseDown=!0,a._handleEvent(b)},!1),this.container.addEventListener("mousemove",function(b){a.mouseUp=!1,a.mouseDown=!1,a._handleEvent(b)},!1),this.container.addEventListener("mouseup",function(b){a.mouseUp=!0,a.mouseDown=!1,a._handleEvent(b),a.clickStart=!1},!1),this.container.addEventListener("mouseover",function(b){a._handleEvent(b)},!1),this.container.addEventListener("mouseout",function(b){a.mousePos=undefined},!1),this.container.addEventListener("touchstart",function(b){b.preventDefault(),a.touchStart=!0,a._handleEvent(b)},!1),this.container.addEventListener("touchmove",function(b){b.preventDefault(),a._handleEvent(b)},!1),this.container.addEventListener("touchend",function(b){b.preventDefault(),a.touchEnd=!0,a._handleEvent(b)},!1)},_setMousePosition:function(a){var b=a.clientX-this._getContainerPosition().left+window.pageXOffset,c=a.clientY-this._getContainerPosition().top+window.pageYOffset;this.mousePos={x:b,y:c}},_setTouchPosition:function(a){if(a.touches!==undefined&&a.touches.length===1){var b=a.touches[0],c=b.clientX-this._getContainerPosition().left+window.pageXOffset,d=b.clientY-this._getContainerPosition().top+window.pageYOffset;this.touchPos={x:c,y:d}}},_getContainerPosition:function(){var a=this.container,b=0,c=0;while(a&&a.tagName!=="BODY")b+=a.offsetTop,c+=a.offsetLeft,a=a.offsetParent;return{top:b,left:c}},_stripLayer:function(a){a.context.stroke=function(){},a.context.fill=function(){},a.context.fillRect=function(b,c,d,e){a.context.rect(b,c,d,e)},a.context.strokeRect=function(b,c,d,e){a.context.rect(b,c,d,e)},a.context.drawImage=function(){},a.context.fillText=function(){},a.context.strokeText=function(){}},_endDrag:function(a){var b=Kinetic.GlobalObject;b.drag.node&&b.drag.moving&&(b.drag.moving=!1,b.drag.node._handleEvents("ondragend",a)),b.drag.node=undefined},_prepareDrag:function(){var a=this;this.on("mousemove touchmove",function(b){var c=Kinetic.GlobalObject;if(c.drag.node){var d=a.getUserPosition();c.drag.node.drag.x&&(c.drag.node.x=d.x-c.drag.offset.x),c.drag.node.drag.y&&(c.drag.node.y=d.y-c.drag.offset.y),c.drag.node.getLayer().draw(),c.drag.moving||(c.drag.moving=!0,c.drag.node._handleEvents("ondragstart",b)),c.drag.node._handleEvents("ondragmove",b)}},!1),this.on("mouseup touchend mouseout",function(b){a._endDrag(b)})}},Kinetic.GlobalObject.extend(Kinetic.Stage,Kinetic.Container),Kinetic.Layer=function(a){this.className="Layer",this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),this.canvas.style.position="absolute",this.transitions=[],this.transitionIdCounter=0,Kinetic.Container.apply(this,[]),Kinetic.Node.apply(this,[a])},Kinetic.Layer.prototype={draw:function(){this._draw()},clear:function(){var a=this.getContext(),b=this.getCanvas();a.clearRect(0,0,b.width,b.height)},getCanvas:function(){return this.canvas},getContext:function(){return this.context},add:function(a){this._add(a)},remove:function(a){this._remove(a)},_draw:function(){this.clear(),this.visible&&this._drawChildren()}},Kinetic.GlobalObject.extend(Kinetic.Layer,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Layer,Kinetic.Node),Kinetic.Group=function(a){this.className="Group",Kinetic.Container.apply(this,[]),Kinetic.Node.apply(this,[a])},Kinetic.Group.prototype={add:function(a){this._add(a)},remove:function(a){this._remove(a)},_draw:function(){this.visible&&this._drawChildren()}},Kinetic.GlobalObject.extend(Kinetic.Group,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Group,Kinetic.Node),Kinetic.Shape=function(a){this.className="Shape";if(a.stroke!==undefined||a.strokeWidth!==undefined)a.stroke===undefined?a.stroke="black":a.strokeWidth===undefined&&(a.strokeWidth=2);this.drawFunc=a.drawFunc,Kinetic.Node.apply(this,[a])},Kinetic.Shape.prototype={getContext:function(){return this.tempLayer.getContext()},getCanvas:function(){return this.tempLayer.getCanvas()},fillStroke:function(){var a=this.getContext();this.fill!==undefined&&(a.fillStyle=this.fill,a.fill()),this.stroke!==undefined&&(a.lineWidth=this.strokeWidth===undefined?1:this.strokeWidth,a.strokeStyle=this.stroke,a.stroke())},setFill:function(a){this.fill=a},getFill:function(){return this.fill},setStroke:function(a){this.stroke=a},getStroke:function(){return this.stroke},setStrokeWidth:function(a){this.strokeWidth=a},getStrokeWidth:function(){return this.strokeWidth},_draw:function(a){if(this.visible){var b=a.getStage(),c=a.getContext(),d=[];d.unshift(this);var e=this.parent;while(e.className!=="Stage")d.unshift(e),e=e.parent;for(var f=0;f=h.config.duration*1e3?(this._endTransition.apply(h),this._removeTransition(h),h.config.callback!==undefined&&h.config.callback()):this._linearTransition.apply(h,[this.frame])}f&&e.draw()}}},_updateFrameObject:function(){var a=new Date,b=a.getTime();this.frame.lastTime===0?this.frame.lastTime=b:(this.frame.timeDiff=b-this.frame.lastTime,this.frame.lastTime=b,this.frame.time+=this.frame.timeDiff)},_animationLoop:function(){if(this.isAnimating){this._updateFrameObject(),this._runFrames();var a=this;requestAnimFrame(function(){a._animationLoop()})}},_handleAnimation:function(){var a=this;!this.isAnimating&&this._isaCanvasAnimating()?(this.isAnimating=!0,a._animationLoop()):this.isAnimating&&!this._isaCanvasAnimating()&&(this.isAnimating=!1,this.frame.lastTime=0)}},window.requestAnimFrame=function(a){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1e3/60)}}(),Kinetic.Node=function(a){this.visible=!0,this.isListening=!0,this.name=undefined,this.alpha=1,this.x=0,this.y=0,this.scale={x:1,y:1},this.rotation=0,this.centerOffset={x:0,y:0},this.eventListeners={},this.dragConstraint="none",this.dragBounds={},this._draggable=!1;if(a)for(var b in a)switch(b){case"draggable":this.draggable(a[b]);break;case"listen":this.listen(a[b]);break;case"rotationDeg":this.rotation=a[b]*Math.PI/180;break;default:this[b]=a[b]}this.centerOffset.x===undefined&&(this.centerOffset.x=0),this.centerOffset.y===undefined&&(this.centerOffset.y=0)},Kinetic.Node.prototype={on:function(a,b){var c=a.split(" ");for(var d=0;d1?g[1]:"";this.eventListeners[h]||(this.eventListeners[h]=[]),this.eventListeners[h].push({name:i,handler:b})}},off:function(a){var b=a.split(" ");for(var c=0;c1){var h=f[1];for(var i=0;i0&&(this.parent.children.splice(a,1),this.parent.children.splice(a-1,0,this),this.parent._setChildrenIndices())},moveToBottom:function(){var a=this.index;this.parent.children.splice(a,1),this.parent.children.unshift(this),this.parent._setChildrenIndices()},setZIndex:function(a){var b=this.index;this.parent.children.splice(b,1),this.parent.children.splice(a,0,this),this.parent._setChildrenIndices()},setAlpha:function(a){this.alpha=a},getAlpha:function(){return this.alpha},getAbsoluteAlpha:function(){var a=1,b=this;while(b.className!=="Stage")a*=b.alpha,b=b.parent;return a},draggable:function(a){this.draggable!==a&&(a?this._initDrag():this._dragCleanup(),this._draggable=!a)},isDragging:function(){var a=Kinetic.GlobalObject;return a.drag.node!==undefined&&a.drag.node.id===this.id&&a.drag.moving},moveTo:function(a){var b=this.parent;b.children.splice(this.index,1),b._setChildrenIndices(),a.children.push(this),this.index=a.children.length-1,this.parent=a,a._setChildrenIndices(),this.name&&(b.childrenNames[this.name]=undefined,a.childrenNames[this.name]=this)},getParent:function(){return this.parent},getLayer:function(){return this.className==="Layer"?this:this.getParent().getLayer()},getStage:function(){return this.className==="Stage"?this:this.getParent().getStage()},getName:function(){return this.name},setCenterOffset:function(a,b){this.centerOffset.x=a,this.centerOffset.y=b},getCenterOffset:function(){return this.centerOffset},transitionTo:function(a){var b=this.getLayer(),c=this,d=a.duration*1e3,e={};for(var f in a)if(a.hasOwnProperty(f))if(a[f].x!==undefined||a[f].y!==undefined){e[f]={};var g=["x","y"];for(var h=0;h0)this.remove(this.children[0])},_remove:function(a){a.name!==undefined&&(this.childrenNames[a.name]=undefined),this.children.splice(a.index,1),this._setChildrenIndices(),a=undefined},_drawChildren:function(){var a=this.children;for(var b=0;b=0;d--){var e=c[d];if(e.className==="Shape"){var f=this._detectEvent(e,b);if(f)return!0}else this._traverseChildren(e)}return!1},_handleEvent:function(a){var b=Kinetic.GlobalObject;a||(a=window.event),this._setMousePosition(a),this._setTouchPosition(a);var c=this.backstageLayer;c.clear(),this.targetFound=!1;for(var d=this.children.length-1;d>=0;d--){var e=this.children[d];e.visible&&d>=0&&e.isListening&&this._traverseChildren(e,a)&&(d=-1)}},_listen:function(){var a=this;this.container.addEventListener("mousedown",function(b){a.mouseDown=!0,a._handleEvent(b)},!1),this.container.addEventListener("mousemove",function(b){a.mouseUp=!1,a.mouseDown=!1,a._handleEvent(b)},!1),this.container.addEventListener("mouseup",function(b){a.mouseUp=!0,a.mouseDown=!1,a._handleEvent(b),a.clickStart=!1},!1),this.container.addEventListener("mouseover",function(b){a._handleEvent(b)},!1),this.container.addEventListener("mouseout",function(b){a.mousePos=undefined},!1),this.container.addEventListener("touchstart",function(b){b.preventDefault(),a.touchStart=!0,a._handleEvent(b)},!1),this.container.addEventListener("touchmove",function(b){b.preventDefault(),a._handleEvent(b)},!1),this.container.addEventListener("touchend",function(b){b.preventDefault(),a.touchEnd=!0,a._handleEvent(b)},!1)},_setMousePosition:function(a){var b=a.clientX-this._getContainerPosition().left+window.pageXOffset,c=a.clientY-this._getContainerPosition().top+window.pageYOffset;this.mousePos={x:b,y:c}},_setTouchPosition:function(a){if(a.touches!==undefined&&a.touches.length===1){var b=a.touches[0],c=b.clientX-this._getContainerPosition().left+window.pageXOffset,d=b.clientY-this._getContainerPosition().top+window.pageYOffset;this.touchPos={x:c,y:d}}},_getContainerPosition:function(){var a=this.container,b=0,c=0;while(a&&a.tagName!=="BODY")b+=a.offsetTop,c+=a.offsetLeft,a=a.offsetParent;return{top:b,left:c}},_stripLayer:function(a){a.context.stroke=function(){},a.context.fill=function(){},a.context.fillRect=function(b,c,d,e){a.context.rect(b,c,d,e)},a.context.strokeRect=function(b,c,d,e){a.context.rect(b,c,d,e)},a.context.drawImage=function(){},a.context.fillText=function(){},a.context.strokeText=function(){}},_endDrag:function(a){var b=Kinetic.GlobalObject;b.drag.node&&b.drag.moving&&(b.drag.moving=!1,b.drag.node._handleEvents("ondragend",a)),b.drag.node=undefined},_prepareDrag:function(){var a=this;this.on("mousemove touchmove",function(b){var c=Kinetic.GlobalObject,d=c.drag.node;if(d){var e=a.getUserPosition(),f=d.dragConstraint,g=d.dragBounds;if(f==="none"||f==="horizontal"){var h=e.x-c.drag.offset.x;(g.left===undefined||g.lefth)&&(d.x=h)}if(f==="none"||f==="vertical"){var i=e.y-c.drag.offset.y;(g.top===undefined||g.topi)&&(d.y=i)}c.drag.node.getLayer().draw(),c.drag.moving||(c.drag.moving=!0,c.drag.node._handleEvents("ondragstart",b)),c.drag.node._handleEvents("ondragmove",b)}},!1),this.on("mouseup touchend mouseout",function(b){a._endDrag(b)})}},Kinetic.GlobalObject.extend(Kinetic.Stage,Kinetic.Container),Kinetic.Layer=function(a){this.className="Layer",this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),this.canvas.style.position="absolute",this.transitions=[],this.transitionIdCounter=0,Kinetic.Container.apply(this,[]),Kinetic.Node.apply(this,[a])},Kinetic.Layer.prototype={draw:function(){this._draw()},clear:function(){var a=this.getContext(),b=this.getCanvas();a.clearRect(0,0,b.width,b.height)},getCanvas:function(){return this.canvas},getContext:function(){return this.context},add:function(a){this._add(a)},remove:function(a){this._remove(a)},_draw:function(){this.clear(),this.visible&&this._drawChildren()}},Kinetic.GlobalObject.extend(Kinetic.Layer,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Layer,Kinetic.Node),Kinetic.Group=function(a){this.className="Group",Kinetic.Container.apply(this,[]),Kinetic.Node.apply(this,[a])},Kinetic.Group.prototype={add:function(a){this._add(a)},remove:function(a){this._remove(a)},_draw:function(){this.visible&&this._drawChildren()}},Kinetic.GlobalObject.extend(Kinetic.Group,Kinetic.Container),Kinetic.GlobalObject.extend(Kinetic.Group,Kinetic.Node),Kinetic.Shape=function(a){this.className="Shape";if(a.stroke!==undefined||a.strokeWidth!==undefined)a.stroke===undefined?a.stroke="black":a.strokeWidth===undefined&&(a.strokeWidth=2);this.drawFunc=a.drawFunc,Kinetic.Node.apply(this,[a])},Kinetic.Shape.prototype={getContext:function(){return this.tempLayer.getContext()},getCanvas:function(){return this.tempLayer.getCanvas()},fillStroke:function(){var a=this.getContext();this.fill!==undefined&&(a.fillStyle=this.fill,a.fill()),this.stroke!==undefined&&(a.lineWidth=this.strokeWidth===undefined?1:this.strokeWidth,a.strokeStyle=this.stroke,a.stroke())},setFill:function(a){this.fill=a},getFill:function(){return this.fill},setStroke:function(a){this.stroke=a},getStroke:function(){return this.stroke},setStrokeWidth:function(a){this.strokeWidth=a},getStrokeWidth:function(){return this.strokeWidth},_draw:function(a){if(this.visible){var b=a.getStage(),c=a.getContext(),d=[];d.unshift(this);var e=this.parent;while(e.className!=="Stage")d.unshift(e),e=e.parent;for(var f=0;f newX)) { + node.x = newX; + } } - if(go.drag.node.drag.y) { - go.drag.node.y = pos.y - go.drag.offset.y; + if(ds === "none" || ds === "vertical") { + 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(); diff --git a/tests/functionalTests.js b/tests/functionalTests.js index 50373120..3641e1bb 100644 --- a/tests/functionalTests.js +++ b/tests/functionalTests.js @@ -558,6 +558,155 @@ function Test() { layer.add(circle1); layer.add(circle2); 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); + }, }; } \ No newline at end of file diff --git a/tests/unitTests.js b/tests/unitTests.js index 7baacfd5..875dfcb5 100644 --- a/tests/unitTests.js +++ b/tests/unitTests.js @@ -716,6 +716,53 @@ function Test() { 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) { var stage = new Kinetic.Stage(containerId, 578, 200); var layer = new Kinetic.Layer();