Search code examples
javascriptjqueryhtmlkineticjs

Distorting a shape in Canvas by Clicking and Dragging a particular border point


I'm creating a floor-plan application using HTML5 and kinetic js.Currently I'm able to create a rectangular container where you can drag and drop objects inside it. You can see it in action in this link http://jsfiddle.net/wQ8YA/29/

The js code to create it is..

//create a group
var group = new Kinetic.Group({
draggable: true //make group draggable
});
var rec = new Kinetic.Rect({
        x: 10,
        y: 330,
        width: 600,
        height: 600
         });
group.add(rec);

Currently the container looks like this

floor-plan

But i want to make it like this..

new floor plan

In the second image the user can distort the container by clicking on any point on the container border and dragging it.I tried doing it on my own but don't know where to start,what this concept is called and how to achieve it.Please Help..


Solution

  • Basics of how it's done:

    Your bottom image appears to be a set of 4 cubic Bezier curves forming a rectangle.

    The squares on the corners of the rectangle set the starting and ending points of the curve.

    The circles set the 2 control points of the curve.

    If the control points are positioned on the imaginary line between the starting and ending points then the curve becomes a line segment connecting the starting and ending points.

    If you add code to allow the user to drag the starting, ending and control points, you will have the functionality in your bottom image.

    You can use Kinetic.Rect, Kinetic.Circle and Kinetic.Line to allow the user to drag the control points.

    Since KineticJS does not have a Cubic Bezier Curve shape, you will have to use the Kinetic.Shape to custom draw the 4 curves based on the positions of the control points.

    Here's a simple example with 1 anchor that drag-alters the top-right corner:

    http://jsfiddle.net/m1erickson/t2mhpmrs/

    enter image description hereenter image description here

    <!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-v5.0.1.min.js"></script>
    <style>
    body{padding:20px;}
    #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);
    
        var g=new Kinetic.Group({
            x:100,y:100,
        });
        layer.add(g);
    
    
        var room=new Kinetic.Shape({
          x:0,
          y:0,
          width:100,
          height:100,
          stroke:"blue",
          fill: 'red',
          drawFunc: function(context) {
            var x=this.x();
            var y=this.y();
            var w=this.width();
            var h=this.height();
            var right=anchorTR.x();
            var top=anchorTR.y();
            context.beginPath();
            context.moveTo(x,y);
            // top
            context.bezierCurveTo(x+w/3,y,x+w*2/3,y,right,top);
            // right
            context.bezierCurveTo(x+w,y+h/3, x+w,y+h*2/3, x+w,y+h);
            // bottom
            context.bezierCurveTo(x+w*2/3,y+h, x+w/3,y+h, x,y+h);
            // left
            context.bezierCurveTo(x,y+h*2/3, x,y+h/3, x,y);
    
            context.fillStrokeShape(this);
          }
        });
    
        g.add(room);
    
    
        var anchorTR = new Kinetic.Circle({
            x:100,
            y:0,
            radius:8,
            fill:"green",
            stroke: 'black',
            strokeWidth: 1,
            draggable: true
        });
        g.add(anchorTR);
    
    
        layer.draw();
    
    
    }); // end $(function(){});
    
    </script>       
    </head>
    <body>
        <h4>Drag green circle to change red rect</h4>
        <div id="container"></div>
    </body>
    </html>