Chaining CSS animations and retain values between animations - html

Fiddle here: https://jsfiddle.net/gkzqLfxa/1/
I'm trying to chain CSS animations such that I can write generic animations that don't need to have prior knowledge of the values that happened before them.
For example:
MyDiv has an animation that animates its opacity from 0 to SomeValue. Then a second animation fades out, from SomeValue to 0. Currently, I have to write an animation to fade in to 0.5, then another animation that fades out from 0.5 to 0.
I attempted to do this by not putting in the opacity property in the 0% keyframe, like so:
#keyframes fadeinhalf
{
0% { opacity: 0; }
100% { opacity: 0.5; }
}
#keyframes fadeinfull
{
0% { opacity: 0; }
100% { opacity: 1; }
}
#keyframes fadeout
{
0% {
}
100%{
opacity: 0;
}
}
However, when I run this, with fadeinhalf first, then fadeout second, there is a hitch at the 0% keyframe of fadeout where it goes back to opacity: 1;.
I would rather have something where I can write that first animation custom, but the fade out animation could be generic, and fade out from any opacity to 0. Is this possible?

-webkit-animation-fill-mode: forwards;
will retain the state of the final keyframe, though I've not used it for this exact application…
EDIT: possible alternative solution
You may need to use the jQuery Keyframes library (http://keyframes.github.io/jQuery.Keyframes/)
and do something like:
var supportedFlag = $.keyframe.isSupported();
var setOpacity = 0.5;
function setKeyFrame(data) {
var setOpacity = data;
$.keyframe.define([{
name: 'magic',
'0%': {'opacity': 0},
'100%': {'opacity': setOpacity}
}]);
};
$(document).ready(function() {
setKeyFrame(1);
$('#target-element').playKeyframe(
'magic 1s linear 0s infinite normal forwards',
complete
);
});

Related

How do I pause an animation? [duplicate]

So, it is possible to have reverse animation on mouse out such as:
.class{
transform: rotate(0deg);
}
.class:hover{
transform: rotate(360deg);
}
but, when using #keyframes animation, I couldn't get it to work, e.g:
.class{
animation-name: out;
animation-duration:2s;
}
.class:hover{
animation-name: in;
animation-duration:5s;
animation-iteration-count:infinite;
}
#keyframe in{
to {transform: rotate(360deg);}
}
#keyframe out{
to {transform: rotate(0deg);}
}
What is the optimal solution, knowing that I'd need iterations and animation itself?
http://jsfiddle.net/khalednabil/eWzBm/
I think that if you have a to, you must use a from.
I would think of something like :
#keyframe in {
from: transform: rotate(0deg);
to: transform: rotate(360deg);
}
#keyframe out {
from: transform: rotate(360deg);
to: transform: rotate(0deg);
}
Of course must have checked it already, but I found strange that you only use the transform property since CSS3 is not fully implemented everywhere. Maybe it would work better with the following considerations :
Chrome uses #-webkit-keyframes, no particuliar version needed
Safari uses #-webkit-keyframes since version 5+
Firefox uses #keyframes since version 16 (v5-15 used #-moz-keyframes)
Opera uses #-webkit-keyframes version 15-22 (only v12 used #-o-keyframes)
Internet Explorer uses #keyframes since version 10+
EDIT :
I came up with that fiddle :
http://jsfiddle.net/JjHNG/35/
Using minimal code. Is it approaching what you were expecting ?
Its much easier than all this: Simply transition the same property on your element
.earth { width: 0.92%; transition: width 1s; }
.earth:hover { width: 50%; transition: width 1s; }
https://codepen.io/lafland/pen/MoEaoG
I don't think this is achievable using only CSS animations. I am assuming that CSS transitions do not fulfil your use case, because (for example) you want to chain two animations together, use multiple stops, iterations, or in some other way exploit the additional power animations grant you.
I've not found any way to trigger a CSS animation specifically on mouse-out without using JavaScript to attach "over" and "out" classes. Although you can use the base CSS declaration trigger an animation when the :hover ends, that same animation will then run on page load. Using "over" and "out" classes you can split the definition into the base (load) declaration and the two animation-trigger declarations.
The CSS for this solution would be:
.class {
/* base element declaration */
}
.class.out {
animation-name: out;
animation-duration:2s;
}
.class.over {
animation-name: in;
animation-duration:5s;
animation-iteration-count:infinite;
}
#keyframes in {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#keyframes out {
from {
transform: rotate(360deg);
}
to {
transform: rotate(0deg);
}
}
And using JavaScript (jQuery syntax) to bind the classes to the events:
$(".class").hover(
function () {
$(this).removeClass('out').addClass('over');
},
function () {
$(this).removeClass('over').addClass('out');
}
);
Creating a reversed animation is kind of overkill to a simple problem. What you need is:
animation-direction: reverse
However, this won't work on its own because animation spec forgot to add a way to restart the animation, so here is how you do it with the help of JS
let item = document.querySelector('.item')
// play normal
item.addEventListener('mouseover', () => {
item.classList.add('active')
})
// play in reverse
item.addEventListener('mouseout', () => {
item.style.opacity = 0 // avoid showing the init style while switching the 'active' class
item.classList.add('in-active')
item.classList.remove('active')
// force dom update
setTimeout(() => {
item.classList.add('active')
item.style.opacity = ''
}, 5)
item.addEventListener('animationend', onanimationend)
})
function onanimationend() {
item.classList.remove('active', 'in-active')
item.removeEventListener('animationend', onanimationend)
}
#keyframes spin {
0% {
transform: rotateY(0deg);
}
100% {
transform: rotateY(180deg);
}
}
div {
background: black;
padding: 1rem;
display: inline-block;
}
.item {
/* because span cant be animated */
display: block;
color: yellow;
font-size: 2rem;
}
.item.active {
animation: spin 1s forwards;
animation-timing-function: ease-in-out;
}
.item.in-active {
animation-direction: reverse;
}
<div>
<span class="item">ABC</span>
</div>
we can use requestAnimationFrame to reset animation and reverse it when browser paints in next frame.
Also use onmouseenter and onmouseout event handlers to reverse animation direction
As per
Any rAFs queued in your event handlers will be executed in the ​same
frame​. Any rAFs queued in a rAF will be executed in the next frame​.
function fn(el, isEnter) {
el.className = "";
requestAnimationFrame(() => {
requestAnimationFrame(() => {
el.className = isEnter? "in": "out";
});
});
}
.in{
animation: k 1s forwards;
}
.out{
animation: k 1s forwards;
animation-direction: reverse;
}
#keyframes k
{
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
<div style="width:100px; height:100px; background-color:red"
onmouseenter="fn(this, true)"
onmouseleave="fn(this, false)"
></div>
Would you be better off having just the one animation, but having it reverse?
animation-direction: reverse
Using transform in combination with transition works flawlessly for me:
.ani-grow {
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
-ms-transition: all 0.5s ease;
transition: all 0.5s ease;
}
.ani-grow:hover {
transform: scale(1.01);
}
I've put together a CodePen with a CSS-only fix and one with 2 lines of jQuery to fix the on-page load issue. Continue reading to understand the 2 solutions in a simpler version.
https://codepen.io/MateoStabio/pen/jOVvwrM
If you are searching how to do this with CSS only, Xaltar's answer is simple, straightforward, and is the correct solution. The only downside is that the animation for the mouse out will play when the page loads. This happens because to make this work, you style your element with the OUT animation and the :hover with the IN animation.
svg path{
animation: animateLogoOut 1s;
}
svg:hover path{
animation: animateLogoIn 1s;
}
#keyframes animateLogoIn {
from {stroke-dashoffset: -510px;}
to {stroke-dashoffset: 0px;}
}
#keyframes animateLogoOut {
from {stroke-dashoffset: 0px;}
to {stroke-dashoffset: -510px;}
}
Some people found this solution to be useless as it played on page load. For me, this was the perfect solution. But I made a Codepen with both solutions as I will probably need them in the near future.
If you do not want the CSS animation on page load, you will need to use a tiny little script of JS that styles the element with the OUT animation only after the element has been hovered for the first time. We will do this by adding a class of .wasHovered to the element and style the added class with the OUT Animation.
jQuery:
$("svg").mouseout(function() {
$(this).addClass("wasHovered");
});
CSS:
svg path{
}
svg.wasHovered path{
animation: animateLogoOut 1s;
}
svg:hover path{
animation: animateLogoIn 1s;
}
#keyframes animateLogoIn {
from {stroke-dashoffset: -510px;}
to {stroke-dashoffset: 0px;}
}
#keyframes animateLogoOut {
from {stroke-dashoffset: 0px;}
to {stroke-dashoffset: -510px;}
}
And voila! You can find all of this and more on my codepen showing in detail the 2 options with an SVG logo hover animation.
https://codepen.io/MateoStabio/pen/jOVvwrM
Have tried several solutions here, nothing worked flawlessly; then Searched the web a bit more, to find GSAP at https://greensock.com/ (subject to license, but it's pretty permissive); once you reference the lib ...
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.4/gsap.min.js"></script>
... you can go:
var el = document.getElementById('divID');
// create a timeline for this element in paused state
var tl = new TimelineMax({paused: true});
// create your tween of the timeline in a variable
tl
.set(el,{willChange:"transform"})
.to(el, 1, {transform:"rotate(60deg)", ease:Power1.easeInOut});
// store the tween timeline in the javascript DOM node
el.animation = tl;
//create the event handler
$(el).on("mouseenter",function(){
//this.style.willChange = 'transform';
this.animation.play();
}).on("mouseleave",function(){
//this.style.willChange = 'auto';
this.animation.reverse();
});
And it will work flawlessly.
Try this:
#keyframe in {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#keyframe out {
from {
transform: rotate(360deg);
}
to {
transform: rotate(0deg);
}
}
supported in Firefox 5+, IE 10+, Chrome, Safari 4+, Opera 12+

Css auto hide / show every 2s

I want to hide and show a div using css like :
Show => Hide => Show =>...
for do that I've tried that code:
#showMe {
animation: cssAnimation 0s 2s forwards;
visibility: hidden;
}
#keyframes cssAnimation {
to { visibility: visible;
}
}
but it will hide it only plz guys help!!
One way to do this is by adding keyframes for a specific progress (time-progress).
Some attributes in CSS are not animateable, so if you try to animate them they get instantly set to your "to" value, which is visible. To work around this, we simply set the visibility to hidden(in css) and keep it until 50% (in animation). At 51% we set it to visible, where it gets instantly shown (until the end).
To make it "blinking" you can repeat this animation by appending infinite which is a shorthand for animation-iteration-count: infinite.
#showMe {
animation: cssAnimation 1s infinite;
visibility: hidden;
}
#keyframes cssAnimation {
50% {
visibility: hidden;
}
51% {
visibility: visible;
}
}
<div id="showMe">(╯°□°)╯︵ ┻━┻</div>
Try to add property animation-iteration-count and set value it to infinite. It should play the animation infinite times.

Making element invisible while delay function is working

I want elements to appear one by one on the page with an animation. I created the animation but I don’t know how to hide (not display: none) the element while delay function is in use.
So, after 1 second, element appears with appear animation, however there must be something else to hide it before animation starts.
.insta {
animation: appear 0.4s linear 1s;
}
#keyframes appear {
0% {
opacity: 0;
transform: translateX(30%);
}
100% {
opacity: 1;
transform: translateX(0%);
}
}
<p class=«insta»>Instagram</p>
Set opacity: 0. That hides your text. Using animation-fill-mode: forwards will let you have the properties added at the end of the animation.
You can solve it by adding an animation-fill-mode: both; to your CSS. That means that the browser will apply the animation's first frame until it starts, and its last frame after it has finished.
Since your animation starts with opacity: 0; and ends with opacity: 1;, no further modifications required.
You can also combine it into the animation property (just add a both keyword somewhere):
.insta {
animation: appear 0.4s linear 1s both;
}
#keyframes appear {
0% {
opacity: 0;
transform: translateX(30%);
}
100% {
opacity: 1;
transform: translateX(0%);
}
}
<p class="insta">Instagram</p>
Try on CodePen (at least until the Stack Snippets server is down...)

Set New Final State After CSS Animation Complete

I have an HTML Element with a CSS Animation applied to it that I want to snap to a different style after the animation finishes
.animatingElement {
-webkit-animation: myAnimation 0.25s ease-in-out forwards;
}
#-webkit-keyframes myAnimation {
from {
state1
}
to {
state2
}
}
.animatingElementFinal {
/*desired final/3rd state*/
}
Is there any way to, after the animation completes, have it apply a different style than the final state of the animation?
Thanks for any suggestions!

Single SVG with two animations, one delayed to play after the other via CSS only?

I am trying to apply two animations to a single SVG, which I have been able to achieve, however I cannot figure out how to delay one and not the other, as I'd like them to play one and then the other.
I'd like for the first animation (the SVG's path "draws" itself) to play and complete itself, and then for the second animation (The SVG's "fill" fades in) to play afterward.
I have seen people using animation delay in their css, however this would apply to both animations rather than a singular one, as it is a singular SVG target.
Here is the CodePen.
And the relevant CSS:
.preload-container__svg-wrap svg{
stroke: #EE2D24;
fill: #EE2D24;
fill-opacity: 0;
width: 100%;
height: auto;
stroke-dasharray: 1300;
stroke-dashoffset: 1300;
animation: draw/*,fill*/ 5s ease forwards;
}
#keyframes draw {
to {stroke-dashoffset: 0}
}
#keyframes fill {
to {fill-opacity: 1}
}
The JQ in the pen is not relevant, I am focusing on the CSS issue here.
You can add multiple keyframe selectors into a single animation. More details of this can be found in the link sourced at the end of the answer (and towards the end of the linked page).
With this, you can achieve what I believe you are looking for very easily, by replacing your animations with this (I've also included a revised Codepen with this change below):
#keyframes drawAndFill {
0% {
stroke-dashoffset: auto;
fill-opacity: 0;
}
20% {
fill-opacity: 0;
}
100% {
stroke-dashoffset: 0;
fill-opacity: 1;
}
}
Assuming you would like to have your fill animation start later, just increase the percentage in the second block, equally if you would like to start it earlier, decrease the percentage.
https://codepen.io/anon/pen/GEEOdv
Update:
In response to the comments on the question, and to queue the two stages of the animation seamlessly, the above can be altered as follows to achieve the desired behaviour:
#keyframes drawAndFill {
0% {
stroke-dashoffset: auto;
}
50% {
fill-opacity: 0;
}
80% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: 0;
fill-opacity: 1;
}
}
#keyframes: https://www.w3schools.com/cssref/css3_pr_animation-keyframes.asp
You do like this, where you comma separate the animations.
The 3s for the fill is a delay before it should start its animation
animation: draw 5s ease forwards,
fill 5s ease 3s forwards;
Updated codepen
Note, I also changed the script timeout to 6 sec.
Update
If to use 1 keyframe, and assuming you want 5 sec. on each animation, increase the duration to 10s and do like this.
Since the fill-opacity has a visual delay in its animation, I started it at 35% instead of at 50%.
Note, you need to repeat the stroke-dashoffset: 0; at 100%, or else it will disappear in the end of the animation
#keyframes drawfill {
0% {
stroke-dashoffset: 1300;
}
35% {
fill-opacity: 0;
}
50% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: 0;
fill-opacity: 1;
}
}
.preload-container__svg-wrap svg{
animation: drawfill 10s ease forwards;
}
Updated codepen 2