简体   繁体   中英

Resizable and Rotatable Rectangle D3?

I am trying to create a rectangle where I can resize and rotate it using handlers (small circles) located on the top of the rectangle. Similar to the most of the drawing tools that allow us to resize and rotate the shapes.

I added three circles on the top of my rectangle. One circle is for resizing the width of the rectangle (circle on the right side). Another rectangle is for resizing the height of the bar (circle on the top). Resizing the rectangle works perfectly.

 margin = {top: 40, right: 20, bottom: 30, left: 70},
      width = 600 - margin.left - margin.right,
      height = 400 - margin.top - margin.bottom;

    var svg  = d3.select('#canvas').attr("width",width).attr("height",height);

    gContainer =  svg.append('g')
                     .attr("class", "gContainer")
                     .attr("transform", function(d,i){
                          return "translate("+300+","+200+")"
                     })

    gBars = gContainer.append('g').attr("class", "gBar");


    gBars.append("rect")
         .attr("class", "Bar")
         .attr("fill","black")
         .attr("width", 40)
         .attr("height", function(d) { return height - 200})
         .style("opacity", 0.5);


    var handlerRadius = 3.5;
    handlerPointsPosition=[];


    elementWidth = Number(d3.select(".Bar").attr("width"));
    elementHeight = Number(d3.select(".Bar").attr("height"));

    x0 = 0 + (elementWidth/2) ;
    y0 = 0 ;

    x1 = 0 + (elementWidth);
    y1 = 0 +(elementHeight/2) ;

    x2= 0 + (elementWidth/2) ;
    y2= -20;

    handlerPointsPosition = [[x0,y0],[x1,y1],[x2,y2]];


    var rectangleHandlers = d3.behavior.drag()
              .origin(function(d) { return d; })
              .on("dragstart", dragstarted)
              .on("drag", dragged)




    gHandlerPoints= gContainer.selectAll("g.HandlerPoint")
       .data(handlerPointsPosition)
       .enter().append('g')
       .attr("class", "gHandlerPoint")
       .attr("id", function(d,i){return "gHandlerPoint_id_"+i;})
       .attr("transform", function(d,i){
         //console.log(d);
            return "translate("+d[0]+","+d[1]+")"
       })
       .call(rectangleHandlers);

    gHandlerPoints.append("circle")
              .attr("class", "handlerPoint")
              .style("fill", "white")
              .style("stroke", "blue")
              .attr("stroke","")
              .attr("r",function(d,i){return (i == 2 ? 4: 3.5);})
              .attr("id", function(d,i){return "HandlerPointId_"+i;})

    gContainer.append("line")
              .attr("class","handlerLine")
              .attr("x1", (elementWidth/2) )
              .attr("y1", 0- handlerRadius)
              .attr("x2", (elementWidth/2) )
              .attr("y2", -20 + handlerRadius)
              .attr("stroke-width", 1)
              .attr("stroke", "blue");


    function updateHandlerPosition(id, dX, dY)
    {
      d3.select(id).attr("transform", function(d,i){
             return "translate(" + [ dX, dY] + ")"
      })
    }

    function dragstarted(d,i) {
       dragIconX = d3.transform(d3.select(this).attr("transform")).translate[0];
       dragIconY = d3.transform(d3.select(this).attr("transform")).translate[1];

       barStartWidth = d3.select(".Bar").attr("width");
    }

    function dragged(d,i) {
      barHeight = d3.select(".Bar").attr("height");
      if(i == 0) // circle on the top edge of the bar
      {
        dragIconY = dragIconY + d3.mouse(this)[1];
        updateHandlerPosition("#gHandlerPoint_id_"+i, dragIconX, dragIconY );
        updateHandlerPosition("#gHandlerPoint_id_1", (barStartWidth), (barHeight/2) );

        var x = d3.transform(d3.select(".gContainer").attr("transform")).translate[0];
        var y = d3.transform(d3.select(".gContainer").attr("transform")).translate[1];

        d3.select(".gContainer").attr("transform", function(d,i){
               y = y + dragIconY;
               return "translate(" + [ x , y] + ")"
        })

        console.log(height, barHeight, barHeight - Number(dragIconY));
        d3.select(".Bar").attr("height", barHeight - Number(dragIconY));
      }
      else if (i==1) // circle on the right side of the bar
      {
        oldMouseX = dragIconX;
        dragIconX = d3.mouse(this)[0]+dragIconX;
        barWidth = dragIconX;
        updateHandlerPosition("#gHandlerPoint_id_"+i, dragIconX, dragIconY );
        updateHandlerPosition("#gHandlerPoint_id_0", (barWidth/2), 0 );
        updateHandlerPosition("#gHandlerPoint_id_2", (barWidth/2), -20);
        d3.select(".handlerLine").attr("x1",(barWidth/2)).attr("x2", (barWidth/2));
        d3.select(".Bar").attr("width", Math.abs(dragIconX));
      }
      else if(i==3) //circle on very top
      {

           // code for rotation should come here.

      }
    }

Link to jsFiddle: http://jsfiddle.net/Q5Jag/2103/

I put the third circle for rotation (the circle on the very top). However, I have no idea how to fix the rotation. I want the rectangle to rotate when I drag the circle on the very top. I also want to be able to resize the circle accordingly when it is rotated.

Any idea?

You can calculate the rotation with respect to the center of your rectangle. Consider v1 as vector from center of rectangle to the rotation handle before rotation and v2 as a vector from center of rectangle to the rotation handle after rotation . Read here to know the math behind it.

// old vector
v1 = {};
v1.x = oldMouseX-gCenterX;
v1.y = oldMouseY-gCenterY;

// new vector
v2 = {};
v2.x = dragHandleX-gCenterX;
v2.y = dragHandleY-gCenterY;

angle = Math.atan2(v2.y,v2.x) - Math.atan2(v1.y,v1.x);

where

  • gCenterX and gCenterY are coordinates of the center of rectangle
  • oldMouseX and oldMouseY are coordinates of mouse pointer prior to rotation
  • dragHandleX and dragHandleY are coordinates of mouse pointer after rotation

Here is the complete code: http://jsfiddle.net/Q5Jag/2109/ (But it fails if you mix rotation and resizing)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM