How can I reverse an animation after hover like a transition? - html

CSS transitions animate back to the previous state, following the transition rules in reverse. But animations don't. They just go back to the original state abruptly (eg., when :hover or the class is removed). Is there a way to make animations behave like transitions (like the code below).
.box {
width: 50px;
height: 50px;
}
.box.transition {
background-color: green;
transition: 500ms linear all;
}
.box.transition:hover {
width: 100px;
height: 100px;
}
.box.animation {
background-color: red;
animation-duration: 500ms;
animation-fill-mode: both;
animation-timing-function: linear;
}
.box.animation:hover {
animation-name: zoom;
}
#keyframes zoom {
to {
width: 100px;
height: 100px;
}
}
<!-- If I "unhover" .box.animation, it goes back to the original state abruptly -->
<div class="box transition"></div>
<div class="box animation"></div>

Animation like transitions
Yes and no you can make them behave similarly but not identical, but not with :hover.
The hover state ends when you stop hovering an element. So when you stop hovering it, the css rule that targets :hover will instantly be removed.
Transition define what to do when there are changes to different states.
Animation go from one state to another defined by an animation.
Setting the animation direction will make it look closer to a transition.
animation-direction: alternate; will loop over an animation from start to finish and backagain.

Related

Transition to default class upon removal of CSS class with animation

I am animating a button. A class is assigned depending on my app state. This is actually implemented in Svelte as follows:
<div class="default"
class:run-animation="{$animate === true}">
But the equivalent in vanilla javascript without Svelte is:
let element = document... (find element)
element.classList.add("run-animation")
... later ...
element.classList.remove("run-animation)
For the sake of a minimum reproducible example, the classes I'm trying to animate/transition between look like this:
.default {
top: 20px;
color: white;
}
#keyframes button-animation {
from {
top: 20px;
color: white;
}
20% {
top: 23px;
color: white;
}
25% {
color: red;
}
100% {
top: 23px;
color: red;
}
}
.run-animation {
animation-name: button-animation;
animation-duration: 2s;
/* Preserve the effect of the animation at ending */
animation-fill-mode: forwards;
}
I add the class to the element, and the button animates just like I want it to. My problem arises when I remove the class. I want the button to transition smoothly back to the default CSS. I have tried adding the animation to the run-animate class:
.run-animation {
animation-name: ... ;
top: 23px;
color: red;
}
I have come across many people stating the transition upon class removal will apply if I add a transition property to the default class. I have tried this as follows:
.default {
...
transition: all 3s linear;
}
But it isn't working. The animation runs smoothly when it is added but the styling immediately reverts to the default when the class is removed (no smooth transition).
MY GOAL: I want to smoothly transition away from the end-state of the animation to the default class when the animate class is removed. Is this possible?
Ideally, I'm adding the class with the Svelte logic at the top so the animation should not be triggered in javascript but rather naturally occur as a result of class assignment.
(My code in practice is a little more complicated than shown, the button has another class with styles not being animated at all and the animation includes more styles such as box-shadow and text-shadow. Still, I don't see why this should be more problematic than just color and top included above)
// JS only toggles '.animation'
document.querySelector("button").addEventListener("click", () => {
document.querySelector("div.default").classList.toggle("animation");
});
body {display: flex}
button {position: absolute; left: 120px}
div.default {
position: absolute;
width: 100px;
height: 100px;
background: darkgreen;
}
/* Above code to make a visible working example */
div.default {
top: 20px;
color: white;
transition: top 0.4s, color 0.1s 0.4s;
}
#keyframes define-animation {
from {
top: 20px;
color: white;
}
}
div.default.animation {
animation-name: define-animation;
animation-duration: 2s;
top: 24px;
color: red;
}
<div class="default">I'm colourful</div>
<button>Toggle ".animation"-class</button>
Above is a working snippet with an animation running on class addition and no reverse transition on class removal. I have tried setting animation direction to opposite values in .default and .animation. I have tried defining the .animate end state properties in the class and/or in the keyframes to attributes.
EDIT: It works now! How?
You cannot apply:
animation-fill-mode: forwards;
The end-attributes need to be defined in the animate class not in the keyframe.
The animation plays when the class is added. Transition timings are used when the class is removed (if the animation has completed).
To get a transition effect, you can use the transition-property.
The transition-property can be used here, since every property you want to animate only has a start- and end-value.
Translating animation-percentages to seconds
To translate the percentages of your CSS Animation button-animation to seconds, you just calculate 'percentage' * 'animation-duration'.
This works for both the transition-duration-property as well as for the transition-delay-property.
Example:
color is being animated from 20% to 25%, which is a duration of 5% with a delay of 20%.
All in all, the animation should take 2 seconds.
So we calculate for:
transition-duration: 5% * 2s = 0.1s
transition-delay: 20% * 2s = 0.4s
With that, we can add transition: color 0.1s 0.4s to the .default-class.
Why add it to .default, and not to .animation?
If we were to add the transition-property to .animation, the following would happen:
When adding .animation, there will be a transition-effect, since the element now has a transition-property defined.
But when removing .animation, the element would no longer have a transition-property defined, meaning there would be no transition.
Now, we want to transition on both adding and removing .animation, meaning we want to have a transition-property defined both when .animation is present and when it is not. That means, transition should not be defined in .animation.
// JS only toggles '.animation'
document.querySelector("button").addEventListener("click", () => {
document.querySelector("div.default").classList.toggle("animation");
});
body {display: flex}
button {align-self: center}
div.default {
position: relative;
border: 2px solid black;
width: 100px;
height: 100px;
background: darkgreen;
}
/* Above code to make a visible working example */
div.default {
top: 20px;
color: white;
transition: top 0.4s, color 0.1s 0.4s;
}
div.default.animation {
top: 23px;
color: red;
}
<div class="default">Some text to see the "color"-property</div>
<button>Toggle ".animation"-class</button>
Why does it behave differently...
...when placing the properties inside the to-section of the animation, than when placing them inside .animation itself?
That is, because the properties are not directly applied to the element itself, but rather the element is stopped in its animation (right at the very end), giving only the appearance of the properties being actually applied.
Removing animation-fill-mode: forwards shows the actually applied properties after the animation has played. Those actually applied properties will be the start-values for transition after .animation is removed.
When defining these properties in .animation, they will inherently be the to-values for the animation (if not defined otherwise in animation itself), and be the applied properties of the element.
That means, when removing .animation, the transition will start from there.

Change slowly z-index value after hover has ended on image

I have blog post with set of images that are enlarged on hover. My problem is that when i enlarge element and it overlaps with other image that is later in page render order then the next image is on top of the enlarged one.
The easy way to stop this is to give some kind of z-index on :hover pseudo selector. But then i have very pesky problem when just after I stop hovering my image then next one is on top of it for fraction of second.
You can see behaviour in this imgur album or on jsfiddle(hover first image)
In short i have following css for hovering effect:
.photo-exp
{
position: relative;
transition: all .4s ease-in-out;
/* some properties deleted which have no connection to hovering effect */
}
.photo-exp:hover
{
transform: scale(1.7);
z-index : 10;
}
It would be very easy to have same effect with javascript and setTimeout function.
But i would like to avoid javascript solution and have some CSS workaround which will change slowly z-index in time after hovering ends.
I tried CSS transition but it is not working
I tried to eddit this snippet but i could not get it working in the way that i wanted.
You need to assign a new transition-delay property, and remove it as soon as the hover begins. That way the z-index can persist for some time even after the mouse is gone. It's a little counter-intuitive; I would expect that the delay should be added on hover and removed off-hover but the opposite works on chrome:
.expander {
position: absolute;
left: 50%; top: 50%;
width: 100px; height: 100px;
margin-top: -50px; margin-left: -50px;
z-index: 1;
transition: transform 400ms 0ms, z-index 0ms 400ms; /* That final "400ms" delays the z-index transition! */
}
.expander:hover {
transform: scale(1.8);
z-index: 2; /* A hovered expander is always on top */
transition: transform 400ms 0ms, z-index 0ms 0ms; /* Remove the z-index transition delay on hover. This is counter-intuitive but works. */
}
.expander:nth-child(1) {
margin-left: -105px;
background-color: #a00000;
}
.expander:nth-child(2) {
margin-left: 5px;
background-color: #00af00;
}
<div class="expander"></div>
<div class="expander"></div>
Note that (unless you try to mouse around really quickly in order to break it) neither square bleeds through the other, not even for a frame, when they expand.
I've also finally managed to solve by myself my problem.
It's more intuitive than #Gershom Maes answer in my opinion.
Fiddle
I have used animation system to achieve the result.
#keyframes nohovering {
0% { z-index: 9; }
100% { z-index: 1; }
}
#keyframes hovering {
0% { z-index: 10; }
100% { z-index: 10; }
}
First one will be by default fired on selector without :hover like this
animation: nohovering 0.9s;
It will guarantee that after i complete my hovering it will go smoothly down from z-index 9 to z-index 1. After hovering my image will be on top of other images. When i tested it for z-index 10 for 0% i had a little glitch when i tried to hovered 2 images at same time and then hover only 1 of them.
For my hovering selector I used:
animation: hovering 0.1s infinite;
It will just loop my image on z-index 10. On hover it will always be on top of the other images. Short animation time guarantee that it will go off after hovering stopped in maximum time of 0.1s.
After deleting normal static z-indexes it works.

CSS3 Animation on Link Hover not working properly

Background:
Trying to create an esthetically pleasing linking hover for the future
Current JSFiddle:
Available here for FF Browser.
body {
color:#ffffff;
font-family:"Helvetica";
font-size:12pt;
background-color:#000000;
font-weight:bold;
}
a:link {
color:white;
text-decoration:none;
}
a:hover {
animation: myhover 1s;
transition-timing-function: ease-in-out;
font-weight: bold;
}
#keyframes myhover {
from {
background-color: black;
color: white;
}
to {
background-color: white;
color: black;
}
}
Problem:
The transition works in what concerns the effect, but for some reason, even if you remain with the cursor on top of the link, it reverts to the FROM state without even a fade back from TO to FROM.
Need:
What code change is needed to stay at TO effect, until you take the cursor out of the hovered LINK and it reverts the effect to FROM?
Code type restrictions:
I do not wish to use JavaScript or JQuery in the solution, only CSS and HTML.
Many Thanks
Alban
There are two parts to your question:
1) How do you retain the current animation state?
Add animation-fill-mode to your CSS rule:
a:hover {
-webkit-animation: myhover 1s;
animation: myhover 1s;
transition-timing-function: ease-in-out;
font-weight: bold;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
2) How do you revert to the "From" transition
Fairly straight forward - you set the "default" animation properties of the link.
a:link {
-webkit-animation: nohover 1s;
animation: nohover 1s;
color:white;
text-decoration:none;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
The only issue you might run into is the page load. You'll notice the animations kick off before any interaction occurs (for -webkit-based-browsers). Without JavaScript, you'll need to consider this and how your animations will look.
A fiddle for demonstration: http://jsfiddle.net/6hxhxg5t/
You need to set the animation-fill-mode to forwards for the animation to retain the state as at its last keyframe.
animation: myhover 1s forwards;
or
animation-name: myhover;
animation-duration: 1s;
animation-fill-mode: forwards;
Option 1 Demo | Option 2 Demo
Note:
The demo uses -webkit- prefix as I am testing on Chrome, but the same would work with either the -moz- prefix or without any prefixes.
Achieving a reverse effect on hover out would not be possible without adding extra code as animation do not work like transition. The reverse effect would be better achieved with JavaScript/jQuery as the reverse animation cannot be kicked-off by default on the base class without it appearing once on page load also. Here is a way to achieve both the forward and reverse animation effects using jQuery. jQuery is not a must and the same can be done with vanilla JS also but I just used jQuery for doing a quick sample.
Option 3: (Using transtions instead of animations)
If your objective is only to linearly change the background-color and the color properties on mouse hover, then actually transition is a much better option to make use of instead of animation. Transitions can automatically answer both of your concerns. It can make the end state retained till the mouse is hovered out and the hover out will also cause the reverse effect to happen.
a:link {
color:white;
text-decoration:none;
transition: background-color 1s, color 1s;
/*transition: all 1s;*/ /* use this line if you wish to transition all properties */
transition-timing-function: ease-in-out;
}
a:hover {
font-weight: bold;
background-color: white;
color: black;
}
Option 3 Demo

Rotating a banner on hover causes it to spasm

I have an image on my website. When I hover over it I want it to do a 360 spin animation.
I'm currently doing so in CSS:-
.img-responsive:hover {
transition-duration: 2s;
transform:rotate(360deg);
}
However, when the user hovers at the edge of the image, the image rotates and is no longer being hovered over at the edge causing the image to "spasm".
How can I achieve a proper looking and stable rotation? JavaScript would work too.
You can fix it by adding small delay to your transition
Working Demo
.img-responsive:hover {
transition-duration: 2s;
transform:rotate(360deg);
transition-delay: 0.5s;
}
Update:
If you can use animation, You can do this
Updated Demo
.img-responsive {
animation: rotateme;
}
.img-responsive:hover {
animation: rotateme 5s;
}
You can make the image bigger when hovered, so that it's difficult that the user accidentally unhovers
if there is no padding or margin, set
.img-responsive:hover {
transition-duration: 2s;
-webkit-transform:rotate(360deg);
-moz-transform:rotate(360deg);
transform:rotate(360deg);
padding: 50px;
margin: -50px;
}
Increasing the padding to 50px makes sure that the mouse is still on it. Changing the margin in the same amount, but in opposite sense makes it stay at the same location.
fiddle

Fluidly stop a CSS3 Animation halted mid-animation? [duplicate]

This question already has answers here:
css3 animation on :hover; force entire animation
(7 answers)
Closed 8 years ago.
I'm running into a small problem attempting to have an animation stopped mid-animation gracefully returning to the default state. I'm applying an animation to a child element using the :hover pseudoclass on a parent element, but would like the animation to gracefully return to the default state when I stop hovering on the element. I'm getting the feeling that I shouldn't be using the :hover pseudoclass here and there's another method of approaching it, but I can't seem to figure out how to get it to transition off. It just instantly reverts to the base state, which of course makes sense because the animation is being stripped - but that's not the desired effect. This is the current HTML:
<div id="el1">
<div class="child"></div>
</div>
and the current CSS
#el1 {
margin-top: 100px;
position: relative;
background: transparent url(./image.png) no-repeat;
height: 100px;
width: 100px;
}
#el1 .child {
height: 100%;
width: 100%;
background-color: white;
opacity: 0;
}
#el1:hover .child {
-webkit-animation-name: semiopacity;
-webkit-animation-iteration-count: infinite;
-webkit-animation-duration: 0.5s;
-webkit-animation-direction: alternate;
-webkit-animation-timing-function: linear;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes semiopacity {
from { opacity: 0; }
to { opacity: 0.4; }
}
To recap: There's a flashing white effect over the image when hovering. I want it to animate back to the original keyframe when I stop hovering instead of snapping back to "off". I can change either the HTML or CSS; no restrictions on how to make it work.
css3 animation on :hover; force entire animation
Something similar asked a couple days ago. This person also wanted their entire animation to show after hover was removed. Check it out.