Search code examples
javascriptkineticjs

How to separate a group into two subgroups at dragmove?


I have a group which contains a number of rectangles. When I point a rectangle of the group and drag it, I want to separate the group at that point and have two subgroups. I have managed to do a simple case in the following jsfiddle example, however I am using a dblclick event. What happens is that I get the position of the rectangle clicked by using (var shape = evt.targetNode;) and then I recreate the two groups. I am doing this by firing 2 events.

My questions are:

  1. Can this operation be done by firing a single event -- a dragmove event? So that, whenever the user starts dragging the group, according to the direction of the drag, the group to be separated 'automatically'. More then that, I was wondering whenever would be possible that whenever the user starts dragging the group, according to the direction of the drag, the group to be separated 'automatically'.
  2. For this version I am using kinetic-v5.0.1.min.js. However, when I am using the latest kineticjs version (kinetic-v5.1.0.min.js), for some reason (var shape = evt.targetNode;) is not working anymore. Am I missing something here?

jsfiddle link: http://jsfiddle.net/maik18/tbYLe/16/

    group.on('dblclick', function() {

    var groups = stage.find('Group');

    var rects = stage.find('Rect');
    var group1= groups[0].getChildren().slice(0,globalPosition);
    var group2= groups[0].getChildren().slice(globalPosition,6);



    var newGroup1 = new Kinetic.Group({
        x: group1[0].getAbsolutePosition().x,
        y: group1[0].getAbsolutePosition().y-40,
        draggable:true
    });

    var newGroup2 = new Kinetic.Group({
        x: group2[0].getAbsolutePosition().x-globalPosition*100,
        y: group2[0].getAbsolutePosition().y-40,
        draggable: true
    });


    for (var i=0; i < group1.length; i++){
        newGroup1.add(group1[i]);
    }
    for (var i=0; i < group2.length; i++){
        newGroup2.add(group2[i]);
    }

    writeMessage(newGroup2.getChildren()[0].getAbsolutePosition().x);
    shapesLayer.add(newGroup1);
    shapesLayer.add(newGroup2);

    groups[0].getChildren().splice(0,6);
    shapesLayer.draw();

    groups[0].destroy();

});

running example: http://jsfiddle.net/maik18/tbYLe/16/embedded/result/


Solution

    1. Yes, you can use the dragmove event to sub-divide a group into 2 groups:

      • determine if the drag is moving left or right.

      • divide the children into 2 groups (1) Children that should be dragged & (2) Children that should should remain stationary at initial position.

      • create a new group an put the stationary children in that new group.

      • continue dragging the other children in the original group (no need to create 2 new groups--just reuse the original group since you're already dragging it anyway).

    2. "evt.targetNode has changed to evt.target when using event delegation" (see the change log for version 5.1.0: https://github.com/ericdrowell/KineticJS/wiki/Change-Log)

    Note: You can use stage.getIntersection(mousePosition) to determine which rectangle is under the mouse.

    Here's annotated code and a Demo: http://jsfiddle.net/m1erickson/8fuPJ/

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    <script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.1.0.min.js"></script>
    <style>
    body{padding:20px;}
    #container{
      border:solid 1px #ccc;
      margin-top: 10px;
      width:350px;
      height:350px;
    }
    </style>        
    <script>
    $(function(){
    
        // create a stage and a layer
        var stage = new Kinetic.Stage({
            container: 'container',
            width: 350,
            height: 350
        });
        var layer = new Kinetic.Layer();
        stage.add(layer);
    
    
        // make a template group
        // all new groups will be a clone of this template group
        // this template group knows how to sub-divide its children
        var templateGroup=new Kinetic.Group({
          draggable:true,
        });
    
        //
        templateGroup.on('dragstart',function(){
    
            // save the x-coordinate where the mouse started the drag
            // this x is used to determine if the drag is right or left
            var pos=stage.getPointerPosition();   
            this.startX=pos.x;
    
            // determine which rectangle is under the mouse
            // this rect will be the dividing point between this and a new group
            this.dragRect=stage.getIntersection(pos);
    
            // create a new group cloned from the templateGroup
            // (the templateGroup has code necessary to divide itself)
            if(this.dragRect){
                this.newGroup=templateGroup.clone();
                this.newGroup.position(this.initialPosition);
                this.newGroup.initialPosition=this.initialPosition;
                this.isDragging=true;
            }
    
        });
    
        //
        templateGroup.on('dragmove',function(){
    
            // performance: 
            // just do this function once
            // isDragging will be false if this Fn has already been done once
            if(!this.isDragging){return;}
    
            var pos=stage.getPointerPosition();
    
            // performance: 
            // just return if the drag hasn't gone right or left at least 1 pixel
            if(pos.x==this.startX){return;}
    
            // clear the isDragging flag
            this.isDragging=false;
    
            // flag indicating whether the mouse moved left or right
            var isRight=(pos.x>this.startX);
    
            // get the x coordinate of the rect under the cursor
            // this "x" is used to divide the current group in two
            var dragRectX=this.dragRect.x();
    
            // an array that will hold children to be move to the new group
            var newGroupChildren=[];
    
            // get the children of this group
            var children=this.getChildren();
    
            // enumerate all children and add any "non-dragging" rects to
            // the array of children to be moved to the new group
            children.each(function(child){
                if(isRight && child.x()<dragRectX){
                    newGroupChildren.push(child);
                }
                if(!isRight && child.x()>dragRectX){
                    newGroupChildren.push(child);
                }
            });
    
            // move "stationary" children from this group to the new group
            for(var i=0;i<newGroupChildren.length;i++){
                newGroupChildren[i].moveTo(this.newGroup);
            }
    
            // add the new group to the layer
            layer.add(this.newGroup);
    
            // redraw the layer
            layer.draw();
        });
    
        //
        templateGroup.on("dragend",function(){
    
            // store the resting position of this group
            // any future new subgroups will be positioned at this position
            this.initialPosition=this.position();
        });
    
    
        // add a group to the stage
        var group=templateGroup.clone();
        group.initialPosition=templateGroup.position();
        layer.add(group);
    
    
        // testing...add 6 boxes to the group
        for(var i=0;i<6;i++){
            var rect=new Kinetic.Rect({
                id:i,
                x:i*30+50,
                y:100,
                width:25,
                height:20,
                fill:randomColor(),
                stroke: 'black',
                strokeWidth: 2,
            });
            group.add(rect);
        }
        layer.draw();
    
        // utility function to create a random color
        function randomColor(){ 
            return('#'+Math.floor(Math.random()*16777215).toString(16));
        }
    
    
    }); // end $(function(){});
    
    </script>       
    </head>
    <body>
        <h4>Drag a rectangle to create a sub-group</h4>
        <div id="container"></div>
    </body>
    </html>