Question

I'm working on a simple diagram editor using KineticJS. I would like to use two separate canvases for the palette area (that contains a number of Kinetic.Groups that represent the different nodes of the network I might create), and the diagramming area where I can add nodes from the palette via drag and drop, and then add connections between the various nodes as specific anchor points. I'm having trouble figuring out the drag and drop process from the palette canvas of (stationary) Kinetic.Groups over to the other canvas containing diagramming area. I'm guessing that I need to fire off of the dragstart event for the palette objects (although I don't want these themselves to be draggable), and then do something like create an opague copy of the palette object that can be dragged around, and finally dropped into the diagramming area (with full opacity).

Can groups be dragged outside of a the staging canvases boundaries? Maybe I need to generate an image when I start to drag from the palette, drag that image over, and then create another group when dropping into the diagramming area.

Does any one know of any examples that might point me in the right direction, or who can offer some insight (even code) into the required process. I've searched the KineticJS examples but can't quite find enough to get me going.

Was it helpful?

Solution

Here’s one way to drag nodes from a source palette into a destination group:

A Fiddle: http://jsfiddle.net/m1erickson/xtVyL/

enter image description here

Network nodes are represented by small icons (which are really small kinetic image objects).

The user can drag any icon from the source palette to any destination group.

The groups are just defined areas on the canvas, but could be Kinetc.Groups for more flexibility.

During the dragend event, a new duplicate copy of the dragged icon is created in the destination group.

After the dragend event is complete, the original palette icon is automatically moved from The newly created duplicate icon can be dragged around the destination group (but not outside that group).

Here is code and a Fiddle: http://jsfiddle.net/m1erickson/xtVyL/

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Prototype</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    <script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.5.5.min.js"></script>

<style>
#container{
  border:solid 1px #ccc;
  margin-top: 10px;
  width:350px;
  height:350px;
}
</style>        
<script>
$(function(){

    var stage = new Kinetic.Stage({
        container: 'container',
        width: 350,
        height: 350
    });
    var layer = new Kinetic.Layer();
    stage.add(layer);


    // image loader

    var imageURLs=[];
    var imagesOK=0;
    var imgs=[];
    imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/tempPC.png");
    imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/tempServer.png");
    imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/tempRouter.png");
    loadAllImages();

    function loadAllImages(callback){
        for (var i = 0; i < imageURLs.length; i++) {
            var img = new Image();
            imgs.push(img);
            img.onload = function(){ 
                imagesOK++; 
                if (imagesOK==imageURLs.length ) {
                    start();
                }
            }; 
            img.src = imageURLs[i];
        }      
    }


    // top icon positions
    var nextIconX=20;
    var nextIconY=20;

    // define groups
    var groups=[];
    groups.push({x:0,y:100,w:175,h:250,fill:"skyblue"});
    groups.push({x:175,y:100,w:175,h:250,fill:"cornsilk"});
    // add boundary info to each group
    // draw colored rect to show group area
    for(var i=0;i<groups.length;i++){
        var g=groups[i];
        g.left=g.x;
        g.right=g.x+g.w;
        g.top=g.y;
        g.bottom=g.y+g.h;
        var rect=new Kinetic.Rect({
            x:g.x,
            y:g.y,
            width:g.w,
            height:g.h,
            fill:g.fill,
            stroke:"gray"
        });
        layer.add(rect);
    }
    // hittest for each group
    function groupHit(x,y){
        for(var i=0;i<groups.length;i++){
            var g=groups[i];
            if(x>g.left && x<g.right && y>g.top && y<g.bottom){return(i);}
        }
        return(-1);
    }


    function start(){
        makePaletteIcon(imgs[0]);
        makePaletteIcon(imgs[1]);
        makePaletteIcon(imgs[2]);
        layer.draw();
    }


    function makePaletteIcon(img){

        // make an icon that stays in the pallette tray
        var fixedIcon=newImage(nextIconX,nextIconY,img,false);
        layer.add(fixedIcon);


        // make an icon that is dragged from the tray to a group
        var dragIcon=makeDraggableIcon(nextIconX,nextIconY,img);
        layer.add(dragIcon);

        // calc the next icon position
        nextIconX+=(img.width+20);

    }


    function makeDraggableIcon(x,y,img){

        var i=newImage(x,y,img,true);
        // 
        i.trayX=x;
        i.trayY=y;
        //
        i.setOpacity(0.50);

        i.on("dragend",function(){

            var x=this.getX();
            var y=this.getY();

            // if this pallette icon was not dropped in a group
            // put the icon back in the tray and return
            var hit=groupHit(x,y);
            if(hit==-1){
                this.setPosition(this.trayX,this.trayY);
                return;
            }

            // add a copy of this icon to the drop group  
            var component=newImage(x,y,this.getImage(),true);

            // set drag limits
            var group=groups[hit];
            component.maxDragLeft=group.left;
            component.maxDragRight=group.right;
            component.maxDragTop=group.top;
            component.maxDragBottom=group.bottom;

            // limit component dragging to inside the assigned group
            component.setDragBoundFunc(function(pos) {
                var xx=pos.x;
                var yy=pos.y;
                var w=this.getWidth();
                var h=this.getHeight();
                if(pos.x<this.maxDragLeft){xx=this.maxDragLeft;}
                if(pos.x+w>this.maxDragRight){xx=this.maxDragRight-w;}
                if(pos.y<this.maxDragTop){yy=this.maxDragTop;}
                if(pos.y+h>this.maxDragBottom){yy=this.maxDragBottom-h;}
                return{ x:xx, y:yy };
            });

            layer.add(component);

            // move the dragIcon back into the pallette tray
            this.setPosition(this.trayX,this.trayY);

            layer.draw();

        });

        return(i);
    }

    // make a new Kinetic.Image
    function newImage(x,y,img,isDraggable){
        var i=new Kinetic.Image({
            image:img,
            x: x,
            y: y,
            width: img.width,
            height: img.height,
            draggable:isDraggable
        });
        return(i);
    }


}); // end $(function(){});

</script>       
</head>

<body>
    <p>Drag any icon from top into blue or yellow group</p>
    <div id="container"></div>
</body>
</html>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top