D3 удалить круг на dbclick

Я использую Перетаскивание круга I от Bostock и D3 Mouse Event, поэтому я могу щелкнуть svg и создать круг, а также все они перетаскиваются. Это работает, хотя есть побочная проблема, если я дважды щелкаю при создании круга, иногда перетаскивание кругов заставляет их прыгать.

Но главная проблема заключается в том, что я хотел бы иметь возможность дважды щелкнуть по кругу, чтобы он исчез, а также удалить его из данных.

Когда круги нарисованы, я добавил событие dbclick, которое вызывает функцию

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

Эта функция также вызывается при создании нового круга.

Эта функция не удаляет круги, как правильно это сделать? И есть ли конфликт между одним кликом, который делает одно, и двойным кликом, который делает что-то другое?

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>


person Shane G    schedule 23.09.2017    source источник


Ответы (1)


Самая большая проблема, с которой вы столкнетесь в своем коде, — это отличить щелчок от двойного. Однако, поскольку вы конкретно спросили, как удалить круги, этот ответ будет касаться только этой проблемы.

Ваш код для удаления кругов имеет две проблемы.

Во-первых, это...

.on("dblclick", removeElement())

... немедленно вызовет removeElement и вернет его значение (которое, кстати, равно undefined). Это не то, что вы хотите.

Вместо этого сделайте следующее:

.on("dblclick", removeElement) 

Что то же самое:

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

Таким образом, removeElement будет вызываться только тогда, когда пользователь щелкнет кружок, а не сразу.

Вторая проблема заключается в следующем:

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

Поскольку с этим кругом все еще связаны данные, ваш выбор "выход" пуст.

Вместо этого должно быть:

d3.select(this).remove();

Вот ваш код с этими изменениями:

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: я убрал нажатие на SVG для создания кругов. Поскольку эта проблема (отличить щелчок от двойного щелчка) очень сложна, возможно, стоит задать новый отдельный вопрос.

person Gerardo Furtado    schedule 23.09.2017
comment
Теперь я вижу, что щелчок и двойной щелчок — плохая идея. Поэтому вместо этого я пытаюсь щелкнуть по холсту, чтобы создать круг, а затем щелкнуть по кругу, чтобы удалить его. Я задал этот вопрос здесь stackoverflow.com/questions/46390307/ Спасибо. - person Shane G; 24.09.2017