JQuery unable to animate div after "forwards" css animation - javascript

See example
http://jsfiddle.net/nxsv5dgw/
Div appears on stage, a "forwards" animation occurs on it, JQuery can apparently no longer "animate" the properties that were animated.
In the example, a css animation plays on the width of a box. OnClick, a JQuery animation tries to shrink the width and height of the box, but only the height is changed. Here's the code.
$(".a").click(function(e) {
$(this).animate({
width: "-=100px", // doesn't work after CSS animation
height: "-=100px",
}, 400);
})
.a {
background:red;
position:absolute;
height:500px;
width:600px;
animation: anim 0.4s forwards 1s;
}
#keyframes anim {
0% {width:600px;}
100% {width:500px;}
}
Is there any way to circumvent this? I'd rather avoid doing all animations in JQuery if possible.

Only tested for Firefox, but working - I've adjusted your Fiddle adding
$(this).css({
"width": $(this).width(),
"animation": "none"
});
to the click-function. This sets the width to the actual width and overrides the animation, but I think there could be a better solution as it looks like a hack.
Update - also working for Safari and IE.

Actually you can set the width to 500px after the animation is complete so that DOM element knows that DIV has width 500px and remove the css animation from Element.
$(".a").on("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function () {
$(this).width(500);
$(this).css({
"animation": "none"
});
$(".a").click(function (e) {
$(this).animate({
width: "-=100px",
height: "-=100px",
}, 400);
});
});
Basic Idea: To avoid conflicting between CSS animation and jQuery animation one should call the jQuery animation only when CSS animation is completed by using animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd event and by undoing the rules overriden by CSS animation and removing animation from the element.
Working Fiddle

As you said, this happens by using forward in animation.
From the CSS Animations Working Draft
CSS Animations affect computed property values. During the execution of an animation, the computed value for a property is controlled by the animation. This overrides the value specified in the normal styling system. Animations override all normal rules, but are overriden by !important rules.
and Animation Duration
[…] and an animation that fills forwards will retain the value specified at the 100% keyframe, even if the animation was instantaneous. Also, animation events are still fired.
So, it cannot be overriden by default, except for !important rules. But this cannot be done using jQuery.animate()
I'd rather avoid doing all animations in JQuery if possible.
I guess that you can't.
You could use a jQuery solution:
$(document).ready(function() {
$('.a').delay(1000).animate({
width: "-=100px"
}, 400);
});
$(".a").click(function(e) {
$(this).animate({
width: "-=100px", // doesn't work after CSS animation
height: "-=100px",
}, 400);
})
.a {
background:red;
position:absolute;
height:500px;
width:600px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="a"></div>

I'm not sure if this is an option, as the effect is slightly different then what you are aiming for, but I just wanted to put it out there as an alternative.
The problem is clearly caused by the fact that (http://w3.org/TR/css3-animations/#animations)
Animations override all normal rules, but are overriden by !important
rules
Therefore you could try to animate the scale transform in stead. This way you are not altering the properties that have been set trough the animation. jQuery does not support it out of the box, but this plugin adds the functionality: https://github.com/rstacruz/jquery.transit.
This also has the huge added advantage of being much better for performance then animating your width an height, as very well explained here: http://www.html5rocks.com/en/tutorials/speed/high-performance-animations/
The drawback of this method is that the contents of your div is scaled down as well, which may not be the desired effect.
I've set up a demo to demonstrate: http://jsfiddle.net/nxsv5dgw/13/
And the code:
$(".a").click(function (e) {
$(this).transition({
scale: '-=0.1'
}, 4000);
});

Related

CSS transition doesn't animate when immediately changing size via JavaScript

When setting the height of an element to 0 in JavaScript and then immediately changing it to a specific value, the CSS transition for the element doesn't work.
However, by placing the code to increase the height inside a setTimeout(), even with a delay of 0, the transition works, as you can see in the following snippet:
// Doesn't work:
document.getElementById("one").setAttribute("style", "height: 0px");
document.getElementById("one").setAttribute("style", "height: 200px");
// Works:
document.getElementById("two").setAttribute("style", "height: 0px");
setTimeout(function() {
document.getElementById("two").setAttribute("style", "height: 200px");
}, 0);
div {
display: inline-block;
width: 200px;
background-color: black;
transition: height 1s;
}
#two {
background-color: blue;
}
<div id="one">
</div>
<div id="two">
</div>
This behavior is consistent across all major browsers. The problem with this is, that sometimes, there seems to be some kind of lag, which makes the workaround not animate as well. So this doesn't seem to be a clean solution.
What causes the transition to cancel and how can I get around this cleanly?
Most likely browsers optimize transitions and will merge changes which take less than 16ms (which would get you a refresh rate of about 60 frames per second)
So the solution is to simply wrap style changes nested RAF calls (tell the browser to animate when it's ready rather than after an arbitrary timeout)
window.requestAnimationFrame(function(){
document.getElementById("two").setAttribute("style", "height: 0px");
window.requestAnimationFrame(function(){
document.getElementById("two").setAttribute("style", "height: 200px");
});
});
reference: https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
Try adding it to the window.onload event
window.addEventListener("load", init, false);
function init() {
document.getElementById("one").style.height = "200px";
}
also you gonna have to set #one height in CSS to 0
#one {
height:0px;
}
//cache the object in a variable
var one = document.getElementById("one");
//do you really need to overwrite all stlyles defined on this object?
//if not, prefer style-property over attribute
//set animation-start
one.style.height = 0;
//force the browser to apply styles
getComputedStyles(one);
//apply the animation-target
one.style.height = "200px";
You don't need any timeout, but beware, this forces the browser into a render-cycle (one of the most expensive things one could do in JS).
So don't use this in a loop (or on multiple nodes, one after the other).
But instead do two loops, first set all the start-values, force the browser into the render-cycle, and then apply all the target-values.

Sliding from side with jquery (without Jquery UI)

I want to make specialist_pagecontent to appear (slide) from the left, like blindleftin from here but I just can't make it work with this. Actually, the plan is, instead of hide() ideal would be blindLeftOut('fast');hide(), and instead of show() I need show();blindLeftOut('slow'), but as I said, I just can't make blindLeftOut and blindLeftIn work for me.
I think jQuery's animate function might be of use to you.
What you'd need to do is either have a hidden div positioned out of the window added to your HTML (or maybe add it dynamically using jquery on document.ready event, if you prefer) and the use the above mentioned animate function to slide it in and out and bind it to the menu item's click function.
Working Fiddle
Here is a working JSFiddle for you
Give the elements you want to animate in and out a viweport. A layer through which you look to see the elements within. Then set this viewport's overflow property to hidden and give it a specific width/height.
This way you can animate the elements within the viewport so they appear to slide in/out.
Here are the changes I'd make to your JS:
//notice the use of the "active" class to save state
$('.specialist_pagecontent').eq(0).addClass("active").animate({ left : 0 }, 500);
$('.specialist').click(function() {
//stop() is used to stop the current animation, so animations don't queue up if many buttons are clicked rapidly
$('.specialist_pagecontent').filter(".active").removeClass("active").stop(true).animate({ left : '-100%' }, 500);
$('.selected-specialist').removeClass('selected-specialist');
$(this).addClass('selected-specialist');
$('.specialist_pagecontent').eq($(this).index('.specialist')).addClass("active").stop(true).animate({ left : 0 }, 500);
});
And here are my suggested edits to the CSS:
.specialist_pagecontent {
position:absolute;
top:0;
left:-100%;
}
#specialist_lists {
float:left;
border: 1px solid #000000;
position:relative;
width:200px;
height:200px;
overflow:hidden;
}
Here is a demo: http://jsfiddle.net/Jwkw6/1/
This absolutely positions the elements that are to be animated, which is very useful since it removes the elements from the regular flow of the document (meaning it won't trigger whole page redraws when it animates). This also creates the viewport I mentioned, creating a window into which we look to see the animations.

CSS Animation pausing when element is hidden

I'm working on adding click/touch animations to buttons on a project I'm working on, and I've come across a frustrating problem concerning animating buttons which show and hide elements.
The project is a single page mobile web app with a handful of buttons on it. I'm using jQuery to set a css keyframe animation on a button when it's pressed. The button hides the current page, and shows a new one. The problem is, when I click the button, the page changes before the animation is complete, and the animation is paused whilst the container is hidden. When the container is re-shown, the animation continues from where it was hidden, then the webkitAnimationEnd event triggers.
The containers are shown and hidden with:
display: none;
I can't change this to:
visibility: hidden;
because the container will still take up space. Are there any simple ways I can force the animation to be removed when the element becomes invisible, or force the animation to continue when the container is hidden?
Edit: For clarification, this is the keyframe animation I'm applying in the javscript:
#-webkit-keyframes shrink
{
0%
{
-webkit-transform: matrix(1, 0, 0, 1, 0, 0);
}
50%
{
-webkit-transform: matrix(0.95, 0, 0, 0.95, 0, 0);
}
100%
{
-webkit-transform: matrix(1, 0, 0, 1, 0, 0);
}
}
And this is the javascript I've got to apply the animation to the element:
$('body').on(_context.settings.platformInfo.device.touch ? 'touchstart' : 'mousedown', '.shrink', function ()
{
var $item = $(this);
$item.one('webkitAnimationEnd', function ()
{
$item.css({ '-webkit-animation': 'none' });
}).css({ '-webkit-animation': 'shrink 250ms forwards' });
});
You could put the CSS definition of the animation in a separate class and add or remove this extra class based on visibiity:
#SomeDiv{ .... }
.MyAnimation{ .... }
$('#SomeDiv').addClass('MyAnimation').show();
$('#SomeDiv').hide().removeClass('MyAnimation');
You could try setting visibility: hidden; but also absolutely position the element off-screen, e.g position: absolute; left: -500px; /* Or whatever it takes */. In fact, you might not even need to set the visibility. It feels a bit hacky though.
I guess your problem could be solved if the animation for hiding the current page just waits until the button's animation is complete. So you'd have to trigger the page animation in the "on complete" callback of the button's animation:
$("#button").click(function(){
$(this).animate({
//animation parameters
}, 1000, function() {
//button animation complete, change pages
});
});
Test it here: http://jsfiddle.net/Y5HSU/
I've found a solution which will work for this particular problem, although I'm not massively fond of it. Adding a setTimeout into the mix means that even when the container is hidden, the animation will be removed after 250ms (in this case).
$('body').on(_context.settings.platformInfo.device.touch ? 'touchstart' : 'mousedown', '.shrink', function ()
{
var $item = $(this);
setTimeout(function ()
{
$item.css({ '-webkit-animation': 'none' });
}, 250);
$item.css({ '-webkit-animation': 'shrink 250ms forwards' });
});
The main problem with this is if the browser is particularly slow at executing the animation, and the timeout fires too soon cutting the animation off.
In CSS :
.hidden .myAnimation {
/* Will reset and prevent your animation
from running when the element is hidden. */
animation: none;
}

Can I animate the opacity of a CSS background-image?

CSS/Javascript is not my strong point so I would like to ask if is possible to change the background-image opacity to, let's say, 0.5.
I have a div with
background-image: url(images/nacho312.png);
background-position: -50px 0px;
background-repeat: no-repeat no-repeat;
but when I load a certain view it does not look very good, so I want to have a "half-disolve" effect when that view is shown. Is it possible?
Thanks
Here is a start.
var element = document.getElementById('hello'),
targetOpacity = 0.5,
currentOpacity,
interval = false,
interval = setInterval(function() {
currentOpacity = element.getComputedStyle('opacity');
if (currentOpacity > targetOpacity) {
currentOpacity -= 0.1;
element.style.opacity = currentOpacity;
} else {
clearInterval(interval);
}
}, 100);
See it on jsFiddle.
Run this on window.onload = function() { } or research cross browser on DOM ready events.
Of course, it is much easier with a library like jQuery.
$(function() {
$('hello').fadeTo('slow', 0.5);
});
This relies on your container's children inheriting the opacity. To do it without affecting them is a bit of a pain, as you can't reset children's opacity via opacity: 1.
If you want to animate smoothly and without doing too much extra work - this is a good task for jQuery (or another, similar library).
With jQuery you could do:
$('#id_of_div').fadeTo('fast', 0.5);
To get a fast animated fade effect on the relevant DIV.
Update: if you want to actually fade the background image, but not any foreground contents of the DIV, this is a lot harder. I'd recommend using one container DIV with position:relative and two inner DIVs with position:absolute; . The first of the inner DIVs can have the background image and a lower z-index than the second of the DIVs, and the second DIV would contain any text, etc. to show in foreground. When needed you can call $('#id_of_first_div').fadeTo('fast', 0.5); to fade just the DIV containing the background image.
By the way, the literal answer to your question is "No, you cannot animate the opacity of a CSS background image" - you can only (currently) animate the opacity of a DOM element, not its attributes, thus the need for the above hack.
Other Update: if you want to avoid using any third-party library, you can handle the fade of the background DIV using approach in Alex's answer.
background-image: url(images/nacho312.png);
background-position: -50px 0px;
background-repeat: no-repeat no-repeat;
opacity:0.5; //for firefox and chrome
filter:alpha(opacity=50); //for IE

Animate opacity doesn't work properly on IE

I'm trying to use animate() to change the height and opacity of a div. The div has an image background in CSS. It works fine on Firefox and Safari, but when I test it in IE the background is being removed. This is my code:
if (jQuery.support.opacity) {
jQuery('#list_box').animate({opacity: '1',height: '300px',top: newTop},{duration: 300});
} else {
jQuery('#list_box').animate({filter: 'alpha(opacity=100)',height: '300px',top: newTop},{duration: 300});
}
How can I fix this problem?
I was under the impression that jQuery did the whole opacity support thing for you.
Does this work for all browsers?
$('#list_box').animate({opacity: '1',height: '300px',top: newTop},{duration: 300});
You do not need to write a special handler for IE, jQuery does it all for you behind the scenes:
jQuery('#list_box').animate({opacity: '1',height: '300px',top: newTop}, 300);
HOWEVER: If you have a 24-bit transparent PNG as your background image that is disappearing, you need to be aware that you cannot combine filter: alpha (which jQuery correctly uses behind the scenes in IE) with a 24-bit transparent PNG in IE7 or IE8. I believe the only way around it is to set a background color (other than transparent) on the object on which you are using filter: alpha
How to test: Simply set a background color on #list_box to a solid color by adding something like this to your CSS after your background-image declaration:
#list_box { background-color: red }
If the background image remains, and your #list_box animates correctly (except for the hideous background) you know what the problem is and will have to find another way to accomplish what you want.
I've been having the same problem. I stumbled into the answer, when I set the opacity to 40%:
$('#list_box').stop().animate({opacity: '.4'},"slow");
I noticed that made the opacity jump to 100%, then animate down to 40%. Eureka.
So, now I explicitly set the opacity to zero before the animation:
$('#list_box').css({opacity:0}).stop().animate({opacity: '1'},"slow");
That animates smoothly, except the text still looks horrible in IE.
To clean up the text, I removed the opacity from the css in IE after the animation. This seems to clear up the text quite a bit in IE6 and IE8.
$('#list_box').css({opacity:0}).stop().animate({opacity: '1'},"slow",function(){
//remove the opacity in IE
jQuery.each(jQuery.browser, function(i) {
if($.browser.msie){
$('#list_box').css({opacity:''});
}
});
});
I'm testing it on a Mac in Parallels, in IE6 and IE8. Everything seems to work fine on the Mac side.
Very (very) late with the answer, but as this is at the top of Google when I looked for help with a jquery v animate issue in IE8 I thought i'd post it here.
My problem was connected to the hasLayout bug in IE, and adding "display: inline-block" to the element to be faded fixed the problem.
I had the same sort of issue with this:
$('#nav li').hover(function() {
$(this).stop().animate({opacity: '0.4'}, 'slow');
},
function() {
$(this).stop().animate({opacity: '1'}, 'slow');
});
I simply added float:left; to the #nav li css and it fixed the issue.
In jQuery, once the div is set to have either opacity:0 (in Standards Compliant Browsers) or filter:alpha(opacity=0) in IE, you can just use $('#div').animate({opacity:1},100); Since jQuery supports cross-browser support, if you end up animating the filter via IE, then chances are jQuery is trying to support IE and the conflict comes when jQuery fires the opacity change x2.
I hope this helps. I have had the same issue, plus odd issues with IE not being able to handle fading on a div stack with multiple items in it.
I noticed the problem was caused by position:relative of the container. If "switching" to absolute opacity animation will work.
I´ve had the same problem with the IE 7,
the problem was a trailing comma after the opacity property
jQuery(this).animate({opacity:1.00,},800);
It has to be:
jQuery(this).animate({opacity:1.00},800);
I found a solution that worked for me: position:inline-block;
This works for fading text opacity, I haven't tried it with a CSS background image. Maybe it helps anyway.
I just wanted to report a small bug with fadeTo method in Internet Explorer 8. It won't work if your element as "display" set to "inline". I found that you need to put it to "inline-block" and then it works perfectly. There is nothing about this on the web and it's not the first time I have this problem.
Don't know if it's the right way to report this issue, but i'm sure someone will read this post :)
found at http://www.devcomments.com/IE-8-fadeTo-problem-with-inline-elements-to65024.htm
I solved it with adding an opaque background to the animated element:
CSS:
.overlay {
position: absolute;
top: 0;
left: 0;
width: 195px;
height: 274px;
cursor: pointer;
background: #fff url('../images/common/image_hover.png') 0 0 no-repeat; /* the solution */
opacity: 0;
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; /* IE8 */
filter: alpha(opacity=0); /* IE6-7 */
zoom: 1;
}
JS:
$('.overlay').hover(
function(){
$(this).animate({'opacity': 0.7}, 300);
},
function(){
$(this).animate({'opacity': 0}, 250);
}
);
Works for IE7-8
Hope this will help someone ;)
You can use fadeTo to accomplish what you want to do:
$('#list_box').fadeTo("slow", 0.33);
fadeIn and fadeOut do transitions from 0 to 100%, but the above will allow you to fade to an arbitrary opacity.
(http://docs.jquery.com/Effects/fadeTo#speedopacitycallback)
Same problem with IE8. Adding "display: inline-block" to .hover2 in fixed the problem.
$(function() {
$(".hover1").css("opacity","1.0"); // Default set opacity to 1.0
// On Mouse over
$(".hover1").hover(
function () {
// SET OPACITY TO 15%
$("span.hover2").stop().animate({opacity: 0.15}, 1200);
},
// ON MOUSE OUT
function () {
// SET OPACITY BACK TO 100%
$("span.hover2").stop().animate({opacity: 1.0}, 1200);
}
);
}
);
Ok this might help a little bit, I found a solution in this site about the exact problem http://blog.bmn.name/2008/03/jquery-fadeinfadeout-ie-cleartype-glitch/
in conclusion, the general problem is the opacity filter on IE, in your specific case there is not much you can do, thought
but in case you fade in and out, the prevent the problem with a png background image you just have to remove the filter attribute the jQuery function added whe the fx ends. Just use a callback function, something like that would do it:
$('#node').fadeOut('slow', function() {<br/>
this.style.removeAttribute('filter');<br/>
});
in case you selectors returns more than one, use the each function, something like this:
$('.nodes').fadeIn('fast',
function() {
$(this).each (
function(idx,el) {
el.style.removeAttribute('filter');
}
);
}
);
Do you use some pngfix script ? that may be the culprit.

Categories

Resources