Ваш код сталкивается с трудностями, потому что вы позиционируете элементы g
и дочерние элементы text
и circle
s.
Круги и текст изначально расположены по атрибутам x / y:
draggables.append('circle')
.attr("cx", (d, i) => circleRadius * (2 * i + 1))
.attr("cy", circleRadius)
draggables.append("text")
.attr("x", (d, i) => circleRadius * (2 * i + 1))
.attr("y", circleRadius)
Перетащите события, чтобы переместить круги и текст сюда:
d3.select(this).select("text").attr("x", d3.event.x).attr("y", d3.event.y);
d3.select(this).select("circle").attr("cx", d3.event.x).attr("cy", d3.event.y);
А затем мы сбрасываем круги и текст, пытаясь сместить родителя g
с помощью преобразования:
d3.select(`#${resetVar}`).attr('transform', `translate(${-current_x}, ${-current_y})`)
Где current_x
и current_y
- текущие значения x, y для кружков и текста. Мы также сохранили начальные значения x, y для текста, но в целом это становится более сложным, чем необходимо, поскольку у нас есть два конкурирующих набора координат позиционирования.
Это можно упростить изрядной суммой. Вместо того, чтобы располагать текст и круги, просто примените преобразование к родительскому элементу g
, содержащему и круг, и текст. Затем, когда мы перетаскиваем, мы обновляем преобразование, а когда мы заканчиваем, мы сбрасываем преобразование.
Теперь у нас нет модификаций атрибутов x, y / cx, cy и преобразований для позиционирования элементов относительно друг друга. Без смещений и родительское преобразование g
всегда будет отображать положение круга и текста.
Ниже я отслеживаю исходное преобразование с датумом (не атрибутом элемента) - обычно я бы использовал свойство элемента данных, но у вас есть необъектные данные, поэтому я просто заменяю элемент на оригинальное преобразование :
const circleRadius = 40;
const variables = ['one', 'two', 'three', 'four'];
const inZone = [];
// DOM elements
const svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500)
const dragDockGroup = svg.append('g')
.attr('id', 'draggables-dock');
// Immovable placemarkers:
const dock = dragDockGroup.selectAll('g')
.data(variables)
.enter().append("g")
.attr("id", (d, i) => `dock-${variables[i]}`);
dock.append("circle")
.attr("cx", (d, i) => circleRadius * (2 * i + 1))
.attr("cy", circleRadius)
.attr("r", circleRadius)
.style("stroke", "none")
.style("fill", "palegoldenrod");
dock.append("text")
.attr("x", (d, i) => circleRadius * (2 * i + 1))
.attr("y", circleRadius)
.attr("text-anchor", "middle")
.style("fill", "white")
.text((d, i) => variables[i]);
// Dragables
const draggablesGroup = svg.append('g')
.attr('id', 'draggables');
const draggables = draggablesGroup.selectAll('g')
.data(variables)
.enter()
.append("g")
.datum(function(d,i) {
return "translate("+[circleRadius * (2 * i + 1),circleRadius]+")";
})
.attr("transform", (d,i) => d)
.attr("id", (d, i) => variables[i])
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded));
draggables.append('circle')
.attr("r", circleRadius)
.style("stroke", "orange")
.style("fill", "yellowgreen");
draggables.append("text")
.attr("text-anchor", "middle")
.style("fill", "white")
.text((d, i) => variables[i]);
svg.append('rect')
.attr("x", 960/2)
.attr("y", 0)
.attr("width", 100)
.attr("height", 500/2)
.attr("fill-opacity", 0)
.style("stroke", "#848276")
.attr("id", "hitZone");
// functions
function dragStarted() {
d3.select(this).raise();
}
function dragged() {
d3.select(this).attr("transform","translate("+[d3.event.x,d3.event.y]+")")
}
function dragEnded() {
d3.select(this).lower();
let hit = d3.select(document.elementFromPoint(d3.event.sourceEvent.clientX, d3.event.sourceEvent.clientY)).attr("id");
if (hit == "hitZone") {
inZone.push(this.id);
if (inZone.length > 1) {
let resetVar = inZone.shift();
resetCircle(resetVar);
}
}
d3.select(this).raise();
}
function resetCircle(resetVar) {
d3.select(`#${resetVar}`)
.transition()
.duration(500)
.style('opacity', 0)
.transition()
.duration(500)
.attr("transform", (d,i) => d)
.transition()
.duration(500)
.style('opacity', 1);
}
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
<script src="https://d3js.org/d3.v5.min.js"></script>