Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move transform-origin back to the center of the element in CSS

When CSS elements are transformed out of their original location the transform-origin doesn't move with them; making all subsequent transforms and rotations still originate from it's original transform-origin ( example below ).

There is one way around this that I know of... and that's to keep adding all new transforms to the right of the previous ones. like this: transform: translateY ( -5rem ) rotate( 45deg ) translateY( -10rem ) rotate( 15deg )... etc. This seems to always start the new transforms from the center of the current element as desired.

the problem

When you are transforming an element based on user input using this technique you will keep adding transforms to the DOM...endlessly. Taking up a lot of memory and causing other unwanted sliding effects ( possibly ).

Here is an animation showing how the transform-origin doesn't move to the center of the element after each transform:

'use strict';

function firstRotation() { 
  var v = document.getElementById( 'v-wrp' ),
      status = document.getElementById( 'status' )
      
  setTimeout( function() { 
    status.innerHTML = 'First, the V is rotated around it\'s central axis.\
                          The <b>transform-origin</b> is centered.';
    
    v.classList.add( 'first-rotation' );
    status.classList.add( 'update' );
  }, 1000 ); 
}

function firstTranslation() { 
  var v = document.getElementById( 'v-wrp' ),
      status = document.getElementById( 'status' )
      
  setTimeout( function() { 
    status.innerHTML = 'Next, the element is translated forward in it\'s \
                        current orientation. The <b>transform-origin</b> stays\
                        behind where it was.';
                        
    v.classList.remove( 'first-rotation' );
    v.classList.add( 'first-translation' );
  }, 6000 ); 
}

function info() { 
  var v = document.getElementById( 'v-wrp' ),
      status = document.getElementById( 'status' )
      
  setTimeout( function() { 
    status.innerHTML = 'This next animation is where it\'s evident that\
                        the <b>transform-origin</b> is no longer centered, but\
                        back where it was at the beginning of these transforms';
    
  }, 11000 ); 
}

function lastRotation() { 
  var v = document.getElementById( 'v-wrp' ),
      status = document.getElementById( 'status' )
      
  setTimeout( function() { 
    status.innerHTML = 'This last rotation is far wider than desired because the\
                        transform origin is back where it started.'
  
    v.classList.remove( 'first-translation' );
    v.classList.add( 'last-rotation' );
  }, 16000 ); 
}

function end() { 
  var v = document.getElementById( 'v-wrp' ),
      status = document.getElementById( 'status' )
      
  setTimeout( function() { 
    status.classList.remove( 'update' );
  }, 21000 ); 
}

function start() {
  firstRotation();
  firstTranslation();
  info();
  lastRotation();
  end();
}

start();
/* / / / / / / / / / / / / / ANIMATION DEFINITIONS / / / / / / / / / / / / / */
.first-rotation, .first-translation, .update, .last-rotation {
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-fill-mode: forwards;
}
.first-rotation {
  animation-name: first-rotation;
}
.first-translation {
  animation-name: first-translation;
}
.update {
  animation-name: update;
  animation-iteration-count: infinite;
}
.last-rotation {
  animation-name: last-rotation;
}

/*/ / / / / / / / / / / / / / ANIMATION KEYFRAMES / / / / / / / / / / / / / /*/
@keyframes first-rotation {
  100% {
    transform: rotate( 315deg );
  }
}
@keyframes first-translation {
  0% {
    transform: rotate( 315deg );
  }
  100% {
    transform: rotate( 315deg ) translate( 0, -5rem );
  }
}
@keyframes update {
  0% {
    background-color: mediumslateblue;
  }
}
@keyframes last-rotation {
  0% {
    transform: rotate( 315deg ) translate( 0, -5rem );
  }
  100% {
    transform: rotate( 400deg ) translate( 0, -5rem );
  }
}
<head>
  <style>
    @import url( "https://fonts.googleapis.com/css?family=Nunito" );

    html {
      overflow: hidden;
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100%;
      font-family: "Nunito", sans-serif;
    }

    html,
    body {
      margin: 0;
      padding: 0;
    }

    .v {
      display: block;
      font-size: 2rem;
      transform: rotate( 180deg );
    }

    p {
      width: 100%;
      padding: 0.5rem;
      position: absolute;
      bottom: 0;
      left: 0;
    }
  </style>
</head>
<body>
  <div id="v-wrp" class="v-wrp">
    <b class="v">V</b>
  </div>
  
  <p id="status" class="status"></p>
</body>

the question

I need to find a way in CSS or JS to reset the position of the transform-origin or move it back to the center of the transformed element. Adding more transforms to the right is one technique that isn't working for me in a real time interactive environment. An attempt can be seen here: Transforms are added...endlessly.

How can I either calculate the location of a transformed element and move the transform-origin back to it's center OR how can I take multiple transform values and condense them into one value that keeps the element in it's same place?

like image 237
sol acyon Avatar asked May 01 '17 18:05

sol acyon


People also ask

How do you change origin in CSS?

The transform-origin property allows you to change the position of transformed elements. 2D transformations can change the x- and y-axis of an element. 3D transformations can also change the z-axis of an element. To better understand the transform-origin property, view a demo.

How do I change the anchor point in CSS?

transform-origin lets you re-position the anchor point from the default center of the element. You can set the origin using real units like px or rem . You can also use percentages for X and Y. Or use keywords: left and right for X-axis, top and bottom for the Y-axis, and center for either/or.

What is the default transform origin?

By default, the origin of a transform is “50% 50%”, which is exactly in the center of any given element. Changing the origin to “top left” (as in the demo above) causes the element to use the top left corner of the element as a rotation point.

How do you scale a center in CSS?

Just replace width: 400px; with transform: scale(2,2) on :hover . transform-origin: center; ? @oCcSking it is the default value Default value: 50% 50% 0 as in Specifications developer.mozilla.org/en-US/docs/Web/CSS/transform-origin Initial value 50% 50% 0 .


1 Answers

You will be able to make use of JavaScript's DOMMatrix or your own values to keep track of the translation and rotation values.

Here is a very basic example like your asteroids game: https://jsfiddle.net/omjktrsh/

The matrix provides you with functions to manipulate it without having to learn all the math. The 2D variant has six properties which are part of a 3x3 matrix:

a c e
b d f
0 0 1

where:

  • a = x axis scale
  • b = y axis shear
  • c = x axis shear
  • d = y axis scale
  • e = x axis translate
  • f = y axis translate

You can use rotateSelf(deg) to have the matrix perform a rotation on its current values, or rotate(deg) to have the matrix return a new matrix with the added rotation (leaving the original matrix with the same values).

You can use translateSelf(x, y) to move it around, relative to its current rotation. If you want to move it around not relative to its current rotation, you can manipulate the e and f values directly like so:

matrix.e += x; // x axis translation amount
matrix.f += y; // y axis translation amount

You can easily apply as many rotations as you want without needing to stack them in CSS, and then use toString() to output the matrix in its CSS form:

const matrix = new DOMMatrix();
const el = document.getElementById("transform-me");
// manipulate matrix as much as you want
...
matrix.translateSelf(...);
...
matrix.rotateSelf(...);
...
// apply matrix to elements css -> matrix(a, b, c, d, e, f)
el.style.transform = matrix.toString();

The drawback is DOMMatrix has less browser support: https://caniuse.com/dommatrix

If you would like to support browsers that do not support DOMMatrix, you can still use your own maths and the transform: matrix(a, b, c, d, e, f) syntax in these browsers: https://caniuse.com/transforms2d

If you would like to know the maths behind 2d transformation matrices, there are examples on Wikipedia. Note that some resources use a flipped matrix to the one above:

a b 0
c d 0
e f 1
like image 151
Manstie Avatar answered Sep 18 '22 08:09

Manstie