Эффект принудительного столкновения D3 смещается при перетаскивании

#d3.js

#d3.js

Вопрос:

Пожалуйста, посмотрите видеоролик о поведении здесь: https://imgur.com/a/Hs4iuC5

Каждый блок перемещается в свое местоположение с помощью силы X и Y, и сила столкновения используется, чтобы раздвинуть их при перетаскивании.

Странное поведение заключается в том, что столкновение смещается от начальной позиции перетаскивания. Чем дальше вы перемещаетесь от начальной позиции перетаскивания, тем больше смещение.

 var size = 40;
var items = [{
    cx: 50,
    cy: 200,
    size: size,
    collideR: size * 0.5
  },
  {
    cx: 100,
    cy: 200,
    size: size,
    collideR: size * 0.5
  },
  {
    cx: 600,
    cy: 200,
    size: size,
    collideR: size * 0.5
  },
  {
    cx: 750,
    cy: 200,
    size: size,
    collideR: size * 0.5
  }
];

var alphaTarget = 0.03;
var sim;

var nodes =
  d3.select(".grid-svg")
  .selectAll("rect")
  .data(items)
  .enter()
  .append("rect")
  .attr("class", "grid-item-block")
  .attr('x', (d) => d.cx)
  .attr('y', (d) => d.cy)
  .attr("width", (d) => d.size)
  .attr("height", (d) => d.size)
  .attr("transform", (d) => `translate(-${d.size  / 2}, -${d.size / 2})`)
  .call(
    d3
    .drag()
    .on("start", (event, d) => {
      if (!event.active) sim.alphaTarget(alphaTarget).restart();

      d.fx = d.x;
      d.fy = d.y;

      sim.force(
        "collide",
        d3.forceCollide().radius((dc) => dc.collideR)
      );
    })
    .on("drag", (event, d) => {
      d.fx = event.x;
      d.fy = event.y;
    })
    .on("end", (event, d) => {
      if (!event.active) sim.alphaTarget(alphaTarget);

      d.fx = null;
      d.fy = null;

      sim.force("collide", null);
    })
  );

sim = d3
  .forceSimulation()
  .alphaDecay(0.2)
  .alphaMin(0.005)
  .force(
    "x",
    d3
    .forceX()
    .strength(3.0)
    .x((d) => d.cx)
  )
  .force(
    "y",
    d3
    .forceY()
    .strength(3.0)
    .y((d) => d.cy)
  );

sim.nodes(items).on("tick", () => {
  nodes.attr("x", (d) => d.x).attr("y", (d) => d.y);
});  
 .grid-item-block {
  fill: #009900;
}  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg class="grid-svg" width="800" height="300"></svg>  

В примере, если вы перетащите левый блок рядом с другими 3 блоками, вы заметите, что эффект на блоке # 2 примерно симметричен, блок # 3 не запускается, пока вы не начнете перекрывать блок, и он сильнее справа от блока # 3, и еще большая разница в смещении сблок # 4

Комментарии:

1. Кроме того, может ли быть так, что задержка возникает после более длительного перетаскивания вместо дальнейшего перетаскивания? Поскольку моделирование близко к моделируемому отжигу, может случиться так, что приложенные силы со временем уменьшаются

Ответ №1:

Я нашел основную причину: чем дальше вы удаляетесь от точки d.cx, d.cy , тем больше он тянет блок, чтобы вернуться. Огромная нагрузка на блок заставляет d3 думать, что нет причин перемещать блок, с которым он сталкивается. В конце концов, после одного тика блок, который вы перетаскиваете, больше не будет перекрываться, потому что он сильно оттянут назад.

Я исправил это, повторно инициализировав центрирующие силы и придав им какую-либо силу, только если это не был текущий перетаскиваемый блок. В противном случае сила равна 0, и сила фактически не применяется.

 var size = 20;
var width = 400;
var height = 200;
var items = [{
    cx: 2 * size,
    cy: height / 2,
    size: size,
    collideR: size
  },
  {
    cx: width - 2 * size,
    cy: height / 2,
    size: size,
    collideR: size
  }
];

var alphaTarget = 0.03;
var sim = d3
  .forceSimulation()
  .alphaDecay(0.2)
  .alphaMin(0.005);

var nodes =
  d3.select(".grid-svg")
  .selectAll("rect")
  .data(items)
  .enter()
  .append("rect")
  .attr("class", "grid-item-block")
  .attr('x', (d) => d.cx)
  .attr('y', (d) => d.cy)
  .attr("width", (d) => d.size)
  .attr("height", (d) => d.size)
  //.attr("transform", (d) => `translate(-${d.size  / 2}, -${d.size / 2})`)
  .call(
    d3
    .drag()
    .on("start", function(event, d) {
      if (!event.active) sim.alphaTarget(alphaTarget).restart();
      d.isDragging = true;

      d.fx = d.x;
      d.fy = d.y;

      sim.force(
        "collide",
        d3.forceCollide().radius((dc) => dc.collideR)
      );
      setForces();
    })
    .on("drag", (event, d) => {
      d.fx = event.x;
      d.fy = event.y;
    })
    .on("end", (event, d) => {
      if (!event.active) sim.alphaTarget(alphaTarget);
      d.isDragging = false;

      d.fx = null;
      d.fy = null;

      sim.force("collide", null);
      setForces();
    })
  );

function setForces() {
  sim
    .force(
      "x",
      d3
      .forceX()
      .strength((d) => d.isDragging ? 0 : 3.0)
      .x((d) => d.cx)
    )
    .force(
      "y",
      d3
      .forceY()
      .strength((d) => d.isDragging ? 0 : 3.0)
      .y((d) => d.cy)
    );
}
setForces();

sim.nodes(items).on("tick", () => {
  nodes.attr("x", (d) => d.x).attr("y", (d) => d.y);
});  
 .grid-item-block {
  fill: #009900;
}  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.js"></script>
<svg class="grid-svg" width="400" height="200"></svg>