У меня есть следующая структура HTML:
<body>
<button onclick = {appendBlueToGreen()}>Append To Green</button>
<div id = "red" style = "transform: rotate(20deg); width: 100px; height: 100px; position: absolute; top: 50px; left: 50px; background-color: red;">
<div id = "green" style = "transform: rotate(20deg); width: 80px; height: 80px; position: absolute; top: 13px; left: 11px; background-color: green;">
</div>
</div>
<div id = "blue" style = "transform: rotate(45deg); width: 50px; height: 50px; position: absolute; top: 29px; left: 313px; background-color: blue;"></div>
</body>
Я хочу добавить элемент #blue к элементу #green, но элемент #blue должен сохранять свое точное положение на экране. Другими словами, элемент #blue должен быть дочерним по отношению к элементу #green, но его визуальное положение и вращение должны оставаться неизменными. Таким образом, независимо от того, прикреплен ли элемент #blue к #green, <body> или любому другому элементу, он должен оставаться в том же положении, что и на изображении ниже.
Чтобы добиться этого, мне нужно:
#green и #red.
относительно тела документа.#blue на основе
вычисленное положение и поворот элементов #red и #green, так что
появляется в том же месте на экране, но внутри #green
элемент.#blue в противоположную сторону, чтобы свести на нет эффект
повороты, примененные к элементам #green и #red.Я изо всех сил пытаюсь выяснить правильные математические расчеты, чтобы добиться этого. Я был бы очень признателен, если бы кто-нибудь предоставил решение на JavaScript для достижения этой цели.
Вот код, который я использовал с помощью ИИ для достижения этой цели, но безуспешно:коден
Мне это нужно для сценария перетаскивания, когда клон синего элемента перетаскивается в корневой элемент. После операции перетаскивания мне нужно сопоставить исходное положение/поворот элемента с клонированным элементом. Для простоты я не включил в этот вопрос весь процесс.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Поскольку речь идет о вращении, формула довольно проста:
newBlueRotation = originalBlueRotation - redRotation - greenRotation;
Однако для новых позиций элемента это неожиданно сложно. Это связано с тем, что каждое вращение выполняется вокруг центра, отличного от опорной точки, используемой для позиционирования. Центр по умолчанию это центр div, но его можно изменить с помощью Transform-Origin CSS-свойство.
Таким образом, для нахождения фактического центра вращения element можно использовать:
window.getComputedStyle(element).transformOrigin
Разница между центром вращения и опорной точкой означает, что для вычисления положений элементов, включенных в строку, для каждого включенного элемента необходимо выполнить перемещение из опорной точки в центр, применить вращение, а затем перевести обратно в ориентир.
Например, контрольная точка только одного повернутого прямоугольника, красного. находится с помощью этой операции (надеюсь, эта запись псевдокода не требует пояснений):
redReferencePoint = redOffset + redCenter - redCenter * rotate(-red)
Смещения — это позиции элементов относительно их родителей (все предполагаемые абсолютно готовы к работе расчета).
Примените эти вычисления последовательно для красных->зеленых->новых синих элементов, установив условие, что положение нового синего элемента совпадает с положением исходного синего элемента, можно получить уравнение, которое можно решить для нового смещения синего цвета, которое огромно:
newBlueOffset = greenCenter - blueCenter +
( blueOffset + blueCenter - redOffset - redCenter -
(greenOffset - redCenter + greenCenter) * rotate(-red)
) * rotate(red+green)
Сложные вычисления также приводят к тому, что результат оказывается немного неточно, вероятно, из-за ошибок округления - иногда бывают минутное «субпиксельное» изменение положения синего элемента,
Возможно, формулы лучше видно в коде; часть кода в этом фрагменте взята из кода, указанного в вопросе.
const appendBlueToGreen = () => {
// Get the elements
const blueElement = document.getElementById('blue');
const greenElement = document.getElementById('green');
const redElement = document.getElementById('red');
// Function to convert degree to radian
const radToDeg = (deg) => (deg * 180) / Math.PI;
// get the rotation center
const getCenter = element => {
try{
const [x, y] = window.getComputedStyle(element).transformOrigin.split(' ').map(parseFloat);
return {x, y};
}
catch(err){
// default is element center
return {x: element.offsetWidth / 2, y: element.offsetHeight / 2};
}
}
// Function to get the rotation angle of an element (in radians)
const getRotation = (element) => {
const transform = window.getComputedStyle(element).transform;
const matrix = transform.match(/^matrix\((.+)\)$/);
if (matrix) {
const values = matrix[1].split(', ');
const a = values[0];
const b = values[1];
return Math.atan2(b, a);
} else {
return 0;
}
};
const getOffset = (element) => {
//return {x: parseFloat(window.getComputedStyle(element).left),
// y: parseFloat(window.getComputedStyle(element).top)};
return {x: element.offsetLeft, y: element.offsetTop};
}
const rotateVec = ({x, y}, theta) => { // rotate vector {x, y} by theta clockwise
const cosTheta = Math.cos(theta),
sinTheta = Math.sin(theta);
return {x: x * cosTheta + y * sinTheta, y: -x * sinTheta + y * cosTheta};
};
const addVec = ({x: x1, y: y1}, {x: x2, y: y2}, ...rest) => {
const sum12 = {x: x1 + x2, y: y1 + y2};
return rest.length === 0 ? sum12 : addVec(sum12, ...rest);
}
const negVec = ({x, y}) => ({x: -x, y: -y});
const roundToFixed = (x, digits) => x.toFixed(digits).replace(/\.?0+$/, '');
// Calculate the rotations
const redRotation = getRotation(redElement);
const greenRotation = getRotation(greenElement);
const blueRotation = getRotation(blueElement);
// calculate the offset positions of elements wrt their parents
const redOffset = getOffset(redElement);
const greenOffset = getOffset(greenElement);
const blueOffset = getOffset(blueElement);
// calculate the centers
const redCenter = getCenter(redElement);
const greenCenter = getCenter(greenElement);
const blueCenter = getCenter(blueElement);
let newOffset = addVec(negVec(greenOffset), negVec(greenCenter), redCenter);
newOffset = rotateVec(newOffset, -redRotation);
newOffset = addVec(newOffset, blueOffset, blueCenter, negVec(redOffset), negVec(redCenter));
newOffset = rotateVec(newOffset, greenRotation + redRotation);
newOffset = addVec(newOffset, greenCenter, negVec(blueCenter));
// Set the new position and rotation for the blue element
blueElement.style.position = 'absolute';
blueElement.style.left = `${roundToFixed(newOffset.x, 2)}px`;
blueElement.style.top = `${roundToFixed(newOffset.y, 2)}px`;
const newBlueRotation = roundToFixed(radToDeg(blueRotation - redRotation - greenRotation), 2);
blueElement.style.transform = `rotate(${newBlueRotation}deg)`;
// Append the blue element to the green element
greenElement.appendChild(blueElement);
console.info(blueElement.style.left, blueElement.style.top, blueElement.style.transform)
this.event.target.setAttribute("disabled","d");
};
window.appendBlueToGreen = appendBlueToGreen;<button onclick = {appendBlueToGreen()}>Append To Green</button>
<div id = "red" style = "transform: rotate(20deg);width: 100px;height: 100px;position: absolute;top: 50px;left: 50px;background-color: red;">
<div id = "green" style = "transform: rotate(20deg);width: 80px;height: 80px;position: absolute;top: 13px;left: 11px;background-color: green;">
</div>
</div>
<div id = "blue" style = "transform: rotate(45deg);width: 50px;height: 50px;position: absolute;top: 29px;left: 313px;background-color: blue;"></div>Спасибо, что нашли время ответить на мой вопрос. Ваше объяснение было ясным и очень полезным. Я ценю вашу помощь!
Если синий элемент не подчиняется ни одному из условий родительского зеленого элемента... почему он должен находиться внутри него?