Here I have an example of CSS keyframe animation (You can see the whole thing on this Demo)
The code will every 1.4 seconds scale the img to 0.75 and go back to it's original (1) scale. That works fine.
Then I add a simple jQuery code to simulate the error:
setTimeout(function () {
$("img").css('visibility', 'hidden');
activate();
}, 3000);
function activate() {
setTimeout(function () {
$("img").css('visibility', 'visible');
}, 3000);
}
#-webkit-keyframes imagebulger {
to {
-webkit-transform: scale(.75);
transform: scale(.75);
}
}
#keyframes imagebulger {
to {
-webkit-transform: scale(.75);
transform: scale(.75);
}
}
img {
-webkit-animation: imagebulger 1.4s infinite alternate;
-moz-animation: imagebulger 1.4s infinite alternate;
-o-animation: imagebulger 1.4s infinite alternate;
animation: imagebulger 1.4s infinite alternate;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img src="http://placehold.it/200x200" />
This will hide the img element after 3 seconds and during 3 seconds. When the img element is back to to visible, the resizing effect will not be running anymore.
It happens to me in Chrome 41.0.2272 (Ubuntu). In Firefox it works as expected.
EDIT
Looks like is bug in with Chrome. I opened an issue. As a workaround, like suggested, either use display:none instead of vissibility:hidden or add a class after setting vissibility:visible
EDIT 2
There is an issue opened here: https://code.google.com/p/chromium/issues/detail?id=444852
It appears to be a bug. The W3 documentation suggests that visibility hidden have the following effect:
The generated box is invisible (fully transparent, nothing is drawn), but still affects layout. Furthermore, descendants of the element will be visible if they have 'visibility: visible'.
Hence it should still be being calculated, just not drawn. Obviously the browser will probably want to make savings and not calculate it where possible, which seems to be where the bug is arising when this calculation doesn't recommence when it should. You can get around it by toggling the display and wrapping your animating element in a div of the same size as the element in order to prevent the layout collapsing. Otherwise you could just reapply the animation CSS by toggling the class as Jeff states.
Please see the JS fiddle showing a hidden element still clearly being animated: JSFiddle. This leads me to think it's a bug. Otherwise the below is an example of it working toggling display.
setTimeout(function () {
$("img").hide();
activate();
}, 3000);
function activate() {
setTimeout(function () {
$("img").show();
}, 3000);
}
.image-wrap {
height: 200px;
width: 200px;
}
#-webkit-keyframes imagebulger {
to {
-webkit-transform: scale(.75);
transform: scale(.75);
}
}
#keyframes imagebulger {
to {
-webkit-transform: scale(.75);
transform: scale(.75);
}
}
img {
-webkit-animation: imagebulger 1.4s infinite alternate;
-moz-animation: imagebulger 1.4s infinite alternate;
-o-animation: imagebulger 1.4s infinite alternate;
animation: imagebulger 1.4s infinite alternate;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="image-wrap">
<img src="http://placehold.it/200x200" />
</div>
I've run into issues with animations stopping their execution before. The solution for me has always been to reapply the animation as a class whenever I want it to restart. I modified your fiddle with this solution:
http://jsfiddle.net/q234Lsx8/5/
I made the CSS rule apply to img.bulge, and then the jQuery code removes and adds the class bulge on hide and show.
Related
I want to make random "blinking" effect when hovering over a doodle. For this i store animation name blink into a variable when user hovers over doodle container. For some reason animation applies only to the first element of the grid. Is there a way to apply the animation to all elements?
CodePen: https://codepen.io/entityinarray/pen/mdbRPRz
<html>
<script src="https://unpkg.com/css-doodle#0.7.2/css-doodle.min.js"></script>
<css-doodle>
:doodle {
#grid: 4/128px;
--h: ;
}
:doodle(:hover) {--h: blink;}
background: #200040;
animation-delay: rand(500ms);
animation: #var(--h) 1s infinite;
#keyframes blink {
0% {
background: #200040;
}
100% {
background: #8000c0;
}
}
</css-doodle>
</html>
The issue is that the use of #var(--h) is generating code like var(--h)-x which is invalid and only the first item is having the good value var(--h).
Instead of doing this you can simply consider the animation state like below. Note that rand() should be used with # and placed after the animation definition:
<html>
<script src="https://unpkg.com/css-doodle#0.7.2/css-doodle.min.js"></script>
<css-doodle>
:doodle {
#grid: 4/128px;
}
:doodle(:hover) {--h:running;}
background: #200040;
animation: blink 1s infinite;
animation-play-state:var(--h,paused);
animation-delay: #rand(500ms);
#keyframes blink {
0% {
background: #200040;
}
100% {
background: #8000c0;
}
}
</css-doodle>
</html>
We are implementing a wishlist functionality on a site we are developing, and when the user clicks on an icon to add the current item to a wishlist, it fires an ajax request.
Now while that ajax request is doing it's business, we add a loading class to the icon, so it scales bigger and smaller slightly. My issue, is once the ajax request has finished loading, we then remove that class, but the animation abruptly stops rather than scaling back down to it's initial size.
How can we make the animation finish, rather than just suddenly stopping?
Below is my CSS:
/**
* Keyframes
*/
#keyframes breathe {
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
/**
* Icon
*/
.wishlist-icn {
transition: all .3s ease;
}
.wishlist-icn--isAdded {
fill: #4B3814;
}
.wishlist-icn--isLoading {
animation: breathe 2s ease-in-out 0s infinite normal both;
}
tl;dr: Try applying animation-fill-mode: forwards
The normal behavior after CSS animation is done is that it resets the styles to the initial state. The way I see the process here is that when you remove the --isLoading class the animation stops and reverts the styles to the original state. Only after then the transitions starts to work and has nothing to do since the styles already are reset. animation-fill-mode: forwards in .wishlist-icn would prevent the animation from resetting, thus the transition would be able to operate gradually. To be sure you can add transform: scale(1) to .wishlist-icn or to :not(.wishlist-icn--isLoading) so that the transition knew what to head for. Not that I have tested it in this particular case, but it's worth trying ;P
$('.start-animation').on('click', function() {
$('.wishlist-icn').addClass('wishlist-icn--isLoading');
});
$('.stop-animation').on('click', function() {
$(".wishlist-icn").bind("mozAnimationIteration animationiteration webkitAnimationIteration", function() {
$(this).removeClass("wishlist-icn--isLoading");
});
});
/**
* Keyframes
*/
#keyframes breathe {
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
/**
* Icon
*/
.wishlist-icn {
position: relative;
display: inline;
transition: all 2s ease;
font-size: 5em;
}
.wishlist-icn--isAdded {
fill: #4B3814;
}
.wishlist-icn--isLoading {
animation: breathe 2s ease-in-out 0s infinite normal both;
animation-fill-mode: forward;
}
.wishlist-icn--isLoaded {
transform: scale(1);
}
}
<link href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="wishlist-icn"> <i class="ion-heart"></i>
</div>
<button class="start-animation">start animation</button>
<button class="stop-animation">stop animation</button>
you would probably have to use javascript to listen for the animation iteration to end then remove the class.
$(".wishlist-icn").bind( "mozAnimationIteration animationiteration webkitAnimationIteration" , function() {
$(this).removeClass("wishlist-icn--isLoading");
});
I'm trying to apply an animation to a group within an SVG element. However, I'm finding that it isn't applied or is being overridden, as it's crossed out in Chrome Developer Tools. That said, I have no idea what could be causing it.
Animation code:
.ghost {
animation: float 3s ease infinite;
}
#keyframes float {
50% {
transform: translate(100px, 100px);
}
}
I'd post the HTML (the problem might be there) but it's incredibly verbose because of all the SVG paths. Link to a Codepen instead: ghost
Thank you so much! I have no idea what could be causing the issue at this point.
You're missing your vendor prefixes. Example:
.ghost {
-webkit-animation: float 3s ease infinite;
}
#-webkit-keyframes float {
50% {
-webkit-transform: translate(100px, 100px);
}
}
For an easy fix, try adding Prefix-free in CodePen's CSS editor. Here it is with Prefix-free enabled:
Codepen
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+
I'm making a simple landing page driven by CSS3. To make it look awesome there's an <a> plopping up:
#keyframes splash {
from {
opacity: 0;
transform: scale(0, 0);
}
50% {
opacity: 1;
transform: scale(1.1, 1.1);
}
to {
transform: scale(1, 1);
}
}
And to make it even more awesome I added a hover animation:
#keyframes hover {
from {
transform: scale(1, 1);
}
to {
transform: scale(1.1, 1.1);
}
}
But there comes the problem! I assigned the animations like this:
a {
/* Some basic styling here */
animation: splash 1s normal forwards ease-in-out;
}
a:hover {
animation: hover 1s infinite alternate ease-in-out;
}
Everything works just fine: The <a> splashes into the users face and has a nice vibration when he hovers it. Bit as soon as the user blurs the <a> the smooth stuff ends abruptly and the <a> repeats the splash-animation. (Which is logical to me, but I don't want it to)
Is there some way to solve this problem without some JavaScript Class Jiggery Pokery?
After hours of googling: No, it's not possible without JavaScript. The animation-iteration-count: 1; is internally saved in the animation shothand attribute, which gets resetted and overwritten on :hover. When we blur the <a> and release the :hover the old class reapplies and therefore again resets the animation attribute.
There sadly is no way to save a certain attribute states across element states.
You'll have to use JavaScript.
If I understand correctly that you want to play the animation on A only once you have to add
animation-iteration-count: 1
to the style for the a.
It can be done with a little bit of extra overhead.
Simply wrap your link in a div, and separate the animation.
the html ..
<div class="animateOnce">
<a class="animateOnHover">me!</a>
</div>
.. and the css ..
.animateOnce {
animation: splash 1s normal forwards ease-in-out;
}
.animateOnHover:hover {
animation: hover 1s infinite alternate ease-in-out;
}
I just got this working on Firefox and Chrome. You just add/remove the below class accordingly to your needs.
.animateOnce {
-webkit-animation: NAME-OF-YOUR-ANIMATION 0.5s normal forwards;
-moz-animation: NAME-OF-YOUR-ANIMATION 0.5s normal forwards;
-o-animation: NAME-OF-YOUR-ANIMATION 0.5s normal forwards;
}
Just use
animation: hover 1s ease-in-out forwards;
An easy solution to solve this problem is by just adding more seconds to the animation in a:hover and taking advantage of the transitions in #keyframes
a:hover {
animation: hover 200s infinite alternate ease-in-out;
}
Just make the progression of #keyframes go faster by using percentages.
#keyframes hover {
0% {
transform: scale(1, 1);
}
1% {
transform: scale(1.1, 1.1);
}
100% {
transform: scale(1.1, 1.1);
}
}
200 seconds or 300 seconds in the animation is more than enough to make sure the animation doesn't restart. A normal person won't last more than a few seconds hovering an image.
Impossible in CSS only, you need a javascript workaround. As already explained by some here, the animation-iteration-count property is reset on a :hover. The best is to do everything in javascript, but for reasons of ease of customization of the code you may want to keep the possibility of doing something in CSS.
So, in JS :
// adding a class to the html tag, during the animation time
const startPage = (() => {
const html = document.documentElement,
s = 'start'
html.classList.add(s)
window.addEventListener('load', function() {
setTimeout(() => {
html.classList.remove(s)
}, 1500) // the time must be at least equal to the duration of the CSS animation (personally I put a little more).
})
})()
And for the CSS:
/* the presence of the `.start` class conditions the animation */
.start .leaflet-marker-pane {
animation: animDrop 1s ease;
}
The following code without "iteration-count: 1" was resulting in all line items pulsing after entering, until the last item loaded, even though 'pulse was not being used.
<li class="animated slideInLeft delay-1s animation-iteration-count: 1"><i class="fa fa-credit-card" aria-hidden="true"></i> 1111</li>
<li class="animated slideInRight delay-1-5s animation-iteration-count: 1"><i class="fa fa-university" aria-hidden="true"></i> 222222</li>
<li class="animated lightSpeedIn delay-2s animation-iteration-count: 1"><i class="fa fa-industry" aria-hidden="true"></i> aaaaaa</li>
<li class="animated slideInLeft delay-2-5s animation-iteration-count: 1"><i class="fa fa-key" aria-hidden="true"></i> bbbbb</li>
<li class="animated slideInRight delay-3s animation-iteration-count: 1"><i class="fa fa-thumbs-up" aria-hidden="true"></i> ccccc</li>
So i just found a solution for that:
In the hover animation do this:
animation: hover 1s infinite alternate ease-in-out,splash 1;