I'm having an issue with a gallery script I wrote. Basically, there's a set of thumbnails and a large image with arrows. When you click on a thumbnail, the current image fades out, and when the new image loads, it fades in. The issue I'm having is that if you click on a thumbnail or arrow a bunch of times during the transition (as if changing the currently displayed image), the entire process starts to get delayed. The more you click, they more you see the delay.
So say I click five times in the middle of the fade transition. After the current transition finishes, if I click on another thumbnail/arrow the scripts effect doesn't start immediately like it's supposed to, it lags for a few seconds and then starts.
Here's my script:
$(".jTscroller a").click(function(event) {
event.preventDefault();
var last = $("#photo").attr("src");
var target = $(this).attr("href");
if (last != target) {
$("#photo").stop(false,false).fadeTo("fast", 0, function() {
$("#photo").attr("src",target);
$("#photo").load(function() {
$("#photo").fadeTo("fast", 1);
});
});
};
});
$("#photo-area .arrow.left").click(function(event) {
event.preventDefault();
var current = $("#photo-area #photo").attr("src");
var prev = $(".jTscroller a[href=\""+current+"\"]").prev("a").attr("href");
var next = $(".jTscroller a[href=\""+current+"\"]").next("a").attr("href");
if (typeof prev != "undefined") {
if (prev != current) {
$("#photo").stop(false,false).fadeTo("fast", 0, function() {
$("#photo").attr("src",prev);
$("#photo").load(function() {
$("#photo").fadeTo("fast");
});
});
};
};
});
$("#photo-area .arrow.right").click(function(event) {
event.preventDefault();
var current = $("#photo-area #photo").attr("src");
var prev = $(".jTscroller a[href=\""+current+"\"]").prev("a").attr("href");
var next = $(".jTscroller a[href=\""+current+"\"]").next("a").attr("href");
if (typeof next != "undefined") {
if (next != current) {
$("#photo").stop(false,false).fadeTo("fast", 0, function() {
$("#photo").attr("src",next);
$("#photo").load(function() {
$("#photo").fadeTo("fast", 1);
});
});
};
};
});
JS Fiddle: http://jsfiddle.net/G5VAf/7/
My theory for the fix is to add a Boolean variable that gets changed when the animation completes, but the 50 times I've tried it hasn't worked. Hopefully someone more skilled than me can figure it out.
Oh, and I know I should shrink it down to one smaller script, I'm just trying to get this working before going any farther down that road...
Update: Tried implementing the queue thing mentioned below, and now it won't fade in at all.
$("#photo").animate({
opacity: 0
}, {queue: false, duration: 500}, function() {
$("#photo").attr("src",target);
$("#photo").load(function() {
$("#photo").animate({
opacity: 1
});
});
});
Update 2: figured it out. Final script:
$(document).ready(function() {
$(".jTscroller a").click(function(event) {
event.preventDefault();
var last = $("#photo").attr("src");
var target = $(this).attr("href");
if (last != target) {
$("#photo").stop(false,true).animate({
opacity: 0
}, {queue: false, duration: 500, complete: function() {
$("#photo").attr("src",target);
$("#photo").load(function() {
$("#photo").stop(false,false).animate({
opacity: 1
}, {queue: false, duration: 500});
});
}});
};
});
$("#photo-area .arrow.left").click(function(event) {
event.preventDefault();
var current = $("#photo-area #photo").attr("src");
var prev = $(".jTscroller a[href=\""+current+"\"]").prev("a").attr("href");
var next = $(".jTscroller a[href=\""+current+"\"]").next("a").attr("href");
if (typeof prev != "undefined") {
if (prev != current) {
$("#photo").stop(false,true).animate({
opacity: 0
}, {queue: false, duration: 500, complete: function() {
$("#photo").attr("src",prev);
$("#photo").load(function() {
$("#photo").stop(false,false).animate({
opacity: 1
}, {queue: false, duration: 500});
});
}});
};
};
});
$("#photo-area .arrow.right").click(function(event) {
event.preventDefault();
var current = $("#photo-area #photo").attr("src");
var prev = $(".jTscroller a[href=\""+current+"\"]").prev("a").attr("href");
var next = $(".jTscroller a[href=\""+current+"\"]").next("a").attr("href");
if (typeof next != "undefined") {
if (next != current) {
$("#photo").stop(false,true).animate({
opacity: 0
}, {queue: false, duration: 500, complete: function() {
$("#photo").attr("src",next);
$("#photo").load(function() {
$("#photo").stop(false,false).animate({
opacity: 1
}, {queue: false, duration: 500});
});
}});
};
};
});
});
Sounds like you are having problems with the animate queue. The jQuery functions you are using are just shorthand for the .animate() function.
.animate( properties, options )
queue: A Boolean indicating whether to place the animation in the effects queue. If false, the animation will begin immediately. As of jQuery 1.7, the queue option can also accept a string, in which case the animation is added to the queue represented by that string.
Basically each click is then getting added to the animate queue hence the delay. You can try the .animate() function and set queue to false and see if that helps.
Related
I have this pseudo code. I need to check that all animations have ended.
My function with many animations inside:
function animateText($element, $callback) {
var $this = $element;
var $wordList = $this.text().split("");
$this.text("");
$this.css('opacity', 1);
$.each($wordList, function (idx, elem) {
var newEL = $("<span/>").text(elem).css({
opacity: 0
});
newEL.appendTo($this);
newEL.delay(idx * 125);
newEL.animate({
opacity: 1
}, 500, 'swing', function () {
// now are animations are done
if ($wordList.length === idx + 1) {
$callback();
}
});
});
};
We started!
animateText('#myText', function(){
console.log('ALL ANIMATIONS ARE DONE!');
});
I need to know when all the animations have ended and after call my callback. This approach works, but is possible to write it somehow better?
I'm using the plugin smoothstate.js on my website. For some reason, every now and again when I navigate through the pages using the back and forward buttons, the back button stops working.
The URL changes but the content remains the same?
I've checked the console for errors this is displaying:
TypeError: Cannot read property 'state' of undefined
Does anyone know why this is happening? Like I said, the majority of the time it works okay but all of sudden it doesn't.
The code I'm using is like so:
$(function(){
'use strict';
var options = {
prefetch: true,
debug:true,
cacheLength: 0,
repeatDelay: 500,
onStart: {
duration: 0, // Duration of our animation
render: function ($container) {
// Add your CSS animation reversing class
$container.addClass('is-exiting');
// Restart your animation
smoothState.restartCSSAnimations();
}
},
onProgress: {
// How long this animation takes
duration: 0,
// A function that dictates the animations that take place
render: function ($container) {
$container.addClass('is-loading');
$('#progressBar').append('<div id="bar"></div>');
var progress = '100%';
$('#bar').animate({
width: progress
}, 400);
}
},
onReady: {
duration: 0,
render: function ($container, $newContent) {
$container.removeClass('is-loading is-exiting');
// Inject the new content
$container.html($newContent);
},
},
onAfter: function() {
navbarAnimate();
closeMenu();
ImageSliders();
initPhotoSwipeFromDOM('.gallery');
ImageOverlay();
window.parsePinBtns();
backToTop();
}
},
smoothState = $('#main').smoothState(options).data('smoothState');
});
I also ran into this issue which happens when the back and forward buttons are clicked too fast without the page fully loading. A hacky solution for me was to reload the page if page.cache[page.href] is undefined.
/** Handles the popstate event, like when the user hits 'back' */
onPopState = function(e) {
if(e.state !== null || typeof e.state !== undefined) {
var url = window.location.href;
var $page = $('#' + e.state.id);
var page = $page.data('smoothState');
if (typeof(page.cache[page.href]) !== 'undefined') {
var diffUrl = (page.href !== url && !utility.isHash(url, page.href));
var diffState = (e.state !== page.cache[page.href].state);
if(diffUrl || diffState) {
if (diffState) {
page.clear(page.href);
}
page.load(url, false);
}
}
else {
//reload the page if page.cache[page.href] is undefined
location.reload();
}
}
},
I commented a fadeOut for some of the sections but cannot get them to reappear after scrolling a second time and I don't know javascript well enough to find the right solution. How would I do that?
$(document.body).fadeIn(1000);
$(document).ready(function() {
'use strict';
// variables
var $isH1First = $('.first .is-animated h1'),
$ispFirst = $('.first .is-animated p'),
$isH1Second = $('.second .is-animated h1'),
$ispSecond = $('.second .is-animated p'),
$isH1Third = $('.third .is-animated h1'),
$ispThird = $('.third .is-animated p');
// initialize fullPage
$('#fullpage').fullpage({
// navigation: true,
scrollingSpeed: 1400,
resetSliders:true,
fitToSectionDelay: 1000,
afterLoad: function(index, nextIndex) {
if( nextIndex === 1 ) {
$isH1First.addClass('animated fadeInLeft').css('animation-delay', '.05s');
$ispFirst.addClass('animated fadeInLeft').css('animation-delay', '.45s');
}
},
onLeave: function(index, nextIndex) {
// first animation
if( nextIndex === 2) {
// $isH1First.addClass('animated fadeOutUp');
// $ispFirst.addClass('animated fadeOutUp');
$isH1Second.addClass('animated fadeInDown').css('animation-delay', '.45s');
$ispSecond.addClass('animated fadeInDown').css('animation-delay', '.85s');
}
// second animation
else if( nextIndex === 3 ) {
// $isH1Second.addClass('animated fadeOutUp');
// $ispSecond.addClass('animated fadeOutUp');
$isH1Third.addClass('animated bounceInDown').css('animation-delay', '.45s');
$ispThird.addClass('animated flipInX').css('animation-delay', '.85s');
}
}
});
});
I know this is rather late ,I use velocity.js to get nice animations and I wanted to only show the animations once and not every time the section loads.
If I understand you correctly then you are trying to achieve the opposite which was the default behavior for me.
A small example of my code :
if (index === 1) {
//slide 1 animation
if (!$jq(".refine").is(":visible")
{
$jq(".refine").velocity("fadeIn", {duration: 2000});
}
}
I have made a jQuery plugin which acts as a slideshow. All of my problem is that it does fades out first, and then the next images fades in. Which is a little odd. I have thought all the day and am really beat about it. Can anyone helps me? Here is the info:
Below is a list of websites with current slideshow hosted:
http://behdis.org
www.ms-models.com
www.royal-fci.com
Now, PROBLEM is:
there are naturally a bunch of images for the slideshow to fade them in and out. Now, the slideshow makes the first image faded out and then makes the next image faded in. I want the slideshow to begin fading in the next image when the current image has already begun fading out. Just like hundreds of slideshows out there on many websites. No need to give link.
Here is the plugin script itself:
(function(){
/* function read_line (path_to_file, container, tag, url_prefix)
{
myObject = {}; //myObject[numberline] = "textEachLine";
$.get(, functipath_to_fileon(myContentFile) {
var lines = myContentFile.split("\r\n");
for(var i in lines)
{
myObject[i] = i;
container.append('<'+tag+'>'+"<img src='"+url_prefix+'/'+lines[i]+"'>"+'</img>'+'</'+tag+'>');
}
}, 'text');// end of anonymous function
} */
$.fn.mtSlideGallery = function(options){
var options = $.extend({
fullWidth : false, // this makes a full background slideshow
FadeIn : 2, // this is the duration of fading in
FadeOut : 2, // this is the duration of fading out
visibilityDuration : 2, // this is the duration of visible image
startSlide : 1, // this is the first slide when the page loads
height : '100%',
height_fixed : true,
enable_error : false,
enable_gallery : false,
slide_previous : '.slide-left-control', // this is the name of class for previous-picture-handle
slide_next : '.slide-right-control', // this is the name of class for next-picture-handle
print_output : false,
print_area : '.slideshow-label',
seo_campatible : false, // this allows the script to print the title of each slide out into a h1-h6 tag
seo_suggestion : 'h2' // this allows the user to specify which h1-h6 tag be used. default is h6
/*auto_generate_from_file : false, // if true, then it will generate the div tag based on a file given
auto_generate_url : false, // if true, then a url_prefix is added to each line of text
auto_generate_tag : 'div', // the tag inside which the image tags nest. default is div and it is recommended no to change
auto_generate_url_prefix : "" // this is the url added to each image address. Depends on auto_generate_url options*/
}, options)
// develope $fn protype
return this.each(function(){
if(options.print_output === true)
{
var text_for_output = $(this).children('div').eq(options.startSlide).children('img').attr('alt');
$(options.print_area).text(text_for_output);
}
if(options.fullWidth === true)
{
$("body").css({'padding' : '0', 'margin' : '0'});
$("body").css({'overflow' : 'hidden'});
$("html").css({'padding' : '0', 'margin' : '0'});
$(this).css({'width' : '100%', 'height' : options.height});
$(this).css({'margin' : '0', 'padding' : '0'});
}
if(options.height_fixed === true)
{
$(this).css({'overflow' : 'hidden'});
}
var slideWrapper = $(this);
var slidesGroup = slideWrapper.children('div');
slidesLength = slidesGroup.length;
if(options.enable_error == true)
{
if(options.startSlide > slidesLength)
{
alert("Please correct the \"slideStart\" option. The number has to be less than the total number of images.")
}
}
i = options.startSlide;
slidesGroup.not(":eq("+i+")").hide();
console.log(slidesLength);
////////////////////// Print To label if true
var print_label = function(i)
{
var label = slidesGroup.eq(i).children('img').attr('alt');
$(options.print_area).text(label);
}
;
///////////////////// End of printing label
//////////////// GALLERY SLIDESHOW IF ENABLED USES THIS SCRIPT
if(options.enable_gallery === true)
{
$(options.slide_previous).click(function(event){ // this is a click event for the previous picture in the queue
event.preventDefault();
event.stopPropagation();
slidesGroup.eq(i).hide();
if(i === slidesLength)
{
i=0;
}
i -= 1;
slidesGroup.eq(i).show();
if(options.print_output === true)
{
print_label(i);
}
});
$(options.slide_next).click(function(event){ // this is a click event for the next picture in the queue
event.preventDefault();
event.stopPropagation();
slidesGroup.eq(i).hide();
if(i === slidesLength)
{
i=0;
}
i += 1;
slidesGroup.eq(i).show();
if(options.print_output === true)
{
print_label(i);
}
});
}
////////////////////// END OF GALLERY-ENABLED SCRIPT
////////////////////////////////////////////////////////
var slideShow = function () {
slidesGroup.eq(i).fadeOut(options.FadeOut*1000, function(){
slidesGroup.eq(i).hide();
i += 1;
if(i === slidesLength)
{
i=0;
}
slidesGroup.eq(i).fadeIn(options.FadeIn*1000);
if(options.print_output === true)
{
print_label(i);
}
})
}
setInterval(slideShow, options.visibilityDuration*1000);
})
}
})(jQuery)
Now, the slideshow makes the first image faded out and then makes the
next image faded in. I want the slideshow to begin fading in the next
image when the current image has already begun fading out.
The issue here is that the call to fade in the next slide is part of an anonymous callback function that does not fire until the initial jQuery animation is complete. You'll want to rewrite your code such that the $.fadeOut() and $.fadeIn() functions occur simultaneously:
var transition = function() {
//Determine the index of the next slide to show, based on the current index
var next_index = (current_index < slidesLength - 1) ? current_index + 1 : 0;
//Fade out the 'old' slide while fading in the 'new' slide
slidesGroup.eq(current_index).fadeOut({
duration: options.FadeOut*1000,
start: function() {
slidesGroup.eq(next_index).fadeIn({
duration: options.FadeIn*1000
});
},
complete: function() {
//Update the value of current_index
current_index = next_index;
}
});
}
However, this function pretty much restricts you to forward-only transitions. You could include a function argument that specifies a transition direction (forward and backward) that will determine the 'next slide' index.
Working jsFiddle: http://jsfiddle.net/gh25s/
EDIT: To clarify, you'll want to replace this part of your code:
var slideShow = function () {
slidesGroup.eq(i).fadeOut(options.FadeOut*1000, function(){
slidesGroup.eq(i).hide();
i += 1;
if(i === slidesLength) {
i=0;
}
slidesGroup.eq(i).fadeIn(options.FadeIn*1000);
if(options.print_output === true) {
print_label(i);
}
});
};
with this:
var slideShow = function() {
//Determine the index of the next slide to show, based on the current index
var next_index = (i < slidesLength - 1) ? i + 1 : 0;
//Fade out the 'old' slide while fading in the 'new' slide
slidesGroup.eq(i).fadeOut({
duration: options.FadeOut*1000,
start: function() {
slidesGroup.eq(next_index).fadeIn({
duration: options.FadeIn*1000
});
},
complete: function() {
//Update the value of i
i = next_index;
}
});
}
Also, using the variable name i throughout your code is confusing. I would suggest giving it a more meaningful, descriptive name like current_index. Variable names like i are typically read as temporary, local variables that are being used for some kind of iteration.
I wrote a slideshow plugin, but for some reason maybe because I've been working on it all day, I can't figure out exactly how to get it to go back to state one, once it's reached the very last state when it's on auto mode.
I'm thinking it's an architectual issue at this point, because basically I'm attaching the amount to scroll left to (negatively) for each panel (a panel contains 4 images which is what is currently shown to the user). The first tab should get: 0, the second 680, the third, 1360, etc. This is just done by calculating the width of the 4 images plus the padding.
I have it on a setTimeout(function(){}) currently to automatically move it which works pretty well (unless you also click tabs, but that's another issue). I just want to make it so when it's at the last state (numTabs - 1), to animate and move its state back to the first one.
Code:
(function($) {
var methods = {
init: function(options) {
var settings = $.extend({
'speed': '1000',
'interval': '1000',
'auto': 'on'
}, options);
return this.each(function() {
var $wrapper = $(this);
var $sliderContainer = $wrapper.find('.js-slider-container');
$sliderContainer.hide().fadeIn();
var $tabs = $wrapper.find('.js-slider-tabs li a');
var numTabs = $tabs.size();
var innerWidth = $wrapper.find('.js-slider-container').width();
var $elements = $wrapper.find('.js-slider-container a');
var $firstElement = $elements.first();
var containerHeight = $firstElement.height();
$sliderContainer.height(containerHeight);
// Loop through each list element in `.js-slider-tabs` and add the
// distance to move for each "panel". A panel in this example is 4 images
$tabs.each(function(i) {
// Set amount to scroll for each tab
if (i === 1) {
$(this).attr('data-to-move', innerWidth + 20); // 20 is the padding between elements
} else {
$(this).attr('data-to-move', innerWidth * (i) + (i * 20));
}
});
// If they hovered on the panel, add paused to the data attribute
$('.js-slider-container').hover(function() {
$sliderContainer.attr('data-paused', true);
}, function() {
$sliderContainer.attr('data-paused', false);
});
// Start the auto slide
if (settings.auto === 'on') {
methods.auto($tabs, settings, $sliderContainer);
}
$tabs.click(function() {
var $tab = $(this);
var $panelNum = $(this).attr('data-slider-panel');
var $amountToMove = $(this).attr('data-to-move');
// Remove the active class of the `li` if it contains it
$tabs.each(function() {
var $tab = $(this);
if ($tab.parent().hasClass('active')) {
$tab.parent().removeClass('active');
}
});
// Add active state to current tab
$tab.parent().addClass('active');
// Animate to panel position
methods.animate($amountToMove, settings);
return false;
});
});
},
auto: function($tabs, settings, $sliderContainer) {
$tabs.each(function(i) {
var $amountToMove = $(this).attr('data-to-move');
setTimeout(function() {
methods.animate($amountToMove, settings, i, $sliderContainer);
}, i * settings.interval);
});
},
animate: function($amountToMove, settings, i, $sliderContainer) {
// Animate
$('.js-slider-tabs li').eq(i - 1).removeClass('active');
$('.js-slider-tabs li').eq(i).addClass('active');
$('#js-to-move').animate({
'left': -$amountToMove
}, settings.speed, 'linear', function() {});
}
};
$.fn.slider = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
return false;
}
};
})(jQuery);
$(window).ready(function() {
$('.js-slider').slider({
'speed': '10000',
'interval': '10000',
'auto': 'on'
});
});
The auto and animate methods are where the magic happens. The parameters speed is how fast it's animated and interval is how often, currently set at 10 seconds.
Can anyone help me figure out how to get this to "infinitely loop", if you will?
Here is a JSFiddle
It would probably be better to let go of the .each() and setTimeout() combo and use just setInterval() instead. Using .each() naturally limits your loop to the length of your collection, so it's better to use a looping mechanism that's not, and that you can break at any point you choose.
Besides, you can readily identify the current visible element by just checking for .active, from what I can see.
You'd probably need something like this:
setInterval(function () {
// do this check here.
// it saves you a function call and having to pass in $sliderContainer
if ($sliderContainer.attr('data-paused') === 'true') { return; }
// you really need to just pass in the settings object.
// the current element you can identify (as mentioned),
// and $amountToMove is derivable from that.
methods.animate(settings);
}, i * settings.interval);
// ...
// cache your slider tabs outside of the function
// and just form a closure on that to speed up your manips
var slidertabs = $('.js-slider-tabs');
animate : function (settings) {
// identify the current tab
var current = slidertabs.find('li.active'),
// and then do some magic to determine the next element in the loop
next = current.next().length >= 0 ?
current.next() :
slidertabs.find('li:eq(0)')
;
current.removeClass('active');
next.addClass('active');
// do your stuff
};
The code is not optimized, but I hope you see where I'm getting at here.