D3拖动并在v6中单击以获取分组的对象

问题描述

Mike's example的基础上,我试图弄清楚如何将概念转换为分组元素。我可以分别创建启动拖动和单击,但是在一起似乎无法使单击生效。下面是一个代码段-这只是我使用过的许多方法之一。

  const width = 400;
  const height = 300;
  const radius = 5;
  
  const svg = d3.select("#chart").append('svg')
    .attr('width','400')
    .attr('height','400')
    .style('border','solid 1px');

  const circles = d3.range(20).map(i => ({
    x: Math.random() * (width - radius * 2) + radius,y: Math.random() * (height - radius * 2) + radius,index: i
  }));

const group =  svg.selectAll("g").data(circles).enter()
      .append("g")
      .attr("cx",d => d.x)
      .attr("cy",d => d.y)            
      .call(d3.drag()
      .on("start",dragstarted)
      .on("drag",dragged)
      .on("end",dragended))
  
  group.append("circle")
      .attr("cx",d => d.y)            
      .attr("r",radius)
      .attr("fill",d => d3.schemeCategory10[d.index % 10])
      .on("click",clicked);
      
    group.append("text")
      .attr("x",d => d.x)
      .attr("y",d => d.y)            
      .style("fill",'white')
      .text('a')
    

  function clicked(event,d) {
    if (event.defaultPrevented) return; // dragged

    d3.select(this).transition()
        .attr("fill","black")
        .attr("r",radius * 2)
      .transition()
        .attr("r",radius)
        .attr("fill",d3.schemeCategory10[d.index % 10]);
  }


  function dragstarted() {
    d3.select(this).attr("stroke","black");
  }

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

  function dragended() {
    d3.select(this).attr("stroke",null);
  }
<script src="https://d3js.org/d3.v6.js"></script>
<div id="chart"></div>

解决方法

关键问题是您要为每个g设置cxcy属性,并在拖动时更新它们。 gcx不能定位cy元素,因此初始设置这些元素并在拖动时对其进行更新将保持不变。

您需要使用翻译来定位g元素:

  .attr("transform",d=> "translate("+[d.x,d.y]+")" )   

此外,您无需立即定位圆和文本-或每个x和y坐标将应用两次-一次应用于g,一次应用于子元素。

现在,您只需要为每个拖动更新翻译,例如:

function drag(event,d) {
    d.x = event.x;
    d.y = event.y;
    d3.select(this).raise().attr("transform",d.y]+")" )
}

哪个应该给你类似的东西

const width = 400;
  const height = 300;
  const radius = 5;
  
  const svg = d3.select("#chart").append('svg')
    .attr('width','400')
    .attr('height','400')
    .style('border','solid 1px');

  const circles = d3.range(20).map(i => ({
    x: Math.random() * (width - radius * 2) + radius,y: Math.random() * (height - radius * 2) + radius,index: i
  }));

const group =  svg.selectAll("g").data(circles).enter()
      .append("g")
      .attr("transform",d.y]+")" )            
      .call(d3.drag()
      .on("start",dragstarted)
      .on("drag",dragged)
      .on("end",dragended))
  
  group.append("circle")    
      .attr("r",radius)
      .attr("fill",d => d3.schemeCategory10[d.index % 10])
      .on("click",clicked);
      
    group.append("text")    
      .style("fill",'white')
      .text('a')
    

  function clicked(event,d) {
    if (event.defaultPrevented) return; // dragged

    d3.select(this).transition()
        .attr("fill","black")
        .attr("r",radius * 2)
      .transition()
        .attr("r",radius)
        .attr("fill",d3.schemeCategory10[d.index % 10]);
  }


  function dragstarted() {
    d3.select(this).attr("stroke","black");
  }

  function dragged(event,d.y]+")" )
  }

  function dragended() {
    d3.select(this).attr("stroke",null);
  }
<script src="https://d3js.org/d3.v6.js"></script>
<div id="chart"></div>