Smoothly change CSS animation mid-animation - javascript

I'm trying to transition smoothly from one half-completed CSS animation to the next one and I can't find a way to do it without a slight stutter. I have an infinite animation at very high speed that should gently slow down to a stop on click. Right now, I'm always getting a slight hickup while switching animations, likely partially because I need to wait for the next requestAnimationFrame before starting the next animation. Are there other options? Here's approximately what I'm doing:
function onClick(){
// get current location of element
var content = $(".content");
var currentOffset = $(content[0]).css("top");
// set property to stop jumping back to the beginning of current animation
content.css({ top: currentOffset });
// stop the current animation
content.removeClass("anim-infinite");
// update content based on current location
// updateContent(currentOffset);
// setup ease-out animation
window.requestAnimationFrame(function() {
content.addClass("anim-ease-out");
content.css({ top: parseFloat(currentOffset) + 50 });
});
}
And here's the relevant CSS.
#keyframes "spin" {
from { top: 0 };
to { top: -200%; }
}
.anim-infinite{
animation: spin 1s linear infinite;
}
.anim-ease-out{
transition: all 0.25s ease-out;
}
The distances and timespans are reasonable to maintain constant speed between the two animations and I'm using the relevant browser prefixes.
I get the same stutter when I use a "linear" timing function for the second animation. I tried setting an animation-fill-mode:both, without success. It appears to only affect animations that complete.
The stutter gets worse when I try to update the content based on the location of the content - which is dependent on when the animation gets stopped.

While trying to work out a jsFiddle that demos the problem, I found the source of most of the stutter. Anything that happens between removing the anim-infinite class and in the requestAnimationFrame can have a big perf impact, especially if it modifies the DOM in the content and causes the content to reflow. That's obvious in retrospect but the minor DOM updates had a bigger impact than expected.
I still have a slight occasional stutter but it's "good enough" for now.
For reference, here's the fiddle.

Related

CSS transition speed change based on mouse position

I have a div element whose width is determined by the users mouse position inside a defined area. Upon entering this area the transition should be smooth, until the width has catched up to the current mouse position and smooth again when leaving the area and the width goes back to normal. In the time between the transition should be fast, so the width is responsive to the mouse position.
Html
<div class="widthChanging"></div>
<div class="HoverA"></div>
CSS
.widthChanging {
transition: width 300ms cubic-bezier(.45,.1,.14,.61);
}
.HoverA:hover + .widthChanging {
width: calc(0.001 / var(--mouse-x, 0) * 100vw);
transition: width 0s;
}
I get the mouse position using Javascript.
When leaving the "HoverA" area the width returns smoothly, because the transition speed defined in "widthChanging" takes over. Sadly I cant (I think) do the same for entering the "HoverA" area. My only idea would be waiting the transition time before assigning a class with the slower transition time, but this seems like a bad solution to me and would also require more Javascript.
I hope you have some more elegant ideas than me :)
I solved the problem by waiting and then assigning a class with the new transition speed using javascript. I dont think that this is the best way to do it.
Here the codepen https://codepen.io/Teiem/pen/EReJPx
html
<div class="HoverA" onmouseover="SpeedEnter2()" onclick="SpeedEnter2()"onmouseout="SpeedLeave2()"></div>
Javascript
function SpeedEnter2() {
goVar = setTimeout(function(){
document.getElementById("SpeedId2").classList.add('Fast');
}, 300);
Css
.Fast {
transition: width 0s !important;
}

Jquery animate - callback executing before animation is complete

What I'd like to accomplish is for the element to expand in height, and then apply a background image once the height change is complete. I've noticed that the background image in the callback applies before the height animation is complete resulting in laggy performance. Can anyone tell me why this is?
$(document).ready(function() {
$('#hero').animate({
height: ($(window).height() - $("#hero").offset().top - 50)
}, 100, function() {
$('#hero').css('background-image', 'url(./img/hero.jpg)');
});
});
You have a transition: all 1s ease on #hero, which will cause the animation to play erratically since you are trying to use css3 animations to animate the jQuery animation (and throw all kinds of timing problems).
What I suspect is happening is that you are getting notified that the jQuery animation completed, but the css3 animation doesn't complete for an additional 1s, which is why you are seeing what you are.

Change Height of Navbar on Scroll Jquery with Bootstrap3

Hello i have a problem with my navbar.
I want to animate it on scroll and change his height. When i scroll a bit down it should animate smaller and when im at the top of the page it should aniamte bigger. The standard height is 100px. The problem is when im at the top of the page it takes a delay, which i need to wait, until it animates. They delays gets longer if i scroll first to the bottom of the page and then back to the top. The has a height of 11000px. This is my code for it:
$(document).on("scroll",function(){
if($(document).scrollTop()>500)
{
$( ".navbar" ).animate({height: 50} ,{duration:100});
}
else if($(document).scrollTop()==0)
{
alert("dhsihsp");
$( ".navbar" ).animate({height: 100} ,{duration:100});
}
});
Maybe u can help me. I use Google Chrome and Bootstrap 3.
The problem you are having is that the "scroll" fires every single time the scrollbar moves. So every single time the scrollbar moves a pixel, it will do the IF checks. That's why you delay your animation for so long. The queue of things to run stacks up immensely if you move the scrollbar too much.
DEMO
The scroll event seems to fire a lot when you scroll so all the events get queued. So the event that actually changes you header seems to take a long time to appear.
I added a css transition on the height of a .navbar. for making this happen almost instantly. Are the events not still there? True, but changing css is a lot less demanding then adding animations (with a duration of 100ms). The transition does have a duration but it does not have to finish so an other event can come in at any time.
CSS
.navbar {
transition: height 0.1s;
}
Jquery
$(window).scroll(function () {
var scrollh = $(this).scrollTop();
if (scrollh == 0) {
$(".navbar").css({
'height':'100px',
});
} else {
$(".navbar").css({
'height':'50px',
});
}
});

CSS3 Infinite Loop Transition

I am trying to animate a clock but I am having a lot of trouble trying to get the numbers to loop infinitely. I am moving the numbers by incrementing translateY() but when the 59th second is reached all of the other numbers fly past as it transitions back to 0. I would like a smooth transition over to 0 to continue to 1 and so on without showing anything in between.
I've tried transition: none, animation-play-state: paused, transitionEnd event listeners and even delays but I wasn't able to use any to achieve what I'm looking for.
Link to code on JSFiddle http://jsfiddle.net/bwr4yz0s/
The code below outlines one failed attempt:
/*Seconds*/
if (sec === 0){
/*Move to fake 0*/
$("#secn").css({transform: 'translateY(-100px))'});
/*Turn off animation*/
//$("#secn").css({transition: 'none'});
/*Move back to actual 0*/
//$("#secn").css({transform: 'translateY(0px)'});
/*Turn animations back on*/
//$("#secn").css({transition: 'all 0.8s'});
}
else{
vert = sec*20 + 'px';
$("#secn").css({transform: 'translateY(-'+vert+') translateZ(0)'});
}
Any help would be greatly appreciated. Thanks.

CSS animation won't trigger on addClass in jQuery

I have a very strange issue. I'm loading articles from JSON in jQuery and as they load, I'd like to add a class of 'animate' to each dynamic element.
$.each(jsonArticles, function (i, article) {
var $articleHTML = $(
'<article class="article">' +
'<img src="' + jsonObject.imagePath + article.reviewImage + '" alt="">' +
'<h1>' + article.reviewTitle + '</h1>' +
'<p>' + article.reviewSummary + '</p>' +
'</article>');
$articles
.append($articleHTML)
.find("article")
.addClass("animate");
});
All of this works great and checking in Firebug reveals that the class is successfully added to each article tag.
However, when trying to use a CSS transition on the article for the class that's added, it does not animate, but instead skips straight to the final style (opacity: 1).
.article {
opacity: 0;
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
.article.animate {
opacity: 1;
}
The animation doesn't happen, but the class is added and the article is successfully set to opacity: 1. It shows up instantly.
Anyone have any ideas about this? I cannot figure this one out at all.
On another point, which is rather interesting...if I change the .animate class to have a :hover, then the articles won't show until I hover and the animation does work. Why it would work for hover and not when it's simply added immediately, seems strange to me.
.article.animate:hover {
opacity: 1;
}
I'd appreciate any input.
Thanks,
Mikey.
Live Demo: http://jsfiddle.net/Pz5CD/
Notice how the articles just pop in at 100% opacity. No animation is seen.
Update:
It turns out the OP wants to fade in each element sequentially, which is beyond the scope of the original question. I'll leave my answer here as an answer to the original question.
CSS animation won't trigger on addClass in jQuery
The issue is that your new html is added to the page and the animate class is added before the css for that html has been applied. The browser will skip ahead like that for the sake of efficiency. For example, if you added a class, then removed it, and repeated that process a hundred times, there wouldn't be a visual difference. It would have just skipped to the result. For this reason, you have to force a redraw on the element so that all previous styles have applied before adding the class. I wrote a function to handle this that should work in every circumstance on every browser, though there's no way to guarantee the behavior of a reDraw. It probably will always work and it's nice to have!
Live demo here (click). You can tell the reDraw is making the difference by commenting it out and just leaving the addClass().
$('body').append($articleHTML);
$a = $('body').find("article");
reDraw($a).then(function() {
$a.addClass("animate");
});
function reDraw($element) {
var deferred = new $.Deferred();
setTimeout(function() {
var h = $element[0].offsetHeight;
var s = $element[0].getComputedStyle;
deferred.resolve();
},0);
return deferred.promise();
}
The best way to force a redraw is to either access the offsetHeight or getComputedStyle of an element. However, there have been cases where those have failed for force a redraw on certain mobile devices. To add some extra encouragement for a redraw, I added a setTimeout as well. Even a time of 0 on the timeout will work, but it throws off the call stack, so I use a promise to ensure the next operation (adding your class) will happen after the redraw. That just means you'll use the syntax I demonstrated above to add the class - redraw($element).then(function() { //your code
For fun, I made a little demo of flipping classes with and without reDraw. http://jsbin.com/EjArIrik/1/edit
You need to add the class to the element after it is rendered to the dom, a set timout might work
setTimeout(function(){
$articleHTML.addClass("animate");
}, i * 500 );
http://jsfiddle.net/Pz5CD/1/

Categories