Вы не совсем правильно используете шаблон ввода, если хотите взять «данные, создать прямоугольник для каждой точки данных и затем отобразить текст в прямоугольнике.»
Давайте разберем то, что выhave:
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)
Здесь вы выбираете элемент с идентификатором heatmap
, добавляете svg, а затем вводите g
для каждого элемента в вашем массиве данных.Таким образом, svg
- это выборка из трех g
элементов, и вы вызываете перетаскивание этих g
элементов.
Далее вы берете этот выбор из трех g
элементов и выбираете дочерний * 1014.* элементы.Поскольку нет дочерних элементов 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
имеет три прямоугольника, будет перемещен только первый.
Одним из решений было бы не делать цикл ввода для каждого g
в svg
: ваши данные не являются вложенными, у нас уже есть 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 );
Вот работающий пример с вышеуказанной модификацией.