Unappend circle when pulled from a group

How do I disable a circle element when I drag it out of the group? Currently I can add it to the group, but I cannot cancel it on drag and drop. If anyone has an idea please help. I am new to d3js and javascript

Demo: http://jsfiddle.net/q7L9qdyv/

code:

var svg = d3.select("body").append("svg")
                        .attr("width", 800)
                        .attr("height", 803);

                //Draw the Circle  
                var circle = svg.append("circle")
                        .attr("r", 25)
                        .attr("cx", 35)
                        .attr("cy", 145)
                        .style("stroke-opacity", .9)
                        .style("stroke", "green")
                        .style("fill", "white")
                        .style("stroke-width", "2px")
                        .classed("baseCircle", true); // created a class to identify

                var targetCircle = circle;
                var tempCircle = circle;

                //unique id for circles
                function guid() 
                {
                    function _p8(s) 
                    {
                        var p = (Math.random().toString(16) + "000000000").substr(2, 8);
                        return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p;
                    }
                    return _p8() + _p8(true) + _p8(true) + _p8();
                }

                //function to move circles
                function moveCircle() 
                {
                    d3.select(this)
                            .attr('cx', d3.event.x)
                            .attr('cy', d3.event.y);
                }

                //Move the group along wth elements/Drag Behaviour for Group
                var dragGroup = d3.behavior.drag()
                        .origin(function () {
                            var g = this;
                            return {x: d3.transform(g.getAttribute("transform")).translate[0],
                                y: d3.transform(g.getAttribute("transform")).translate[1]};
                        })
                        .on("drag", function (d, i) {
                            g = this;
                            translate = d3.transform(g.getAttribute("transform")).translate;
                            console.log(translate);
                            x = d3.event.dx + translate[0],
                                    y = d3.event.dy + translate[1];
                            d3.select(g).attr("transform", "translate(" + x + "," + y + ")");
                            d3.event.sourceEvent.stopPropagation();
                        });

                //Group element
                var targetG = svg.append("g")
                        .call(dragGroup)
                        .style('cursor', 'move')
                        .attr("transform", "translate(150,150)");

                //Append rectangle to group
                targetG.append("rect")
                        .attr("fill", "none")
                        .style("stroke", "black")
                        .style("stroke-width", "2px")
                        .attr("width", 200)
                        .attr("height", 200)
                        .style("fill", "white");

                //Drag behaviour starts for circles
                var drag = d3.behavior.drag()
                        .on("dragstart", dragstart)
                        .on("drag", drag)
                        .on("dragend", dragend);

                //Start
                function dragstart() {
                    console.log("circle dragged is::" + d3.select(this).attr("id"));

                    if (d3.select(this).classed("baseCircle") === true) {

                        targetCircle = svg.append("circle")
                                .attr("r", 25)  //get radius from targetCircle and also styles?
                                .attr("cx", targetCircle.attr("cx"))
                                .attr("cy", targetCircle.attr("cy"))
                                .style("fill", "white")
                                .style("stroke", "green")
                                .style("stroke-width", "2px");
                        targetCircle.call(drag);
                    }
                    else
                    {
                        targetCircle = d3.select(this);
                        tempCircle = this;
                    }
                    targetCircle.classed("dragTarget", true).attr("id", "dragTargetId");
                }

                //Drag
                function drag()
                {
                    targetCircle.attr("cx", d3.event.x)
                            .attr("cy", d3.event.y);
                }

                //End
                function dragend(d) {
                    //Get event x and y
                    var tx = targetCircle.attr("cx"),
                            ty = targetCircle.attr("cy");

                    var flag = 0;
                    //Select all elements in svg
                    try {
                        var elemArr = svg.selectAll("*").each(function (d, i) {
                            //If the element is a member of a group, check
                            if ($(this.parentNode).is("g")) {
                                //Get coordinates
                                var box = this.getBBox();
                                var bx = box.x,
                                        by = box.y,
                                        bw = box.width,
                                        bh = box.height;
                                //Make shape inherit translate of parent element
                                var translate = d3.select(this.parentNode).attr("transform");
                                var translate = translate.substring(translate.indexOf("translate(") + 10, translate.length);
                                translate = (translate.substring(0, translate.indexOf(")"))).split(",");

                                bx += parseInt(translate[0]);
                                by += parseInt(translate[1]);
                                //Check if within x and y bounds
                                if (tx >= bx && tx <= (bx + bw) && !flag && ty >= by && ty <= (by + bh))
                                {
                                    //Flag to prevent further action
                                    flag = 1;
                                    //Append target circle to g element
                                    targetG.append("circle")
                                            .attr("r", 25)  //get radius from targetCircle and also styles?
                                            .attr("id", guid())
                                            .classed("circleAddedClass", true)
                                            .attr("cx", d3.mouse(this)[0])
                                            .attr("cy", d3.mouse(this)[1])
                                            .style("fill", "white")
                                            .style("stroke", "black")
                                            .style("stroke-width", "2px")
                                            .call(
                                                    d3.behavior.drag()
                                                    .on("dragstart", function () {
                                                        d3.event.sourceEvent.stopPropagation();
                                                    })
                                                    .on('drag', moveCircle).origin(function () {
                                                var t = d3.select(this);
                                                return {x: t.attr("cx"), y: t.attr("cy")};
                                            }));

                                    targetCircle.remove();
                                }
                            }
                        });
                    } catch (e) {
                        log(e);
                    }
                }
                function log(s) {
                    console.log(s);
                    return s;
                }
                circle.call(drag);

      

+3


source to share


3 answers


CSS

.baseCircle {
    stroke-opacity: 0.9;
    stroke: green;
    stroke-width: 2px;
    fill: white;
}
.dragTarget {
    stroke: green;
    stroke-width: 2px;
    fill: white;
}
.circleAddedClass {
    stroke: black;
    stroke-width: 2px;
    fill: white;
}

      


Script



I changed your code a little

// unique id for circles
function guid() {
    function _p8(s) {
        var p = (Math.random().toString(16) + "000000000").substr(2, 8);
        return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p;
    }
    return _p8() + _p8(true) + _p8(true) + _p8();
}

function getNode(d3Element) {
    return d3Element[0][0];
}

// check if within bounds
function isWithin(x, y, box) {
    return (x >= box.x && x <= (box.x + box.width) && y >= box.y && y <= (box.y + box.height))
}

function getTranslate(t) {
    var translate = d3.transform(t.getAttribute("transform")).translate;
    return {
        x: Number(translate[0]),
        y: Number(translate[1])
    };
}


var svg = d3.select("body")
            .append("svg")
            .attr("width", 800)
            .attr("height", 803);


// movement for a transformed element
var dragG = d3.behavior.drag()
                // set the origin to the start position
                .origin(function () {
                    return getTranslate(this);
                })
                .on("drag", function () {
                    d3.select(this).attr(
                        "transform",
                        "translate(" +
                            d3.event.x + "," +
                            d3.event.y + ")");
                });

// drop group
var targetG = svg.append("g")
                .style('cursor', 'move')
                .attr("transform", "translate(150,150)")
                .call(dragG)

// append rectangle to group
targetG.append("rect")
        .attr("fill", "none")
        .style("stroke", "black")
        .style("stroke-width", "2px")
        .attr("width", 200)
        .attr("height", 200)
        .style("fill", "white");


// drag functions
var draggedCircle;
var circleDrag = function () {
    draggedCircle
        .attr("cx", d3.event.x)
        .attr("cy", d3.event.y);
}

function circleDragEnd() {
    // get event x and y
    var dx = Number(draggedCircle.attr("cx"));
    var dy = Number(draggedCircle.attr("cy"));
    if (draggedCircle.classed("circleAddedClass")) {
        var t = getTranslate(getNode(draggedCircle).parentNode);
        console.log([dx, dy])
        dx += t.x;
        dy += t.y;
        console.log([dx, dy])
    }

    svg.selectAll("g").each(function () {
        var rect = d3.select(this).select("rect");
        if (rect.length === 1) {
            var box = getNode(rect).getBBox();

            // the actual coordinates of the element must include the parent transform too
            var t = getTranslate(this);
            box = {
                x: box.x + t.x,
                y: box.y + t.y,
                width: box.width,
                height: box.height
            }

            if (isWithin(dx, dy, box)) {
                if (!draggedCircle.classed("circleAddedClass")) {
                    draggedCircle = draggedCircle.remove()
                                        .attr("cx", dx - t.x)
                                        .attr("cy", dy - t.y)
                                        .attr("id", guid())
                                        .classed("dragTarget", false)
                                        .classed("circleAddedClass", true);

                    // append target circle to g element
                    this.appendChild(getNode(draggedCircle))
                }
            } else if (draggedCircle.classed("circleAddedClass")) {
                if (getNode(draggedCircle).parentNode === this) {
                    draggedCircle = draggedCircle.remove()
                                        .attr("cx", dx)
                                        .attr("cy", dy)
                                        .classed("dragTarget", true)
                                        .classed("circleAddedClass", false);

                    getNode(svg).appendChild(getNode(draggedCircle))
                }
            }
        }
    });
}

// drag behaviour for circle instances
var dragCircleInstance = d3.behavior.drag()
                            .on("dragstart", function () {
                                draggedCircle = d3.select(this)
                                d3.event.sourceEvent.stopPropagation();
                            })
                            .on("drag", circleDrag)
                            .on("dragend", circleDragEnd);

// drag behaviour for circle template
var dragCircleTemplate = d3.behavior.drag()
                            .on("dragstart", function () {
                                draggedCircle = svg.append("circle")
                                                .attr("r", 25)  //get radius from targetCircle and also styles?
                                                .attr("cx", d3.select(this).attr("cx"))
                                                .attr("cy", d3.select(this).attr("cy"))
                                                .classed("dragTarget", true)
                                                .call(dragCircleInstance);
                            })
                            .on("drag", circleDrag)
                            .on("dragend", circleDragEnd);

// draw the circle template
var circle = svg.append("circle")
                .attr("r", 25)
                .attr("cx", 35)
                .attr("cy", 145)
                .classed("baseCircle", true)
                .call(dragCircleTemplate);

      


Fiddle - http://jsfiddle.net/f7u11xm4/

+2


source


If I had to check the code, I would make the following suggestion:

  • Too many different drag and drop handlers. You need two: how to drag the group and how to drag the circle.
  • Think of it as a state machine and ask what are the different states of my circle - not in the field and not dragged, not unloaded or pulled out, in the field and being pulled out of the box and pulled out. Code for these capabilities.
  • I found how you make dragstart

    yours confusing and problem prone. I would just create a static circle that marks where you drag and clone at the end of the drag. This, of course, adds another state; initial movement to any point.

All this in mind, here is my recode.



<!DOCTYPE html>
<html>

  <head>
    <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
  </head>

  <body>
    <script>
    
    var svg = d3.select("body")
      .append("svg")
      .attr("width", 800)
      .attr("height", 803);
                        
    //Move the group along wth elements/Drag Behaviour for Group
    var dragGroup = d3.behavior.drag()
      .origin(function () {
          var g = this;
          return {
            x: d3.transform(g.getAttribute("transform")).translate[0],
            y: d3.transform(g.getAttribute("transform")).translate[1]
          };
      })
      .on("drag", function (d, i) {
          g = this;
          translate = d3.transform(g.getAttribute("transform")).translate;
          var x = d3.event.dx + translate[0],
              y = d3.event.dy + translate[1];
          d3.select(g).attr("transform", "translate(" + x + "," + y + ")");
          d3.event.sourceEvent.stopPropagation();
      });
      
      // circle drag
      function dragging(){
        var self = d3.select(this);
        self.attr("cx", d3.event.x)
          .attr("cy", d3.event.y);
      }
      
      // start
      function dragstart() {
        d3.event.sourceEvent.stopPropagation();
      }

      //End
      function dragend(d) {
        var self = d3.select(this);
        
        // is this the first time I'm dragged?
        // replace me with a new one
        if (self.classed('initialDragCircle')){
          // append a new inital draggable
          createInitialDragCircle();
          self.attr('class','dragCircle');
        }
        
        // get necessary coordinates
        var tx = self.attr('cx'),
            ty = self.attr('cy');
        
        var transG = d3.transform(targG.attr('transform')).translate;
        var bx = transG[0];
            by = transG[1];

        // was it outside of the box?
        if (self.classed('dragCircle')){
          var coords = d3.mouse(targG.node());
          var box = targRect.node().getBBox();
          // and now is it in box?
          if (coords[0] > 0 && coords[1] > 0 &&
              coords[0] < box.width && coords[1] < box.height) {
            
            targG.append("circle")
              .attr("r", 25)
              .attr("cx", coords[0])
              .attr("cy", coords[1])
              .style("stroke-opacity", .9)
              .style("stroke", "black")
              .style("fill", "white")
              .style("stroke-width", "2px")
              .attr('class','inBoxDragCircle')
              .call(drag);
            self.remove();
          }
        } 
        // was it in the box?
        else if (self.classed('inBoxDragCircle'))
        {
          var coords = d3.mouse(targG.node());
          var box = targRect.node().getBBox();
          // and now it is out of the box?
          if (coords[0] < 0 || coords[1] < 0 ||
              coords[0] > box.width || coords[1] > box.height) {
            coords = d3.mouse(svg.node());
            svg.append("circle")
              .attr("r", 25)
              .attr("cx", coords[0])
              .attr("cy", coords[1])
              .style("stroke-opacity", .9)
              .style("stroke", "green")
              .style("fill", "white")
              .style("stroke-width", "2px")
              .attr('class','dragCircle')
              .call(drag);
            self.remove();  
                
          }
        }
      }
      
    var drag = d3.behavior.drag()
      .on("dragstart", dragstart)
      .on("drag", dragging)
      .on("dragend", dragend);
      
    //Group element
    var targG = svg.append("g")
      .call(dragGroup)
      .style('cursor', 'move')
      .attr("transform", "translate(150,150)");
      
    var targRect = targG.append("rect")
      .attr("fill", "none")
      .style("stroke", "black")
      .style("stroke-width", "2px")
      .attr("width", 200)
      .attr("height", 200)
      .style("fill", "white");

    //Draw the Circle  
    svg.append("circle")
      .attr("r", 25)
      .attr("cx", 35)
      .attr("cy", 145)
      .style("stroke-opacity", .9)
      .style("stroke", "green")
      .style("fill", "white")
      .style("stroke-width", "2px")
      .classed("baseCircle", true);
    
    function createInitialDragCircle(){
      svg.append("circle")
        .attr("r", 25)
        .attr("cx", 35)
        .attr("cy", 145)
        .style("stroke-opacity", .9)
        .style("stroke", "green")
        .style("fill", "white")
        .style("stroke-width", "2px")
        .classed("initialDragCircle", true)
        .call(drag);
    };
    
    createInitialDragCircle();

    </script>
  </body>

</html>
      

Run codeHide result


+1


source


The pseudocode is as follows: -

  • Add id=xxx

    to the whole circle created.
  • function to define a circle inside or outside (using the top and bottom points of the container)
  • d3.select("_dragged_circle_ID_").append("_another_<g>_container_");

0


source







All Articles