Как добавить элемент к другому повернутому и позиционированному элементу, сохраняя его положение на экране?

У меня есть следующая структура 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 для достижения этой цели.

Вот код, который я использовал с помощью ИИ для достижения этой цели, но безуспешно:коден

Если синий элемент не подчиняется ни одному из условий родительского зеленого элемента... почему он должен находиться внутри него?

Spencer May 14.04.2024 00:18

Мне это нужно для сценария перетаскивания, когда клон синего элемента перетаскивается в корневой элемент. После операции перетаскивания мне нужно сопоставить исходное положение/поворот элемента с клонированным элементом. Для простоты я не включил в этот вопрос весь процесс.

cyrus-d 14.04.2024 09:00
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку речь идет о вращении, формула довольно проста:

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>

Спасибо, что нашли время ответить на мой вопрос. Ваше объяснение было ясным и очень полезным. Я ценю вашу помощь!

cyrus-d 18.04.2024 10:13

Другие вопросы по теме