TommyF
TommyF

Reputation: 7150

Animate fixed element to the center of the viewport

I have an element that is position: fixed; in the upper right corner of the page.
I want it to "grow and move" to the center of the screen when clicked.

The best solution I'm aware of to position something in the dead center of the page is

position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);

Everything works great if I have the element initially positioned with top and left properties, but there is a nasty jump in the animation if the element is initially positioned with right instead of left.

const myFunc = function() {
  let f = document.getElementById('element')

  if (f.className.includes('bar')) {
    f.className = 'foo'
  } else {
    f.className = 'foo bar'
  }
}
.wrapper {
  position: relative;
  width: 100%;
  height: 100%;
}

.foo {
  position: fixed;
  top: 20px;
  right: 20px;
  width: 200px;
  height: 50px;
  background-color: blue;
  transition: 0.5s all;
}

.bar {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 500px;
  height: 500px;
  background-color: red;
  transform: translate(-50%, -50%);
  transition: 0.5s all;
}
<div class="wrapper">
  <div class="foo" id="element" onclick="myFunc()">
  </div>
</div>

Codepen

How do I get rid of the initial jump in the animation to the horizontal center? (i.e. I want the reverse of the "shrinking back to top right", here everything works fine).

Upvotes: 5

Views: 1855

Answers (2)

Pie
Pie

Reputation: 594

I'll admit, this gave me some trouble at first, but with some perseverance I was able to get you there lol

This is because you're not giving the CSS transition a starting point from which to associate the right property positioning. You've switched from giving the element a right property of 20px to a left property of 50%. For the effect you want, you need to give it a starting right position of 20px and an ending right position of 50% for .bar (getting rid of the left: 50%;).

Now, after you fix the above mentioned issue, you'll need to address the .bar class transform method by making it transform: translate(50%, -50%); This is because we've switched to using right x-axis positioning, so we need it to go 50% from the right, rather than -50% (which would make the element go off-screen -50% of the space from the dead center of the parent).

Here is your new live demo. I hope this makes sense :) CSS logically requires all transitions to have a starting point and a valid, cohabitated ending point. Some browsers make the assumption for you, but they should never do that because that will stymie development cross-platform.

Upvotes: 0

web-tiki
web-tiki

Reputation: 103780

You just need to change the right to left on the foo state.
This also implies you change the translate property to : translate(50%,-50%); as the tanslateX value should be from left to right to center the element :

const myFunc = function () {
  let f = document.getElementById('element')

  if(f.className.includes('bar')) {
    f.className = 'foo'
  }
  else {
    f.className = 'foo bar'
  }
}
.wrapper {
  position: relative;
  width: 100%;
  height: 100%;
}

.foo {
  position: fixed;
  top: 20px;
  right: 20px;
  width: 200px;
  height: 50px;
  background-color: blue;
  transition: 0.5s all;
}

.bar {
  position: fixed;
  top: 50%;
  right: 50%;
  width: 500px;
  height: 500px;
  background-color: red;
  transform: translate(50%, -50%);
  transition: 0.5s all;
}
<div class="wrapper">
  <div class="foo" id="element" onclick="myFunc()">
  </div>
</div>

On a side note, if you know the size of your element in both states you could make the transition only on the top and right properties using calc() like this :

const myFunc = function() {
  let f = document.getElementById('element')

  if (f.className.includes('bar')) {
    f.className = 'foo'
  } else {
    f.className = 'foo bar'
  }
}
.wrapper {
  position: relative;
  width: 100%;
  height: 100%;
}

.foo {
  position: fixed;
  top: 20px;
  right: 20px;
  width: 200px;
  height: 50px;
  background-color: blue;
  transition: 0.5s all;
}

.bar {
  position: fixed;
  top: calc(50% - 250px);
  right: calc(50% - 250px);
  width: 500px;
  height: 500px;
  background-color: red;
  transition: 0.5s all;
}
<div class="wrapper">
  <div class="foo" id="element" onclick="myFunc()">
  </div>
</div>

This changes the animation trajectory and prevents the element from beeing animated in one direction with top/right and in the ohter at the same time by the transform property.

Upvotes: 3

Related Questions