D3 remove circle on dbclick

谁说我不能喝 提交于 2019-12-11 07:06:14

问题


I am using Bostock's Circle Dragging I and Will's D3 Mouse Event so i can click on svg and create a circle and also they are all draggable. That is working, although there is a side issue if I double click when creating a circle sometimes dragging circles causes them to jump around.

But the main issue is that I would like to be able to double click on a circle and have it disappear but also remove it from the data.

When circles are drawn I added a dbclick event which calls a function

  function removeElement(d) {
    // need to remove this object from data
    d3.select(this)
      .exit()
      .remove();
  }

This function is also called when a new circle is created.

This function is not removing circles, what is the correct way to do this? And is there a conflict between a single click doing one thing and a double click doing something else?

var svg = d3.select("svg"),
  width = +svg.attr("width"),
  height = +svg.attr("height"),
  radius = 32;

var data = [{
    x: 100,
    y: 200
  },
  {
    x: 200,
    y: 300
  },
  {
    x: 300,
    y: 200
  },
  {
    x: 400,
    y: 300
  }
];

var xScale = d3.scaleLinear()
  .domain([0, d3.max(data, function(d) {
    return d.x_pos
  })]).range([0, width]);

svg.selectAll("circle")
  .data(data)
  .enter().append("circle")
  .attr("cx", function(d) {
    return d.x;
  })
  .attr("cy", function(d) {
    return d.y;
  })
  .attr("r", radius)
  .style("fill", "lightblue")
  .attr('id', function(d, i) {
    return 'rect_' + i;
  })
  .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended))
  .on("dblclick", removeElement());

svg.on("click", function() {
  var coords = d3.mouse(this);

  var newData = {
    x: d3.event.x,
    y: d3.event.y
  };

  data.push(newData);

  svg.selectAll("circle") // For new circle, go through the update process
    .data(data)
    .enter()
    .append("circle")
    .attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    })
    .attr("r", radius)
    .style("fill", "red")
    .attr('id', function(d, i) {
      return 'circle_' + i;
    })
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended))
    .on("dblclick", removeElement());
})

function dragstarted(d) {
  d3.select(this).raise().classed("active", true);
}

function dragged(d) {
  d3.select(this)
    .attr("cx", d.x = d3.event.x)
    .attr("cy", d.y = d3.event.y);
}

function dragended(d) {
  d3.select(this)
    .classed("active", false);
}

function removeElement(d) {
  // need to remove this object from data
  d3.select(this)
    .exit()
    .remove();
}
.active {
  stroke: #000;
  stroke-width: 2px;
}
<!DOCTYPE html>
<meta charset="utf-8">
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>

回答1:


The biggest issue you'll face with your code is telling a click from a double click. However, since you asked specifically how to remove the circles, this answer will deal with that problem only.

Your code for removing the circles has two problems.

First, this...

.on("dblclick", removeElement())

... will call removeElement immediately and return its value (which, by the way, is undefined). This is not what you want.

Instead, do this:

.on("dblclick", removeElement) 

Which is the same of:

.on("dbclick", function(d){
    removeElement(d);
}

That way, removeElement will be called only when the user clicks the circle, not immediately.

The second problem is this:

d3.select(this).exit().remove();

Since there are still data associated with that circle, your "exit" selection is empty.

Instead of that, it should be:

d3.select(this).remove();

Here is your code with those changes:

var svg = d3.select("svg"),
  width = +svg.attr("width"),
  height = +svg.attr("height"),
  radius = 32;

var data = [{
    x: 100,
    y: 200
  },
  {
    x: 200,
    y: 300
  },
  {
    x: 300,
    y: 200
  },
  {
    x: 400,
    y: 300
  }
];

var xScale = d3.scaleLinear()
  .domain([0, d3.max(data, function(d) {
    return d.x_pos
  })]).range([0, width]);

svg.selectAll("circle")
  .data(data)
  .enter().append("circle")
  .attr("cx", function(d) {
    return d.x;
  })
  .attr("cy", function(d) {
    return d.y;
  })
  .attr("r", radius)
  .style("fill", "lightblue")
  .attr('id', function(d, i) {
    return 'rect_' + i;
  })
  .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended))
  .on("dblclick", removeElement);



function dragstarted(d) {
  d3.select(this).raise().classed("active", true);
}

function dragged(d) {
  d3.select(this)
    .attr("cx", d.x = d3.event.x)
    .attr("cy", d.y = d3.event.y);
}

function dragended(d) {
  d3.select(this)
    .classed("active", false);
}

function removeElement(d) {
  // need to remove this object from data
  d3.select(this)
    .remove();
}
.active {
  stroke: #000;
  stroke-width: 2px;
}
<!DOCTYPE html>
<meta charset="utf-8">
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>

PS: I removed the click on the SVG for creating the circles. Since that issue (distinguishing click from double click) is very complex, it may be worth a new, separated question.



来源:https://stackoverflow.com/questions/46384860/d3-remove-circle-on-dbclick

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!