How to smoothly revert CSS animation to its current state? - html

I've got not animated element as default. There's also a trigger that lets me turn on & off animation on that element. The animation itself is very simple: moves element from left to the right and back.
When I stop animation, then my element obviously goes back to initial position. But it goes back suddenly, not smoothly. So it just changes its position from the one when I turned off animation to initial one. My question is: is there a way to stop it smoothly, so when I turn off the animation it goes back to initial position but smoothly/animating.
Here's my element and animation: http://jsfiddle.net/2Lwftq6r/
HTML:
<input type="checkbox" id="anim">
<label for="anim">Start / stop animation</label>
<div></div>
CSS:
div {
margin-top: 50px;
width: 50px; height: 10px;
background: #000;
transform: translateX(0);
}
#anim:checked ~ div {
-webkit-animation: dance 2s infinite ease-in-out;
-moz-animation: dance 2s infinite ease-in-out;
}
#-webkit-keyframes dance {
0%, 100% { -webkit-transform: translateX(0); }
50% { -webkit-transform: translateX(300px); }
}
#-moz-keyframes dance {
0%, 100% { -moz-transform: translateX(0); }
50% { -moz-transform: translateX(300px); }
}

I just had the same problem and I solved it by not using animation and it works perfectly! Check out my solution:
So I had this spatula that I had to move when hovered over only, and I wanted it to transition back smoothly, so this is what I did:
#Spatula:hover{
animation-direction:alternate;
transform: translate(1.2cm,1cm);
transition: all 1.5s;
-webkit-transition: all 1.5s;
}
#Spatula{
-webkit-transition: all 1.5s;
transition: all 1.5s;
}
Good luck!

You can't archive this effect only CSS3 way, but if you really need it, you could use jQuery + CSS3 Transitions. My solution (http://jsfiddle.net/sergdenisov/3jouzkxr/10/):
HTML:
<input type="checkbox" id="anim-input">
<label for="anim-input">Start / stop animation</label>
<div class="anim-div"></div>
CSS:
.anim-div {
margin-top: 50px;
width: 50px;
height: 10px;
background: #000;
}
.anim-div_active {
-webkit-animation: moving 2s ease-in-out infinite alternate;
animation: moving 2s ease-in-out infinite alternate;
}
.anim-div_return {
-webkit-transition: -webkit-transform 0.5s ease-in-out;
transition: transform 0.5s ease-in-out;
}
#-webkit-keyframes moving {
0% { -webkit-transform: translateX(0); }
100% { -webkit-transform: translateX(300px); }
}
#keyframes moving {
0% { transform: translateX(0); }
100% { transform: translateX(300px); }
}
Javascript:
$('#anim-input').on('change', function() {
var $animDiv = $('.anim-div');
if (this.checked) {
$animDiv.removeClass('anim-div_return')
.addClass('anim-div_active');
return;
}
var transformValue = $animDiv.css('webkitTransform') ||
$animDiv.css('transform');
$animDiv.css({'webkitTransform': transformValue,
'transform': transformValue})
.removeClass('anim-div_active');
requestAnimationFrame(function() {
$animDiv.addClass('anim-div_return')
.css({'webkitTransform': 'translateX(0)',
'transform': 'translateX(0)'});
});
});
P.S.
Vendor prefixes are based on actual browsers list from http://caniuse.com.

Check out This StackOverflow question.
You aren't going to like this answer, but reality is that CSS3
animations aren't really useful to achieve this. To make this work you
would need to replicate a lot of your CSS in your Javascript which
kind of destroys the point (Like for example in this closely related
answer
Change speed of animation CSS3?).
To really make it stop smoothly your best bet would be to write the
animation on a platform like the Greensock animation library
which provides all the tools you need to make it actually smoothly
stop instead of suddenly stop.
There's also another answer below it that does make an effort at using CSS, you can look at that one.

There is also an alternate solution, it might not give you the desired effect of going back to it's original state, but since nobody mentioned it and this problem seems to have no solution, it's possible to pause the animation purely in css, locking it's state until it's started again
To pause the animation you need first to make the animation available even when the checkbox is not checked
And make use of the animation-play-state property
div {
margin-top: 50px;
width: 50px; height: 10px;
background: #000;
animation: dance 2s infinite ease-in-out paused;
}
#anim:checked ~ div {
animation-play-state: running;
}
#keyframes dance {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(300px); }
}
<input type="checkbox" id="anim">
<label for="anim">Start / stop animation</label>
<div></div>

Related

Smooth parent transition on child "translateX"

On GIF below, there's parent div, which contain "left-menu" and "app-content". Left menu is animating, using keyframes and translate property
#LeftMenuContainer {
&.menu_hidden {
animation: slide-out 0.6s forwards;
}
&.menu_shown {
animation: slide-in 0.6s forwards;
}
}
#keyframes slide-in {
0% { transform: translateX(-100%); width: 0; }
100% { transform: translateX(0); width: auto; }
}
#keyframes slide-out {
0% { transform: translateX(0); width: auto; }
100% { transform: translateX(-100%); width: 0; }
}
DOM tree looks like this
<div id="contentContainer" class="flex">
<app-left-menu></app-left-menu>
<div>Be smooth!</div>
</div>
Is there any way, to "smooth" transition parent width, when "left-menu" is hiding? I try to add styles for parent, with
transition-property: width;
transition-duration: 0.6s;
but it doesn't work.
https://im2.ezgif.com/tmp/ezgif-2-a1f74da85c.gif
Assume It only flickers when comes to animate, so you can try to use will-change, css-will-change-property
The will-change CSS property hints to browsers how an element is expected to change. Browsers may set up optimizations before an element is actually changed
If you are using framework like React can try to use framer-motion

Css transition not transitioning back to original state when using animation with it

I am trying to make a loading animation. I am using css transition to transition into the loading by scaling and then using animation to scale out the x axis. But when I try to transition back to the original state it doesn't use the transition anymore it just snaps back. I could use animation for the whole thing but I want to account for the page continuing to load so I don't want to have to write extra javascript logic to handle it. It would be nice if It would just transition on its own.
When you click the following snippet the first time it works fine. But when you click it again it just snaps back to its original state and doesn't use the transition. If you use a different property like opacity in the animation part then it works fine so I'm assuming there is something with the browser not recognizing the current scaled value. Is this a bug or am I doing something wrong?
document.querySelector('.wrapper').addEventListener('click', () => {
document.querySelector('.wrapper').classList.toggle('loading')
})
.wrapper{
position:fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.wrapper > div{
color: white;
display:flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: blue;
transition: transform 500ms ease-out;
transform: scale(1);
}
.wrapper.loading > div{
transform: scale(0.2, 0.002);
animation: loading 1000ms ease-out infinite;
animation-delay: 500ms;
}
#keyframes loading {
0%{
transform: scale(0.2, 0.002)
}
50%{
transform: scale(0.5, 0.002)
}
100%{
transform: scale(0.2, 0.002)
}
}
<div class="wrapper">
<div>click me</div>
</div>
TL;DR
I believe that this happens because CSS transition eventually gives a class two states and transitions between them, when you remove your class you don't change its state, you remove it. my solution would be to add another class to set it back.
CSS transitions work by defining two states for the object using CSS. In your case, you define how the object looks when it has the class "loading" and you define how it looks when it doesn't have the class "saved" (it's normal look). When you remove the class "loading", it will transition to the other state according to the transition settings in place for the object without the "loading" class.
If the CSS transition settings apply to the object (without the "loading" class), then they will apply to both transitions.
your transition CSS settings only apply to .saved and thus when you remove it, there are no controls to specify a CSS setting. You may want to add another class ".fade" that you leave on the object all the time and you can specify your CSS transition settings on that class so they are always in effect.
I don't know a pure css fix for this.
But you can add a different class with a animation that restores to what it was before with JS
const wrapper = document.querySelector(".wrapper");
wrapper.onclick = () => {
if ([...wrapper.classList].includes("loading")) {
wrapper.classList.add("restore");
} else {
wrapper.classList.remove("restore");
}
wrapper.classList.toggle("loading");
};
.wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.wrapper>div {
color: white;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: blue;
animation: none;
transform: scale(1);
transition: transform 500ms ease-out;
}
.wrapper.restore>div {
animation: restore 500ms ease-out;
}
.wrapper.loading>div {
transform: scale(0.2, 0.002);
animation: loading 1000ms 500ms ease-out infinite;
}
#keyframes restore {
0% {
transform: scale(0.2, 0.002);
}
100% {
transform: scale(1);
}
}
#keyframes loading {
0% {
transform: scale(0.2, 0.002)
}
50% {
transform: scale(0.5, 0.002)
}
100% {
transform: scale(0.2, 0.002)
}
}
<div class="wrapper">
<div>click me</div>
</div>
You can use animation iteration count property:
div {
animation-iteration-count: 2;
}
or use fill mode to freeze the animation at the end:
-webkit-animation-fill-mode: forwards;

Can't get rotation on a custom Angular element

I've used the following CSS taken from here.
:host {
animation: rotation 2s infinite linear;
border: 10px solid yellow;
}
#keyframes rotation {
from { transform: rotate(0deg); }
to { transform: rotate(359deg); }
}
I don't get to see any rotation, although I can see the border, so I know that I target the correct element. I don't think it's required to have an IMG (and mine is a custom thingy in Angular). It works on the IMG and also on a DIV. Not sure how to diagnose it further as rotations/animations aren't my strongest suite.
One approach is to put the custom component in a DIV and rotate that. However, it does rotate around the middle of the screen (wiiiide circle) instead of spinning around itself.
<div id="loading">
<my-icon-globe></my-icon-globe>
</div>
#loading { animation: rotation 2s infinite linear; }
#keyframes rotation {
from { -webkit-transform: rotate(0deg); }
to { -webkit-transform: rotate(359deg); }
}
You can adjust transform-origin of the DIV if you attempt the second approach.

CSS animation of scale & color together causes font pixelation

I fire css animation of font-icon by adding it a class. The animation scaling icon from 1 to 30, and change color from #000 to #ff0000.
While it works fine in mozilla, it will make icon scales like if it was low quality png image in chrome, opera and safari. Can't check ie.
It can be fixed in chrome and opera by isolating color animation in ::before pseudoelement.
But in safari even just scale animation alone treats font-icon like png image.
As animation is finished, icon recover its font nature, and pixelation disappears.
Examples:
works only in mozilla http://codepen.io/g1un/pen/Kgrpjq
works in mozilla, chrome, opera http://codepen.io/g1un/pen/BLzoWp
Code, that works properly only in mozilla:
<div>
<h1></h1>
</div>
div {
display: flex;
justify-content: center;
height: 100vh;
align-items: center;
}
h1 {
position: relative;
font-size: 34px;
cursor: pointer;
}
h1::before {
content: 'A';
}
h1.anima {
animation: anima;
-webkit-animation: anima;
animation-duration: 3s;
-webkit-animation-duration: 3s;
animation-fill-mode: forwards;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes anima {
0% {
transform: scale(1);
color: #000;
}
100% {
transform: scale(30);
color: #ff0000;
}
}
#keyframes anima {
0% {
transform: scale(1);
color: #000;
}
100% {
transform: scale(30);
color: #ff0000;
}
}
$('h1').on('click', function(){
$(this).addClass('anima');
var _this = $(this);
setTimeout(function(){
_this.removeClass('anima');
}, 5000);
});
CSS changes, that helps chrome and opera:
h1.anima::before {
animation: anima-before;
-webkit-animation: anima-before;
animation-duration: 3s;
-webkit-animation-duration: 3s;
animation-fill-mode: forwards;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes anima {
0% {
transform: scale(1);
}
100% {
transform: scale(30);
}
}
#keyframes anima {
0% {
transform: scale(1);
}
100% {
transform: scale(30);
}
}
#keyframes anima-before {
0% {
color: #000;
}
100% {
color: #ff0000;
}
}
#-webkit-keyframes anima-before {
0% {
color: #000;
}
100% {
color: #ff0000;
}
}
Does anyone know better way to make chrome and opera animates properly without pseudoelement hack? And who knows what's wrong with safari, and how pixelated scaling can be fixing in it?
UPDATE:
As #ZBerg has mentioned in his comment: "font smoothing options have a wide array support varients. If something has affected your desktop profile it may have a knock on effect (google - smooth edges of screen fonts)".
Taking into account, that I haven't no more problems with chrome (but really had it as you can see via screenshot, linked in comment), something has really affected my desktop (but I can't google smth exactly about smoothing issue while scaling).
On the whole, I guess that the full answer to my question must include:
the decision for safari (or explanation what's wrong with it);
(optionally) explanation of what was wrong with chrome.
Under explanation I mean link to the issue report or regarding chrome the way to reproduce the error.
One solution that works for me is scale the parent, 'div' in this case and made the scale over him.
CSS
div.anima {
animation: anima;
-webkit-animation: anima;
animation-duration: 3s;
-webkit-animation-duration: 3s;
animation-fill-mode: forwards;
-webkit-animation-fill-mode: forwards;
}
JS:
$('div').on('click', function(){
as follows:
updated

How to scale an element when it loads using only CSS?

I'm loading an element that has the initial css values of :
.popOver {
height: 100%;
width: 100%;
position: fixed;
background-color: #d9dfe5;
transition: all 2s ease-in-out;
transform: scale(0,0);
}
I need to change to scale(1, 1) when the element loads in the page and see the transition. Anyone can help?
transition will apply the moment you load the page so that is not an ideal solution in your situation, what you will need is CSS #keyframes where you need to set scale(0,0) to the class and then scale(1,1) for 100% as keyframes will shoot after the page is completely loaded.
Demo (Refactored the code a bit and added animation-fill-mode to prevent the popup from scaling back to 0 so using rev 2)
.popOver {
height: 100%;
width: 100%;
position: fixed;
background-color: #d9dfe5;
-webkit-animation: bummer 2s;
animation: bummer 2s;
-webkit-transform: scale(0,0);
transform: scale(0,0);
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards; /* Add this so that your modal doesn't
close after the animation completes */
}
#-webkit-keyframes bummer {
100% {
-webkit-transform: scale(1,1);
}
}
#keyframes bummer {
100% {
transform: scale(1,1);
}
}
Here as I explained before, am setting the initial scale of the element to 0,0 and than am animating it to 1,1 using keyframes. The time of the animation can be controlled by tweaking the 2s which is nothing but 2 Seconds.