CSS rotation causing issues with absolute positioning

In the example below, I have demonstrated the current behavior I am experiencing and the desired outcome.

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

The red div represents the "original" div without any transformations applied. The blue div is rotated by 90 degrees but their corners do not align as expected. The green div shows the correct position where the blue div should be.

It is evident that the left and top properties are not working as intended. While I am aware of the issue, I am seeking solutions or alternatives. I have come across the transform-origin property but faced challenges in its implementation due to dynamically created elements with unknown dimensions that may change over time.

For this specific example, adding transform-origin: 40px 40px; to the div#rotated element resolves the issue. However, replicating this for multiple elements and adapting it to changing dimensions is not practical in my project.

I am exploring two potential solutions:

  • A CSS-based approach that dynamically calculates the element's height to determine the transform-origin (or any CSS solution that works effectively)

  • Using JavaScript to compute the accurate position (e.g., -10, 160) whenever an element needs to be moved

--- update ---

This challenge is further complicated when dealing with rotations of 180deg or 270deg, rendering the default transform-origin of 40px 40px ineffective. Recalculating the transform-origin for each element rotation is impractical and something I aim to avoid.

Answer №1

To achieve the desired effect, consider adding a translate function to your transform property.

.container {
  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="container">
  <div id="rotated"></div>
  <div id="original"></div>
  <div id="expected"></div>
</div>

All elements are enclosed within a wrapper with a visible border. The transform origin is set as per the bounding box.

Answer №2

A different approach to transforming and rotating elements

If the rotation isn't quite what you're looking for, try adjusting the translate parameters:

  • translateY
  • translateX
  • translate with 2 values

Make sure to always consider the bounding box of the wrapper: (0, 0) is at the top left corner

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

Answer №3

My solution involves calculating the appropriate transform-origin based on the rotation angle (always 0, 90, 180, or 270). Here's the TypeScript code snippet:

export function calculateTransformOrigin(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";
}

For rotations of 0 or 90 degrees, the transform origin is half the height of the element (40px 40px). If it's rotated by 180 degrees, we set it to center. For 270 degrees, additional adjustments are needed including translating the element downwards by the difference between its width and height.

When updating the angle of an element, simply adjusting the transform origin at the end suffices:

     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 = calculateTransformOrigin(this.element);
    }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

HTML and Python synchronize perfectly when handling date formatting

Here is a code snippet I am working with: {% for x in fixtures %} <TR style="display:block" onclick="team_visibility('match{{ forloop.counter }}');"> <TD> {{ x.fixturedate }}</TD> ...

I am attempting to implement an Express static middleware as demonstrated in this book, but I am having trouble understanding the intended purpose of the example

I'm currently studying a chapter in this book that talks about Express, specifically concerning the use of express.static to serve files. However, I'm encountering an issue where the code catches an error when no file is found. I've created ...

Alert from Google Chrome about Service Worker

My situation involves using FCM for sending web notifications. However, I am encountering a warning and the notifications are not functioning as expected when clicked (i.e., opening the notification URL). Below is my Service-Worker code: importScripts(& ...

How can I prevent a browser from allowing users to select an image tag?

I'm dealing with an issue on my webpage where I have an image that is inadvertently picking up mouse clicks, causing the browser to perform drag and drop actions or highlight the image when clicked. I really need to use the mousedown event for somethi ...

simulating the use of `usePrompt` in react-router-dom version 6

Prompt.js import { unstable_usePrompt as usePrompt } from 'react-router-dom'; // eslint-disable-next-line react/prop-types export default function CustomPrompt({ when, message }) { usePrompt({ when, message }); return null; } CustomPrompt.t ...

How to check off a checkbox using a jQuery function

I have a listbox displayed in my view. This listbox is using a template Listbox <div id="UsersLoad" style="width: 50%"> @Html.EditorFor(i => i.Users, "UsersForEdit") </div> UserForEdit Template (Code snippet) @model string[] @{ ...

What is the best method to generate a distinct identifier for individual input fields using either JavaScript or jQuery?

I have attempted to copy the table n number of times using a for loop. Unfortunately, the for loop seems to only work on the first iteration. I am aware that this is due to not having unique IDs assigned to each table. As a beginner, I am unsure how to cre ...

The response from the XHR object is coming back as "Object Object

Recently, I've been faced with the challenge of working with an API that provides articles. Within the array returned by the API, there are attributes like author, title, and description. However, despite my efforts, each time I attempt to retrieve th ...

retrieve the variable contained within the callback function

const axios = require('axios'); const options = { url: 'https://api.github.com/repos/axios/axios', headers: { 'User-Agent': 'axios' } }; function handleResponse(error, response, body) { if (!error && re ...

Using Lodash to Substitute a Value in an Array of Objects

Looking to update the values in an array of objects, specifically the created_at field with months like 'jan', 'Feb', etc.? One way is to loop through using map as demonstrated below. However, I'm curious if there's a more co ...

Output JSON data using Javascript

Here is some JSON data I am working with: { "lang": [ { "SECTION_NAME": { "english": "My title" }, "SECTION_NAME_2": { "english": "My title" } } ] } I ...

The art of layering images: How to stack one image on top of another

I am in the process of trying to place a logo on top of an image, but I have been unsuccessful so far. Rather than overlapping, the second image is appearing next to the first one. Can you help me figure out how to solve this issue? This is for a website ...

Arrange components within the form

Just started experimenting with this a few days ago. In my form, there is a text field and a button that I want to align side by side at the center of the form. .row { width: 100%; margin-bottom: 20px; display: flex; flex-wrap: wrap; } .col-6 { ...

Modify the JSON file stored on the local server

I am currently working on a project that is being hosted on a local server. My main objective is to be able to edit and save a JSON file that will contain the configurations for this project. I have succeeded in reading the file and accessing it using axio ...

Transforming HTML content in real-time using Express.js

I'm a little uncertain if I understand the concept of Express MVC correctly: If my goal is to create a single page application and dynamically modify the HTML, can Express assist me with this? Or will I only be able to work with static pages that req ...

The ace.edit function is unable to locate the #javascript-editor div within the mat-tab

Having trouble integrating an ace editor with Angular material Error: ace.edit cannot locate the div #javascript-editor You can view my code on StackBlitz (check console for errors) app.component.html <mat-tab-group> <mat-tab label="Edito ...

Managing a large number of records in a for loop on a Node.js server can be challenging, especially when dealing with nearly

After setting up a NodeJS server and connecting it to a MySQL database with around 5000 users, I needed to read the data from MySQL and update a MongoDB database. I managed to write code for this process. https://gist.github.com/chanakaDe/aa9d6a511070c3c78 ...

How to iterate over the request body in Node.js using Express?

When I send a request with data in the form of an array of objects: [ {id: "1"}, {id: "2"}, {id: "3"} ] I am utilizing JSON.stringify() and my req.body ends up looking like this: { '{"id":"1"} ...

Tips for refreshing a D3.js bubble chart with live JSON data updates

Currently delving into d3 and experimenting with transforming a static bubble chart into a dynamic one that adjusts by removing or adding bubbles based on JSON changes. I am aiming to have the JSON file refreshed every 5 seconds to update the bubble chart ...

Effortlessly refresh a data object in Vue.js without relying on a function

I need assistance with the following: <Checkbox label="View" :initialState="data.something" @updateStatus="updateCheckbox" > </Checkbox> The variable data.something is a b ...