拖动矩形未按预期工作
Drag rect not working as expected
我创建了一个 d3 可视化,它采用 json 数据,为每个数据点创建一个矩形,然后在矩形中显示文本。然而,拖
仅适用于第一个矩形。
我想知道如何为每个矩形执行自然拖动动作。
我的代码笔项目:https://codepen.io/moriakijp/project/editor/ZRnVwr
代码如下:
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');
如果您想参加 "data, create a rect for each data point, and then displays the text in the rect."
,您没有正确使用输入模式
让我们分解一下你所拥有的:
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)
这里你 select id 为 heatmap
的元素附加一个 svg,然后为数据数组中的每个项目输入 g
。因此,svg
是三个 g
元素的 select 离子,您调用这些 g
元素上的拖动。
接下来,您采用三个 g
元素和 select 个子 g
元素的 selection。由于没有子 g
元素(这是一个空的 selection),输入并追加 (rect
s),为 g
中的每个 g
创建三个子矩形=61=]离子svg
:
svg
.selectAll("g")
.data(data)
.enter()
.append("rect")
....
你对文本做同样的事情。现在我们有 9 个矩形和 9 个文本,每个父 g
元素(包含 selection 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
:
添加一个文本元素和一个矩形元素
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 );
这里是running example 上面的修改。
我创建了一个 d3 可视化,它采用 json 数据,为每个数据点创建一个矩形,然后在矩形中显示文本。然而,拖 仅适用于第一个矩形。
我想知道如何为每个矩形执行自然拖动动作。
我的代码笔项目:https://codepen.io/moriakijp/project/editor/ZRnVwr
代码如下:
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');
如果您想参加 "data, create a rect for each data point, and then displays the text in the rect."
,您没有正确使用输入模式让我们分解一下你所拥有的:
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)
这里你 select id 为 heatmap
的元素附加一个 svg,然后为数据数组中的每个项目输入 g
。因此,svg
是三个 g
元素的 select 离子,您调用这些 g
元素上的拖动。
接下来,您采用三个 g
元素和 select 个子 g
元素的 selection。由于没有子 g
元素(这是一个空的 selection),输入并追加 (rect
s),为 g
中的每个 g
创建三个子矩形=61=]离子svg
:
svg
.selectAll("g")
.data(data)
.enter()
.append("rect")
....
你对文本做同样的事情。现在我们有 9 个矩形和 9 个文本,每个父 g
元素(包含 selection 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
:
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 );
这里是running example 上面的修改。