Search code examples
javascripthtmlcsskineticjsdiagramming

Diagram creater using KineticJS


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.


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>