Reputation: 6747
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:
transition
for the transform-origin
property → Doesn't worksetTimeout
and slowly setting the transform-origin
at the right position → plays the zoom animation and then jumpsIs there any way to animate both transform: scale()
and transform-origin
in one go?
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
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
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