问题
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