MauriceNino
MauriceNino

Reputation: 6747

Is there any way to animate transform-origin in combination with transform: scale?

So I have an image viewer which has a zoom functionality, which works via the transform: scale() property.

Animating the zoom is no problem with transition: transform .3s.

To make the zoom on the mousewheel go to where the mousewheel is pointed, I calculated the correct position to set the new origin, but when I set it, it just jumps there with no animation.

What I have tried:

Is there any way to animate both transform: scale() and transform-origin in one go?

Dupe

As the last question has been closed as a duplicate of How to have multiple CSS transitions on an element?, here is a snippet, to show you that this is not my question.

const img = document.querySelector('#container img');

let on = true;

const toggleEffect = () => {
  if(on) {
    img.style.transform = 'scale(2.5)';
    img.style.transformOrigin = '80% 80%';
  } else {
    img.style.transform = 'scale(1.4)';
    img.style.transformOrigin = '20% 20%';
  }
  
  on = !on;
};
#container {
  width: 500px;
  height: 300px;
  overflow: hidden;
  background-color: red;
}

#container img {
  height: 100%;
  width: 100%;
  object-fit: contain;
  transform: scale(1.4);
  transform-origin: 20% 20%;
  transition: transform .3s, transform-origin .3s;
}
<div id="container">
  <img src="https://images.unsplash.com/photo-1482066490729-6f26115b60dc?ixlib=rb-1.2.1&auto=format&fit=crop&w=2004&q=80"/>
</div>
<button onclick="toggleEffect()">Toggle</button>

Upvotes: 1

Views: 1357

Answers (2)

FTav
FTav

Reputation: 409

For me the only way to get around this bug was to ensure a redraw of the element on each "animation" (in this case transition) frame as you can clearly see via getComputedStyle that the transform-origin is correctly transitioned!

Basically I added eventlisteners for the transitionstart and transitionend and on each animationframe toggle some style attribute that enforces a redraw (f.e. in my case margin-left from 0 to 1 to 0px until the animation is finished)

function forceRedraw(ts) {
  this.style.marginLeft = this.style.marginLeft == '1px' ? '0px':'1px';
  if (this.classList.contains('transitioning'))
    requestAnimationFrame(forceRedraw.bind(this));
}

In my example I transition rotation and the transform-origin (from top left to bottom left) at the same time.

https://codepen.io/ftav/pen/QWvYEPj

Depending on which element you modify this might have more or less of a performance impact. It works fine for me. I just wish they would fix the bug and this workaround could go away.

Upvotes: 0

Alberto Rhuertas
Alberto Rhuertas

Reputation: 773

EDIT: Technically this is a duplicated. (Chrome Bug in some versions)

- Using both transition:

body, div {
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

div {
  background-color: gray;
  width: auto;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 30px;
  transform: scale(1.1);
  transform-origin: 50% -30px -100px;
  transition: transform-origin .2s ease-in-out, transform 4s ease-in-out;
}
div:hover {
  transform: scale(1.7);
  transform-origin: 100px 100px;
}
<div>Test</div>

- Using animation with@keyframes:

body,div {
  width: 100%;
  height: 100vh;
}
        div {
          width: auto;
          display: flex;
          justify-content: center;
          align-items: center;
          transform-origin: 0 0 0;
          animation: scale-origin 3s infinite;
          font-size: 30px;
         }

         @keyframes scale-origin {
            0% {
              transform: scale(.5);
               transform-origin: 100px 100px 1000px;
            }
           100% {
             transform: scale(1.1);
             transform-origin: left 500px -30px
            }
         }
<div>Test</div>

Upvotes: 2

Related Questions