#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>