Element moves after transforming

css transform animation
css scale animation
css transition move left to right
css transform-origin
css transform: translate
css transition bottom to top
css transition slide right to left
css transition position

I would like to have a span within the .btn rotate 180 deg when I click the .btn. However, the span also moved to the right and went down after it was rotated. Could anyone help me explain why it moved like that. I tried to transform: translate(-21px, 1px) after rotation then the span will moved the right place but I believe there will be another better way to fix it. I also tried transform-origin: 50% 50% but it doesn't work either.

$('.btn').on('click', function(){
  $(this).toggleClass('open');
})
@import "https://fonts.googleapis.com/css?family=Raleway";
body {
  background-color: #ddd;
  font-family: "Raleway", "Microsoft JhengHei", Arial, sans-serif;
  color: #7a7b7c;
}

.btn {
  width: 80px;
  height: 80px;
  background-color: #6F7272;
  border-radius: 50%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 2rem #babbbc;
  position: absolute;
}
.btn span {
  width: 40px;
  height: 2px;
  background-color: #ffffff;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  position: absolute;
}
.btn::before, .btn::after {
  content: "";
  width: 40px;
  height: 2px;
  background-color: #ffffff;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  position: absolute;
}
.btn::before {
  margin-top: -7px;
}
.btn::after {
  margin-top: 7px;
}

.open {
  transition: 0.3s ease-in-out;
  background-color: #6F7272;
}
.open span {
  transition: .3s ease-in-out;
  transform: rotate(180deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="btn">
  <span></span>
</div>

Your approach isn't working as expected, because after click event, you are transforming the X & Y values of your span using translate property and then rotating it. This causes it to shift it's location when transformed.

You used position: absolute to place your span in middle. this can be also be achieved via other CSS properties like display: table or display :flex

Check out the below suggestion - most of the code is all yours with just slight change.

$('.btn').on('click', function() {
  $(this).toggleClass('open');
})
@import "https://fonts.googleapis.com/css?family=Raleway";
body {
  background-color: #ddd;
  font-family: "Raleway", "Microsoft JhengHei", Arial, sans-serif;
  color: #7a7b7c;
}

.btn {
  width: 80px;
  height: 80px;
  background-color: #6F7272;
  border-radius: 50%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 2rem #babbbc;
  position: absolute;
  display: flex;
  justify-content: center;
  align-items: center;
}

.btn span {
  width: 40px;
  height: 2px;
  background-color: #ffffff;
  /*  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  position: absolute; */
}

.btn::before,
.btn::after {
  content: "";
  width: 40px;
  height: 2px;
  background-color: #ffffff;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  position: absolute;
}

.btn::before {
  margin-top: -7px;
}

.btn::after {
  margin-top: 7px;
}

.open {
  transition: 0.3s ease-in-out;
  background-color: #6F7272;
}

.open span {
  transition: .3s ease-in-out;
  transform: rotate(180deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="btn">
  <span></span>
</div>

A fix for when your elements move up and/or blur during animations , yourDivClass { -webkit-backface-visibility: hidden; -webkit-transform: translateZ(0​) scale(1.0, 1.0); transform: translateZ(0); }. And when text is  Element is smaller but it doesn't change its actual pixel size in DOM. Because of that I don't think that CSS-only solution exist. I put scalable element into container which keeps the same pixel ratio as rest of elements. Using Java Script I simply change container's size. Everything is still based on CSS3 transform: scale.

If you remove transform: translate(-50%, -50%); and just keep left:26% and top: 49% It works fine.

To your question why it is happening. Because you have moved it using left and top position and again you are trying to neutralise it by translate. which is moving the center of animation to different position.

$('.btn').on('click', function(){
  $(this).toggleClass('open');
})
@import "https://fonts.googleapis.com/css?family=Raleway";
body {
  background-color: #ddd;
  font-family: "Raleway", "Microsoft JhengHei", Arial, sans-serif;
  color: #7a7b7c;
}

.btn {
  width: 80px;
  height: 80px;
  background-color: #6F7272;
  border-radius: 50%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 2rem #babbbc;
  position: absolute;
}
.btn span {
  width: 40px;
  height: 2px;
  background-color: #ffffff;
  top: 49%;
  left: 26%;
//  transform: translate(-50%, -50%);
  position: absolute;
}
.btn::before, .btn::after {
  content: "";
  width: 40px;
  height: 2px;
  background-color: #ffffff;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  position: absolute;
}
.btn::before {
  margin-top: -7px;
}
.btn::after {
  margin-top: 7px;
}

.open {
  transition: 0.3s ease-in-out;
  background-color: #6F7272;
}
.open span {
  transition: .3s ease-in-out;
  transform: rotate(180deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="btn">
  <span></span>
</div>

transform, translate() : Moves an element sideways or up and down. rotate() : Rotates the element clockwise from its current position. matrix() : A function that  The transform is taking place on the z axis, not the x or y axes. When working with three-dimensional transforms, being able to move an element on the z axis does have great benefits, like when building the cube below for example. HTML

Update this style

.open span {
    transition: .3s ease-in-out;
    transform: rotate(180deg) translate(20px, 1px );
}

$('.btn').on('click', function(){
  $(this).toggleClass('open');
})
@import "https://fonts.googleapis.com/css?family=Raleway";
body {
  background-color: #ddd;
  font-family: "Raleway", "Microsoft JhengHei", Arial, sans-serif;
  color: #7a7b7c;
}

.btn {
  width: 80px;
  height: 80px;
  background-color: #6F7272;
  border-radius: 50%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 2rem #babbbc;
  position: absolute;
}
.btn span {
  width: 40px;
  height: 2px;
  background-color: #ffffff;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  position: absolute;
}
.btn::before, .btn::after {
  content: "";
  width: 40px;
  height: 2px;
  background-color: #ffffff;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  position: absolute;
}
.btn::before {
  margin-top: -7px;
}
.btn::after {
  margin-top: 7px;
}

.open {
  transition: 0.3s ease-in-out;
  background-color: #6F7272;
}
.open span {
  transition: .3s ease-in-out;
  transform: rotate(180deg) translate(20px, 1px );
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="btn">
  <span></span>
</div>

CSS Transitions and Transforms for Beginners, When used together, these properties allow you to create simple animations At their most basic level, transforms move or change the appearance of an Without a transition, an element being transformed would change  Leppa Berries used from the Bag or as a held item can only restore PP to moves the Pokémon knows before transforming. A held Leppa Berry will only be consumed if a move known before transforming reaches 0 PP. The maximum PP of a move known due to Transform is the move's base maximum PP, regardless of any PP Ups used by the target.

Check if this is what you are trying to achieve.

Here, I have added a single line to the css transform-origin: 10px;. Everything else is the same. The change in JS is to remove the add class as soon as the animation carried out. Otherwise, you will need to click double times on the button to see the animation again.

$('.btn').on('click', function(){
  $(this).addClass('open');
  setTimeout(function() {
    $('.btn').removeClass('open');
  }, 300);
})
body {
  background-color: #ddd;
  font-family: "Raleway", "Microsoft JhengHei", Arial, sans-serif;
  color: #7a7b7c;
}

.btn {
  width: 80px;
  height: 80px;
  background-color: #6F7272;
  border-radius: 50%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 2rem #babbbc;
  position: absolute;
}
.btn span {
  width: 40px;
  height: 2px;
  background-color: #ffffff;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  position: absolute;
}
.btn::before, .btn::after {
  content: "";
  width: 40px;
  height: 2px;
  background-color: #ffffff;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  position: absolute;
}
.btn::before {
  margin-top: -7px;
}
.btn::after {
  margin-top: 7px;
}

.open {
  transition: 0.3s ease-in-out;
  background-color: #6F7272;
}
.open span {
  transition: .3s ease-in-out;
  transform: rotate(180deg);
  transform-origin: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="btn">
  <span></span>
</div>

CSS Pseudo-Elements and Transforms: My Favorite CSS Tools , If you've ever used :before or :after in your selector and it had the Move element 50px left */ transform: translate(50px); /* Move element 2rem  These nine elements form a set of building blocks for digital transformation. Currently, no company in our sample has fully transformed all nine elements. Rather, executives are selecting among these building blocks to move forward in the manner that they believe is right for their organizations.

Why moving elements with translate() is better than pos:abs top/left , TL;DR: Only transform & opacity ; never top / left ! In modern days we have two primary options for moving an element across the screen: using  It’s important to note that an element using transform will not cause other elements to flow around it. By using the translate function below and nudging the green square out of its original position, we’ll notice how the surrounding text will remain fixed in place, as if the square is a block element:

CSS 2D Transforms, CSS transforms allow you to move, rotate, scale, and skew elements. With the CSS transform property you can use the following 2D transformation methods:. A 2D rotation moves an element and any descendants it may have around a fixed point (a point whose position is preserved following the transform). The final result depends on the position of this fixed point. Starting from the same element, two rotations of identical angles around two different points will produce different results.

Animation Using CSS Transforms < CSS, Internet Explorer 11 fails trying to animate when the Just be sure not to move the hovered element  To scale the width and height proportionally, either press Shift as you drag a corner handle, or select Constrain Proportions in the options bar, and then drag a corner handle. To rotate, move the pointer outside of the bounding box and drag. When positioned outside the bounding box, the pointer becomes a curved,

Comments
  • Did you try setting transform-origin property?
  • @Abinthatha yes, I did but it dosen't work either.
  • Thank you. I understood why it happens. I better use flex in this case.
  • Thank you. I didn't think using transform: translate() will cause change the center point of the animation.
  • Thank you, that was one of my solution. I tried transform: rotate(180deg) translate(20px, 1px); and I works well but I don't know why did the span move after rotating.
  • The result is what I expected. However I couldn't understand why the span moved like. I supposed transform-origin is at mid point of the span. Am I correct ?
  • Not exactly, we can set the transform-origin at the point where we want, it can be left, right, center or user-specific. Please check tympanus.net/codrops/css_reference/transform-origin.
  • Thank you, I just briefly read the material and it says "The default value of the transform origin is at 50% 50%, which is exactly the center of any given element." as I expected. And in my case, I expected the span would stay in the same place after rotating which means transform-origin: 50% 50%. However, the span moved to somewhere that made me confuse. As @Tusha and Deepak said, it happened because I tried to put the span in the middle using translate.