D3.js Drag rect未按预期工作

D3.js Drag rect未按预期工作,d3.js,svg,drag,D3.js,Svg,Drag,我创建了一个d3可视化,它获取json数据,为每个数据点创建一个rect,然后在该rect中显示文本。但是,拖动 仅适用于第一个矩形 我想知道如何为每个矩形执行自然拖动操作 我的代码笔项目: 代码如下: drawNumbers = layout => { const width = innerWidth; const height = width * 0.5; const margin = { top: height * 0.05,

我创建了一个d3可视化,它获取json数据,为每个数据点创建一个rect,然后在该rect中显示文本。但是,拖动 仅适用于第一个矩形

我想知道如何为每个矩形执行自然拖动操作

我的代码笔项目:

代码如下:

drawNumbers = layout => {
    const width = innerWidth;
    const height = width * 0.5;
    const margin = {
        top: height * 0.05,
        bottom: height * 0.05,
        left: width * 0.05,
        right: width * 0.05
    };

    d3.json(layout).then(data => {

    const colsize = data[data.length-1].col;
    const rowsize = data[data.length-1].row;
    const blocksize = colsize < rowsize ? 
            (width - margin.left - margin.right) / colsize: 
            (height - margin.left - margin.right) / rowsize;

        function dragstarted(d) {
        }

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

        const dragended = (d) => {
        }

        const drag = d3
        .drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended);

        const svg = d3
        .select("#heatmap")
        .append("svg")
        .attr("width", width)
        .attr("height", height)
        .attr("transform", `translate(${margin.left}, ${margin.top})`)
        .selectAll("g")
        .data(data)
        .enter()
        .append("g")
        .call(drag)

        svg
        .selectAll("g")
        .data(data)
        .enter()
        .append("rect")
        .attr("id", "block")
        .attr("class", "block")
        .attr("x", (d, i) => blocksize * (i % colsize)) // relative to 'svg'
        .attr("y", (d, i) => blocksize * (data[i].row - 1)) // relative to 'svg'
        .attr("width", blocksize)
        .attr("height", blocksize)
        .attr("fill", "#d00a")
        .style("opacity", 0.5)
        .attr("stroke", "#000")
        .attr("stroke-width", "2")

        svg
        .selectAll("g")
        .data(data)
        .enter()
        .append("text")
        .attr("id", "text")
        .attr("class", "text")
        .text(d => `${d.char}`)
        .attr("x", (d, i) => blocksize * (i % colsize))
        .attr("y", (d, i) => blocksize * (data[i].row - 1))
        .attr("text-anchor", "middle")
        .attr("dominant-baseline", "middle")
        .attr("fill", "#333")
        .attr("dx", blocksize / 2)
        .attr("dy", blocksize / 2)
        .style("font-size", blocksize / 2 );
    });
    };

    drawNumbers('number.json');
drawNumber=layout=>{
常量宽度=内部宽度;
常数高度=宽度*0.5;
常量边距={
顶部:高度*0.05,
底部:高度*0.05,
左:宽度*0.05,
右:宽度*0.05
};
json(布局)。然后(数据=>{
const colsize=data[data.length-1].col;
const rowsize=data[data.length-1]。行;
const blocksize=colsize{
}
常数阻力=d3
.drag()
.on(“开始”,拖动开始)
.打开(“拖动”,拖动)
.在(“结束”,德拉根德);
常量svg=d3
.选择(“热图”)
.append(“svg”)
.attr(“宽度”,宽度)
.attr(“高度”,高度)
.attr(“transform”,`translate(${margin.left},${margin.top})`)
.全选(“g”)
.数据(数据)
.输入()
.附加(“g”)
.呼叫(拖动)
svg
.全选(“g”)
.数据(数据)
.输入()
.append(“rect”)
.attr(“id”、“块”)
.attr(“类”、“块”)
.attr(“x”,(d,i)=>blocksize*(i%colsize))//相对于“svg”
.attr(“y”,(d,i)=>blocksize*(数据[i].row-1))//相对于“svg”
.attr(“宽度”,块大小)
.attr(“高度”,块大小)
.attr(“填充”、“d00a”)
.样式(“不透明度”,0.5)
.attr(“笔划”,“千”)
.attr(“笔划宽度”、“2”)
svg
.全选(“g”)
.数据(数据)
.输入()
.append(“文本”)
.attr(“id”、“文本”)
.attr(“类”、“文本”)
.text(d=>`${d.char}`)
.attr(“x”,(d,i)=>blocksize*(i%colsize))
.attr(“y”,(d,i)=>blocksize*(数据[i]。行-1))
.attr(“文本锚定”、“中间”)
.attr(“主要基线”、“中间”)
.attr(“填充”,“#333”)
.attr(“dx”,块大小/2)
.attr(“dy”,块大小/2)
.style(“字体大小”,块大小/2);
});
};
DrawNumber('number.json');

如果要获取“数据,为每个数据点创建一个rect,然后在rect中显示文本”,则不能正确使用enter模式

让我们详细分析一下您所拥有的:

const svg = d3
  .select("#heatmap")
  .append("svg")
  .attr("width", width)
  .attr("height", height)
  .attr("transform", `translate(${margin.left}, ${margin.top})`)
  .selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .call(drag)
在这里,您可以选择id为
heatmap
append一个svg的元素,然后为数据数组中的每个项目输入一个
g
。因此,
svg
是三个
g
元素的选择,您可以在这些
g
元素上调用拖动

接下来,选择三个
g
元素并选择子
g
元素。由于没有子
g
元素(这是一个空选择),因此输入并追加(
rect
s),为选择中的每个
g
创建三个子矩形
svg

    svg          
    .selectAll("g")
    .data(data)
    .enter()
    .append("rect")
    ....
你对文本做同样的事情。现在我们有9个矩形和9个文本,每个父元素
g
元素中各有三个(保存选择
svg
)。每个父
g
元素都有一个拖动功能,用于定位其中的第一个矩形:

    d3
        .select(this)
        .select("rect")  // select first matching element
        .attr("x", (d.x = d3.event.x))
        .attr("y", (d.y = d3.event.y));
由于每个
g
都有三个矩形,因此只会移动第一个矩形


一种解决方案是不为
svg
中的每个
g
执行输入循环:您的数据没有嵌套,我们已经为数据数组中的每个项设置了一个
g
。所以我们只需要在每个
g
中添加一个text元素和一个rect元素:

svg.append("rect").attr("x", function(d) {... 
最初绑定到
g
的数据也绑定到此子元素,无需重新绑定数据。尽管如此,我还是将
svg
重命名为其他名称,以便它更能反映其角色和内容

总的来说,这可能看起来像:

const g = d3
  .select("#heatmap")
  .append("svg")
  .attr("width", width)
  .attr("height", height)
  .attr("transform", `translate(${margin.left}, ${margin.top})`)
  .selectAll("g") 
  .data(data)
  .enter()      // create a g for each item in the data array
  .append("g")
  .call(drag)

  // add a rect to each g
  g.append("rect")
  .attr("id", "block")
  .attr("class", "block")
  .attr("x", (d, i) => blocksize * (i % colsize)) // relative to 'svg'
  .attr("y", (d, i) => blocksize * (data[i].row - 1)) // relative to 'svg'
  .attr("width", blocksize)
  .attr("height", blocksize)
  .attr("fill", "#d00a")
  .style("opacity", 0.5)
  .attr("stroke", "#000")
  .attr("stroke-width", "2")

// add text to each g
g.append("text")
  .attr("id", "text")
  .attr("class", "text")
  .text(d => `${d.char}`)
  .attr("x", (d, i) => blocksize * (i % colsize))
  .attr("y", (d, i) => blocksize * (data[i].row - 1))
  .attr("text-anchor", "middle")
  .attr("dominant-baseline", "middle")
  .attr("fill", "#333")
  .attr("dx", blocksize / 2)
  .attr("dy", blocksize / 2)
  .style("font-size", blocksize / 2 );
这是一个与上述修改