Перетащите прямоугольник не работает, как ожидалось - PullRequest
0 голосов
/ 27 августа 2018

Я создал визуализацию d3, которая принимает данные json, создает прямоугольник для каждой точки данных и затем отображает текст в прямоугольнике.Тем не менее, перетаскивание работает только для 1-го прямоугольника.

Мне интересно, как действует естественное действие перетаскивания для каждого прямоугольника.

мой проект с кодовым блоком: 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');

1 Ответ

0 голосов
/ 27 августа 2018

Вы не совсем правильно используете шаблон ввода, если хотите взять «данные, создать прямоугольник для каждой точки данных и затем отобразить текст в прямоугольнике.»

Давайте разберем то, что вы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 );

Вот работающий пример с вышеуказанной модификацией.

...