1

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:

  • Setting transition for the transform-origin property → Doesn't work
  • Doing it manually in JS with setTimeout and slowly setting the transform-origin at the right position → plays the zoom animation and then jumps

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>
MauriceNino
  • 6,214
  • 1
  • 23
  • 60

2 Answers2

2

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>
Alberto Rhuertas
  • 733
  • 4
  • 12
  • Thanks for the comment. Yeah, I have read this, but unfortunately the origin gets calculated from JavaScript and is dynamically, depending on the mouse position. `@keyframe` is static and I can't set it from JS – MauriceNino Aug 12 '20 at 09:54
  • @MauriceNino oops. I added a new solution, may be it can help you. – Alberto Rhuertas Aug 12 '20 at 10:00
  • Thanks again, but it does still jump. This is exactly what I tried and put in my snippet, but `transform-origin`, does not seem to be able to be put in `transition` – MauriceNino Aug 12 '20 at 10:05
  • @MauriceNino mmm whats your current browser? I mean, I can see clearly the difference with `transition: transform-origin .2s ease-in-out, transform 4s ease-in-out;` and `transition: transform 4s ease-in-out;` :S – Alberto Rhuertas Aug 12 '20 at 10:14
  • I am using Chrome Version 83.0.4103.116. What can you see in my example snippet? Does it jump or animate both? – MauriceNino Aug 12 '20 at 10:19
  • 1
    Got it...It's a Chrome bug. I'm with Firefox and it works fine. [See your snippet in my browser](https://thumbs.gfycat.com/DefiniteSlimHorsefly-size_restricted.gif). Also you can check [this question](https://stackoverflow.com/questions/57000539/chrome-75-break-transform-origin-animation) – Alberto Rhuertas Aug 12 '20 at 10:26
  • Ah okay, got it that's unfortunate. Will wait if someone else gives an answer with an alternative way, but if not I would like to accept your answer if you change it to the same as your comment (Chrome bug), so that other users with similar problems can find the answer easily :) – MauriceNino Aug 12 '20 at 11:16
0

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.

FTav
  • 389
  • 4
  • 13