当 d3 中的点发生变化时更新多边形

Update polygon when points change in d3

我目前正在研究 d3 中的多边形,并希望在用户拖动一个点时更新单个多边形。绘制它们最初工作正常,但我无法让更新工作。下面的 fiddle 包含我让它工作的糟糕尝试:

https://jsfiddle.net/z4g5817z/9/

相关代码:

const areas = [{
    uid: 'ajf9v0',
    points: [{
        x: 52,
        y: 92
      },
      {
        x: 50,
        y: 151
      },
      {
        x: 123,
        y: 149
      },
      {
        x: 125,
        y: 91
      }
    ],
    foo: 'bar',
    // ...
  },
  {
    uid: 'ufnf12',
    points: [{
        x: 350,
        y: 250
      },
      {
        x: 450,
        y: 250
      },
      {
        x: 450,
        y: 275
      },
      {
        x: 350,
        y: 275
      }
    ],
    foo: 'baz',
    // ...
  }
];

const svg = d3.select('#root');

svg.attr('width', 500)
  .attr('height', 500);

const areasGroup = svg.append('g')
  .attr('class', 'areas');

function drawAreas(areas) {

  console.log('Called draw');
  const self = this;

  const aGroup = areasGroup.selectAll('g.area')
    .data(areas, (d) => {
      console.log('Areas', d.points.map((d) => [d.x, d.y].join('#')).join('#'));
      return d.points.map((d) => [d.x, d.y].join('#')).join('#');
    });

  areasGroup.exit().remove();

  const areaGroups = aGroup.enter()
    .append('g')
    .attr('class', 'area');

  //const areaPolygon = area.append('g')
  //    .attr('class', 'polygon');

  //const areaPoints = area.append('g')
  //    .attr('class', 'points');

  const polygon = areaGroups.selectAll('polygon')
    .data((d) => {
      console.log('Polygon data points', [d.points]);
      return [d.points];
    }, (d) => {
      console.log('Polygon key', d.map((d) => [d.x, d.y].join('#')).join('#'));
      return d.map((d) => [d.x, d.y].join('#')).join('#');
    });

  polygon.enter()
    .append('polygon')
    .merge(polygon)
    .attr('points', (d) => {
      console.log('Polygon points', d);
      return d.map((d) => [d.x, d.y].join(',')).join(' ');
    })
    .attr('stroke', '#007bff')
    .attr('stroke-width', 1)
    .attr('fill', '#007bff')
    .attr('fill-opacity', 0.25)
    .on('click', this.handlePolygonSelection)

  polygon.exit().remove();

  const circles = areaGroups.selectAll('circle')
    .data((d) => d.points, (d) => d.x + '#' + d.y);

  circles.enter()
    .append('circle')
    .attr('r', 4)
    .attr('cx', (d) => d.x)
    .attr('cy', (d) => d.y)
    .attr('fill', '#007bff')
    .on('click', (d, idx, j) => {
      const parentArea = d3.select(j[idx].parentNode).datum().points;

      const i = parentArea.findIndex((p) => p.x === d.x && p.y === d.y);

      if (i === parentArea.length) {
        parentArea.pop();
      } else if (i === 0) {
        parentArea.shift();
      } else {
        parentArea.splice(i, 1);
      }

      this.drawAreas(areas);
    })
    .call(d3.drag()
      .on('start', function(d) {
        d3.select(this).classed('active', true)
      })
      .on('drag', function(d) {
        d3.select(this)
          .attr('cx', d.x = d3.event.x)
          .attr('cy', d.y = d3.event.y);
        self.drawAreas(areas);
      })
      .on('end', function(d) {
        d3.select(this).classed('active', false)
      }));

  circles.exit().remove();

}

this.drawAreas(areas);

感谢任何花时间看一看的人,我们将不胜感激。

看来我发现了问题:https://jsfiddle.net/z4g5817z/91/

改变

const polygon = areaGroups.selectAll('polygon')

const polygon = areasGroup.selectAll('g.area').selectAll('polygon')

似乎已经解决了。我假设这与 areaGroups 选择仅处理输入事件有关。

另一种方法是保持现在的状态并进行更改

const areaGroups = aGroup.enter()
  .append('g')
  .attr('class', 'area');

const areaGroups = aGroup.enter()
  .append('g')
  .merge(aGroup)
  .attr('class', 'area');

这将产生相同的结果,因为更新事件现在也得到了适当的处理。