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;
}
Related
I've got a project where i've overlaid two imaged and have the top image fade out using a toggleFade function, when the user clicks a toggler (checkbox). it works well, except that to get the images to function correctly the bottom image is set to position:absolute. Of course, when the toggleFade() happens, the absolute positioning means all the lower divs float up.
$(document).ready(function(){
$('.lights').on('click', function (){
$('.day').fadeToggle(3000);
setTimeout(function() {$('.night').css('position: absolute');}, 3000);
});
});
is there any way to prevent this from happening? i've tried setTimeout for the lower div, but that didn't work.
here's the jsFiddle of my project:
https://jsfiddle.net/jsieb81/oue2fnr0/
You can add a class on click event and manage opacity in css with a transition. Like this :
(You don't need jQuery)
document.querySelector('.lights').addEventListener('click',function(){
document.querySelector('.day').classList.add('hide');
});
.hide {
opacity: 0;
-webkit-transition: opacity 3s;
transition: opacity 3s;
}
see this fiddle https://jsfiddle.net/oue2fnr0/9/
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);
});
I have a div that i need to slide out from the right on page load and stay locked on the right side of the window. It would be great if it could delay for about 5 seconds and then slide out
<script type="text/javascript">
$("#slide").animate({width: 'toggle'}, 500);
</script>
html
<div id="slide"><img src="live.jpg"></div>
What am i missing?
You could try a couple of options.
If you want to use .animate(), you'll want to change the position from it's initial state. So in your CSS, you'll want to set the initial state of your div to something that is off of the page, like
#slide {
position: absolute;
right: 999em;
}
With this set, adding .animate() will animate the property you specify toward the value you specify, so specify where you want your div to end up after the animation completes.
$("#slide").delay(5000).animate({right: 0}, 500);
An alternative would be to use the jQuery addClass() method. If you do this, you'll want to define two classes in your stylesheet. The first class will be your div's initial state, again, positioned offscreen; the second class will be where your div ends up. For the animation, just use a CSS animation like this:
.div-start {
position: absolute;
right: 999em;
transition: right 4s ease-in;
-webkit-transition: right 4s ease-in;
-moz-transition: right 4s ease-in;
}
.div-animate {
right: 0;
}
Then in your jQuery, do this:
$('yourDiv').addClass("div-animate");
Edit:
You could also use setTimeout on this to have it fire after 5 seconds, like so:
setTimeout(function() {
$('yourDiv').addClass("div-animate");
}, 5000);
$(document).ready(function(){
$('#slide').delay(5000).animate({"width":"20%"}, 0500);
});
This will delay your animation by 5 seconds from when the pages has fully loaded, and then slide the content out to a width of 20%. Here is an example: http://jsfiddle.net/EdjjH/
$(document).ready(function(){
$("#slide").delay(5000);
$("#slide").animate({
width: '500px',
height: '500px',
});
});
Here is the fiddle: jsfiddle
This fiddle has a 5 second delay so be patient when looking at it.
I have a couple of classes: hide is display: none, and transparent is opacity: 0. The element pr_container has -webkit-transition: opacity 1s. The following JQuery-based code makes an element appear in an animated fasion:
pr_container.removeClass("hide");
setTimeout(function() { pr_container.removeClass("transparent"); }, 0);
However, when I remove setTimeout and instead just remove the second class, there is no animation. Why?
Edit: I'm using the latest Chrome, I haven't checked other browsers yet.
Edit: I tried putting both calls in the same setTimeout callback - no animation. So it's clearly about separation.
Edit: here's the jsFiddle: http://jsfiddle.net/WfAVj/
You can't make a transition if you are changing display property at the same time. So in order to make it work you have to hide your element some other way. For example:
.hide {
height: 0;
width: 0;
/* overflow: hidden; padding: 0; border: none; */
}
http://jsfiddle.net/dfsq/WfAVj/1/
There's no reasonable "curve" to transit from one display status to another, so in current implementation of browsers, any transition that somehow involves display will end up with no transition at all.
With this code:
pr_container.removeClass("hide");
pr_container.removeClass("transparent");
You can imagine the two statements execute in a single "blocking" queue, so browsers practically renders the element from class="hide transparent" to class="", and as stated above, the hide class practically invalidates any existing transition.
By using
pr_container.removeClass("hide");
setTimeout(function() { pr_container.removeClass("transparent"); }, 0);
You told browsers to remove the "transparent" class "as soon as possible, but no in the same queue", so browser first removes "hide", and then moves on. The removal of "transparent" happens when the browser think it has resource to spare, thus the transition does not get invalidated.
only the "transperent" class produce animation .. "hide" is instant. So start the animation and if needed "hide" after 1 second:
test.addClass("transparent");
//hide after 1 sec, when the animation is done
setTimeout(function() {test.addClass("hide"); }, 1000); //1000ms = 1sec
http://jsfiddle.net/WfAVj/4/
By using suggestions in the linked question, I made a version that I'm satisfied with:
.test {
-webkit-transition: visibility 1s, opacity 1s;
}
.hide {
visibility: hidden;
}
.transparent {
opacity: 0;
}
http://jsfiddle.net/xKgjS/
Edit: now the two classes can even be combined to one!
Thanks to everyone!
I am developing a voice recorder function with PhoneGap on Android system. I'm testing on the Transformer Prime. I'm using jQuery and jQuery UI on top of PhoneGap's JavaScript.
My question is this: When the user presses the Record button, how can I make that button flash to indicate that the app is recording?
Of course, the effect I want to create is to let the user know it's still recording. Maybe I can set a timer to give the current recording duration, or something to that effect.
Unfortunately, I don't know one of these effects. Could anyone give me a suggestion with the source code so that I can take it as a reference?
You can use CSS for that: http://jsfiddle.net/fszhJ/. Have a keyframe that sets the element invisible for the first half of the time, and visible for the other half. (You may want add other prefixes than only -webkit-.)
#-webkit-keyframes keyframe {
from {
visibility: hidden;
}
50% {
visibility: hidden;
}
to {
visibility: visible;
}
}
div {
width: 200px;
height: 200px;
background-color: black;
-webkit-animation: keyframe 1s infinite;
}
If you use jQuery you can put a glow png above your record button (initially with opacity 0) and inside your interval animation you can do this:
isVisible = false;
var interval = setInterval(function(){
opacityValue = isVisible ? 0 : 1;
glow.css( 'opacity', opacityValue );
isVisible = !isVisible;
}, 500);
And then, when you want stop it:
clearInterval( interval );
if you want that your glow appears smoothly you can use css transitions.