I have a sequence of images fading out and then back in which appears to work well for about 2 loops. Unfortunately after that it goes crazy and the sequences appears to go completely random.
I have done a fiddle so you can see what's going on https://jsfiddle.net/MichaelCaley/Lmm3kp4m/
var imgshow = function(){
$(".img1").delay(2000).animate({
opacity: 0
}, 3000, function(){
$(".img2").delay(2000).animate({
opacity: 0
}, 3000, function(){
$(".img3").delay(2000).animate({
opacity: 0
}, 3000, function(){
$(".img1").delay(2000).animate({
opacity: 1
}, 3000, function(){
$(".img2, .img3").animate({
"opacity": "1"
}, 0, function(){
imgshow();
});
});
});
});
});
}
imgshow();
After the first run through I've done a sort of reset step which is supposed to be the only time 2 classes are changing at the same time, but when I watch the loop go through I start to see that multiple divs begin fading throughout the process which I just cannot understand.
Thank you for your help
It's very easy to get confused or lost in callback hell, especially with jQuery animations and especially especially when using delay. May I suggest something a bit more clean, in my opinion at least.
// Get all images that need to be looped.
var images = $('.some-class-all-images-share');
// Create an array to store images that have already been shown.
var cycled = $([]);
// Start an interval, which calls the same function every 5 seconds.
setInterval(function(){
// Get the first image from the images array.
var image = images.unshift();
// If the images array is empty, reset everything.
if (images.length === 0) {
images = cycled;
cycled = $([]);
images.removeClass('transparent');
}
// Add a the class 'transparent' to the image.
image.addClass('transparent');
// Add the image to the cycled images array.
cycled.add(image);
}, 5000);
In the CSS:
.some-class-all-images-share {
/* This means that whenever the opacity of the element changes, it will do so gradually, over three seconds, instead of instantly.*/
transition: opacity 3s;
}
.transparent {
opacity: 0;
}
Now every image will get the transparent class applied to it, one by one. This will trigger a three second long animation, and when all images except for the last one have been animated, it will restart.
This way you don't need another callback for each image and it should be, overall much more manageable.
Related
I have a scroller that animates scrolling using prev/next buttons. The duration of the animation is calculated based on the distance moved, so the duration is variable.
$c.animate({ scrollLeft: scrollLeft },
duration, 'easeInOutExpo', function () {
if (showPrev === 0) $s.attr("data-prev", showPrev);
if (showNext === 0) $s.attr("data-next", showNext);
});
After the animation is done, I want to refresh the user interface (the buttons), which you can see is done by updating two data attributes.
To date, I've tried two different options:
1. Update the data attributes before the animation
I use a CSS transition delay to make updates happen later but cannot fully co-ordinate it with the variable duration of the animation - sometimes it can be way out.
2. Update the data attributes after the animation
I attach the update function directly after the animation, which works, but there is an obvious delay after the animation is finished before the updates kick in.
Neither is perfect.
Ideally what I would like to do is kick off the function just before the scroll animation is finished - so if the scroll duration is 1s I would like to update the attributes at duration - 0.2s (0.8s).
This seems entirely possible given that I have full control of the duration.
What I don't know is how to implement this using jQuery - possibly kick off two tasks in parallel?
I'm not a jQuery expert by any stretch of the imagination so any help would be appreciated.
Please try with this code
$c.animate({ scrollLeft: scrollLeft },
duration, 'easeInOutExpo', function () {
if (showPrev === 0) $s.attr("data-prev", showPrev);
if (showNext === 0) $s.attr("data-next", showNext);
time = setTimeout(function () {
//keep your code here
}, duration-0.2s);
});
duration-0.2s => make it correct, i just mentioned it as an example.
I have a fixed div with a solid background and some text that I am using as a loading screen that fades out when the the page is fully loaded via $(window).load . The catch is since there is actual information on the loader, it needs to be up for a minimum amount of time before fading out. However, in the event that the page loads faster than that minimum time, I do not want it to disappear early, and I also don't want it to stay up past the minimum time once the site is loaded as well, and this is where I am stumped.
Logically it needs to operate like this:
-fade in, wait 5 seconds
-page loads
-if 5 seconds has passed, fade out immediately
-else wait out the remaining time ONLY, then fade out (in other words, don't start counting 5 seconds after onload)
I usually just have an easier loader that displays immediately and then I addClass to hide it via CSS transitions on opacity after onload, but since I need to fade the loading text in and then out and also maintain a minimum time without just adding it as a delay after onload, this is a bit trickier.
This was asked here a few years ago but without a proper answer: jQuery loading screen with minimum viewing time
Would prefer to stick with JS/jQuery & CSS. Any help would be greatly appreciated, thanks!
Just set a timeout for five seconds, check if window is loaded, if so, hide it.
On load, check if five seconds has elapsed, and if so, hide it.
I would do it like this:
var loader = (function(window, $loadingScreen) {
var elapsed = false;
var loaded = false;
setTimeout(function() {
elapsed = true;
if (loaded)
hideLoadingScreen();
}, 5000);
var hideLoadingScreen = function() {
//do whatever
}
$(window).on('load', function() {
if (elapsed) {
hideLoadingScreen();
}
});
}(window, $('#loader'))
So - to simplify, we care only about whether both the page has loaded, and five seconds has passed. So, if at both of those events, we check whether the other has already happened, we know that it will trigger either at five seconds (if the page has loaded), or when the page has loaded (if five seconds has passed).
https://jsfiddle.net/4k7uxdxz/
Use CSS to take care of the animating. This will ensure that the animation will take effect as soon as the page opens and will take 5s to fade in.
.opener {
opacity: 0;
background-color: rgb(0, 0, 0);
color: white;
text-align: center;
font-size: 30px;
animation: fadeIn 5s forwards;
}
#keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
#keyframes fadeIn {
to {
opacity: 1;
}
}
When the DOM is ready simply change the animation to fade out
$(document).ready( function() {
$(".opener").on("animationend", () => {
$(".opener").css("animation", "fadeOut 5s forwards")
});
});
I'm running a scroll event that triggers TweenMax animations, and I'm noticing that, while it looks good on Chrome, there is a considerable amount of lag on Firefox. Does anyone have a suggestion about how to handle this scroll event as efficiently as possible? Also, is there something about Firefox's rendering that I'm not aware of that might be causing this? Any leads would be appreciated!
The gist is that I'm looking for containers on my page called "customers", which each contain three individual "customer" elements. When a div that matches "customers" scrolls into view, trigger a TweenMax animation, and add a class called "animated", which prevents the element from re-animating subsequently.
Here is a fiddle with the basic demonstration:
http://jsfiddle.net/epp37jsq/
EDIT
To clarify, the fiddle only demonstrates the behavior of my animation function. The lag does not occur there because the file size is quite small. On the actual site, I have 11 groups of 3 "customers." The image is the same, but pulled in 33 times. In the future, the images will be unique. In essence, the animation is being called for each of these 11 groups. I'm looking for suggestions on how to improve the speed of my page.
And my code:
var scrollTimer = null;
$(window).scroll(function () {
if (scrollTimer) {
clearTimeout(scrollTimer); // clear any previous pending timer
}
scrollTimer = setTimeout(handleScroll, 500); // set new timer
console.log("fired!");
});
function handleScroll() {
scrollTimer = null;
$('.customers').each(function() {
if (!$(this).hasClass('animated')) {
if ($(this).isOnScreen(0.45, 0.45)) {
TweenMax.staggerFromTo($(this).find('.customer'), 0.3, {
y: 50,
opacity: 0
}, {
y: 0,
opacity: 1,
ease: Power2.easeOut
}, 0.15);
$(this).addClass('animated');
}
}
});
}
Usually with Firefox, translating on the x or y axis can cause some jank. Sometimes adding a slight rotation:0.001 to your tween can help make your tween more smooth in Firefox.
http://jsfiddle.net/pwkja058/
Also using the GSAP special property autoAlpha instead of opacity can help increase performance
TweenMax.staggerFromTo($(this).find('.customer'), 0.3, {
y: 200,
rotation:0.01, /* add a slight rotation */
autoAlpha: 0 /* use instead of opacity */
}, {
y: 0,
rotation:0.01, /* add a slight rotation */
autoAlpha: 1, /* use instead of opacity */
ease: Power2.easeOut
}, 0.15);
autoAlpha is part of the GSAP CSSPlugin:
http://greensock.com/docs/#/HTML5/GSAP/Plugins/CSSPlugin/
autoAlpha - Identical to opacity except that when the value hits 0 the visibility property will be set to "hidden" in order to improve browser rendering performance and prevent clicks/interactivity on the target. When the value is anything other than 0, visibility will be set to "inherit". It is not set to "visible" in order to honor inheritance (imagine the parent element is hidden - setting the child to visible explicitly would cause it to appear when that's probably not what was intended). And for convenience, if the element's visibility is initially set to "hidden" and opacity is 1, it will assume opacity should also start at 0. This makes it simple to start things out on your page as invisible (set your css visibility:hidden) and then fade them in whenever you want.
I have about 50 Images that should be shown one after another inside a div.
The delay between showing one image then another is about 750 milliseconds and decreasing with each image.
I made sure that all images are loaded before this animation kicks in, by using:
(window).load(function() { });
The animaton is done using setTimeout
var index = 1;
function newImage(index) {
var interval = setTimeout( function(){
$("#image-container .image").css("display","none");
$("#image-container .image:nth-child("+index+")").css("display","block");
clearTimeout(interval);
index = index + 1;
newImage(index);
},delay[index-1]);
}
Where delay is an array of delays, something like [750,750,650,...].
The animation works fine, but there's a visual delay as fast as a blink of an eye, where no image is shown and only the background is visible, how can I avoid it?
try to use visibility css property instead of display
I would like to change an image in my site with fading effect. The thing is that I am using two different classes for the same div, so actually I want to fade out one class and in the meanwhile start fading in the second class, before the first one has been completely removed.
HTML:
<div id="background_image" class="day"></div>
CSS:
.day{
background: url(day.png);
}
.night {
background: url(night.png);
}
JQuery:
setTimeout(function(){
if($("#background_image").hasClass("day")){
$("#background_image").fadeOut(function() {
$(this).removeClass("day");
});
$("#Landscape").fadeIn(function() {
$(this).addClass("night");
});
}else{
$("#background_image").fadeOut(function() {
$(this).removeClass("night");
});
$("#Landscape").fadeIn(function() {
$(this).addClass("day");
});
}
}, 5000);
But this code makes the image "day.png" first to disappear completely and then the "night.png" comes which is not what I want.
Is there a way to fade out the class "day" and start fade it "night" without having a blank space between the fading? Thanks in advance
It seems that what you're trying to do is cross-fading. This is normally done using 2 divs. If this is for the entire background, then I suggest http://srobbin.com/jquery-plugins/backstretch/. You can take a look at their implementation to narrow it down to just a div if you don't need it to cover the entire background.
This is how I solved it for a similar case.
var images = [
"/content/images/crane1.jpg",
"/content/images/crane2.jpg",
"/content/images/crane-interior.jpg"
];
// The index variable will keep track of which image is currently showing
var index = 0;
// Call backstretch for the first time,
// In this case, I'm settings speed of 500ms for a fadeIn effect between images.
$.backstretch(images[index], { speed: 500 });
// Set an interval that increments the index and sets the new image
// Note: The fadeIn speed set above will be inherited
setInterval(function () {
index = (index >= images.length - 1) ? 0 : index + 1;
$.backstretch(images[index]);
}, 5000);
EDIT:
For non-full background, take a look at this post Crossfade Background images using jQuery
Also take a look at this, might be closer to your scenario Cross fade background-image with jQuery