Я сделал крошечный пример ниже, демонстрирующий поведение, которое я получаю в настоящее время, и поведение, которое я хочу.
// Rotated div
rotated.style.left = "50px";
rotated.style.top = "100px";
// Original "untouched" div
original.style.left = "50px";
original.style.top = "100px";
// Where the rotated div *should* be
expected.style.left = "-10px";
expected.style.top = "160px";div {
position: absolute;
height: 80px;
width: 200px;
opacity: 0.5;
mix-blend-mode: overlay;
}
#rotated {
transform: rotateZ(90deg);
background: blue;
}
#original {
background: red;
}
#expected {
transform: rotateZ(90deg);
background: green;
}<div id = "rotated"></div>
<div id = "original"></div>
<div id = "expected"></div>Красный div — это «исходный» div, к которому я не применял никаких преобразований. Синий div повернут на 90 градусов. Красный и синий элементы div сдвинуты на одинаковые значения, но их углы явно не совпадают. Зеленый div — это ожидаемая (желаемая) позиция синего div.
Как видите, left и top на самом деле не работают так, как хотелось бы. Я понимаю, почему это не так, но я ищу некоторые решения или обходные пути. Я искал в Интернете и нашел свойство transform-origin, но у меня возникли некоторые проблемы с его использованием. Это потому, что элементы, которые я хочу переместить, создаются динамически. У них неизвестная ширина и высота, и, кроме того, ширина и высота позже изменятся!
Я знаю, что для этого статического примера я могу просто добавить transform-origin: 40px 40px; к (это просто высота / 2 дважды) div#rotated, и это сработает, но в моем проекте это означает, что мне придется устанавливать это свойство для каждого элемента и обновлять его каждый раз. время, когда я обновляю размеры элемента.
Я просто не думаю, что это так здорово, и я ищу одно из двух возможных решений:
Решение на чистом CSS, которое каким-то образом получает высоту выбранного элемента и использует его в качестве источника преобразования (или просто любое работающее решение на чистом CSS).
Использование JavaScript для вычисления исправленной позиции (в этом статическом примере я должен получить -10, 160 как позицию элемента) каждый раз, когда я хочу переместить элемент.
--- обновлять ---
Эта проблема еще более усложняется, потому что, если поворот составляет 180 градусов или 270 градусов, то transform-origin из 40px 40px больше не работает. Мне пришлось бы вычислять новое начало преобразования каждый раз, когда я хочу переместить элемент... Это то, чего я действительно хотел бы избежать...



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


Вы можете добавить перевод в ожидаемое преобразование
.wrapper {
position: relative;
margin: 200px 0 0 200px;
width: 200px;
height: 200px;
border: 1px solid black;
}
div {
position: absolute;
height: 80px;
width: 200px;
opacity: 0.5;
mix-blend-mode: overlay;
}
#rotated {
transform: rotateZ(90deg);
background: blue;
}
#original {
background: red;
}
#expected {
transform: rotateZ(90deg) translateY(-100%);
background: green;
transform-origin: 0 0;
}<div class = "wrapper">
<div id = "rotated"></div>
<div id = "original"></div>
<div id = "expected"></div>
</div>Укладываю все в обертку с каймой. Начало преобразования из ограничивающей рамки
второй фрагмент с несколькими повернутыми преобразованиями + перевод
если я до сих пор не понял, какой именно поворот вам нужен, вы можете поиграть с параметрами translate:
всегда проверяйте оболочку вокруг ограничивающей рамки: 0 0 относится к левому верхнему углу
.wrapper1 {
position: relative;
top: 100px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
.wrapper2 {
position: relative;
top: 250px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
.wrapper3 {
position: relative;
top: 400px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
.wrapper4 {
position: relative;
top: 550px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
div {
position: absolute;
height: 80px;
width: 200px;
opacity: 0.5;
mix-blend-mode: overlay;
}
.rotated1 {
transform: rotateZ(90deg);
background: blue;
}
.rotated2 {
transform: rotateZ(90deg) translateY(-100%);
transform-origin: 0 0;
background: blue;
}
.rotated3 {
transform: rotateZ(90deg) translateY(-50%);
transform-origin: 0 0;
background: blue;
}
.rotated4 {
transform: rotateZ(90deg) translate(-50%, -50%);
transform-origin: 0 0;
background: blue;
}
.original {
background: red;
}<div class = "wrapper1">
<div class = "rotated1"></div>
<div class = "original"></div>
</div>
<div class = "wrapper2">
<div class = "rotated2"></div>
<div class = "original"></div>
</div>
<div class = "wrapper3">
<div class = "rotated3"></div>
<div class = "original"></div>
</div>
<div class = "wrapper4">
<div class = "rotated4"></div>
<div class = "original"></div>
</div>На данный момент я решил это, вычислив правильный transform-origin из угла поворота (который всегда равен 0, 90, 180 или 270). Код на TypeScript:
export function computeTransformOrigin(element: HTMLElement) {
const { width, height, transform } = getComputedStyle(element);
if (transform && transform !== "none") {
const values = transform.match(/^matrix\((.+)\)$/)?.[1].split(", ");
if (values) {
element.style.translate = "";
const [a, b] = values.map(Number);
const angle = (Math.round(Math.atan2(b, a) * (180 / Math.PI)) + 360) % 360;
if (angle === 0 || angle === 90) return parseFloat(height) / 2 + "px " + parseFloat(height) / 2 + "px";
if (angle === 180) return "center";
element.style.translate = "0 " + (parseFloat(width) - parseFloat(height)) + "px";
return parseFloat(height) / 2 + "px " + parseFloat(height) / 2 + "px";
}
}
return "center";
}
Для отсутствия поворота или 90 градусов мы можем получить начало преобразования как высоту элемента, деленную на 2 (40px 40px). При повороте на 180 градусов мы используем center, а если это 270, нам нужно проделать дополнительную магию. Начало преобразования такое же, как 90 градусов, но мы также должны перевести элемент вниз на ширину минус высоту.
Затем, когда я обновляю угол для элемента, мне нужно только обновить начало преобразования в конце:
set angle(v: number) {
this.#angle = v % 360;
this.element.style.transform = `rotateZ(${v}deg)`;
if (v === 180) {
this.name.style.transform = `rotateZ(${v}deg)`;
} else {
this.name.style.transform = "";
}
this.element.style.transformOrigin = computeTransformOrigin(this.element);
}
Синий div должен быть там, где зеленый div. Прошу прощения, если неправильно сформулировал задачу.