In the slide show code below, there's a function to press "previous" and "next" links. The "next" one works fine, and if you keep pressing it, it cycles through all the slides.
The "previous" one is a bit messed up, for some reason - it will go back a slide or two but then it will just go blank!
Could you please help?
Thank you!
<script type="text/javascript">
start_slideshow(1, 3, 3000);
var currentSlide = 1;
function start_slideshow(start_frame, end_frame, delay) {
id = setTimeout(switch_slides(start_frame,start_frame,end_frame, delay), delay);
}
function switch_slides(frame, start_frame, end_frame, delay) {
return (function() {
Effect.Fade('slide' + frame, { duration: 1.0 });
if (frame == end_frame) {
frame = start_frame;
currentSlide = frame;
} else {
frame = frame + 1;
currentSlide = frame;
}
Effect.Appear('slide' + frame, { duration: 1.0 });
if (delay == 1000) {
delay = 3000;
}
id = setTimeout(switch_slides(frame, start_frame, end_frame, delay), delay);
})
}
function stop_slideshow() {
clearTimeout(id);
}
function next_slide() {
clearTimeout(id);
Effect.Fade('slide' + currentSlide, { duration: 1.0 });
if (currentSlide == 4) {
currentSlide = 0;
}
currentSlide = currentSlide + 1;
Effect.Appear('slide' + currentSlide, { duration: 1.0 });
id = setTimeout(switch_slides(currentSlide, currentSlide, currentSlide, delay), delay);
}
function previous_slide() {
clearTimeout(id);
if (currentSlide == 0) {
currentSlide = 1;
} else {
Effect.Fade('slide' + currentSlide, { duration: 1.0 });
currentSlide = currentSlide - 1;
Effect.Appear('slide' + currentSlide, { duration: 1.0 });
id = setTimeout(switch_slides(currentSlide, currentSlide, currentSlide, delay), delay);
}
}
</script>
I have run into a problem like this, or similar to this. I am assuming that you are "rapidly" clicking the previous or next button before the effect that is currently queued is finished. This will cause an error with the effects resulting in a given element having more than one affect being applied to it at the same time.
To fix this, scriptaculous gives us the ability to queue effects in order. Reference the docs here:
http://wiki.github.com/madrobby/scriptaculous/effect-queues
Essentially, you need to add a scope to your effects. So after you add your duration, you need to add a queue scope.
Example:
Effect.Fade('slide' + currentSlide, { duration: 1.0, queue: { scope: 'slide_show' } });
This allows us to cancel anything in the queue when a button is pressed.
var queue = Effect.Queues.get( 'slide_show' );
queue.each( function( effect ) { effect.cancel(); } );
This will possibly fix your blanking...but possibly not. Regardless, these methods need to be employed in your slide show ;)
Report back if it doesn't fix it, and I'll do some more troubleshooting.
Related
I build a jquery carousel. But when I do fast Swiperight or left for interval will interfere and work fastly. How can I fix this?
Here is uploaded project
I have settings variable in setting interval is time for Setinterval and items are my images, Slide is active image to show, Clock is my Setinterval Function.
when slide change I clear clock variable and after then set it with time out to user can view the slide and slide does not change.
var settings = $.extend({
interval: 5000,
element: this.selector,
items: ['images/1.jpg','images/2.jpg','images/3.jpg'],
slide: 0,
clock: 0,
}, options);
// Touch
$(settings.element + ' .primary img').on("swiperight", function () {
nextSlide();
pauseInterval();
setTimeout(function () {
if (!settings.clock)
settings.clock = setInterval(nextSlide, settings.interval)
}, 4000);
}).on("swipeleft", function () {
prevSlide();
pauseInterval();
setTimeout(function () {
if (!settings.clock)
settings.clock = setInterval(prevSlide, settings.interval)
}, 4000);
});
// Next Slide
function nextSlide() {
settings.slide++;
if (settings.slide >= settings.items.length) settings.slide = 0;
$(settings.element + ' .primary img').attr('src', settings.items[settings.slide]).attr('data-slide', settings.slide);
activeThumbnail()
}
// Prev Slide
function prevSlide() {
settings.slide--;
if (settings.slide < 0) settings.slide = settings.items.length;
$(settings.element + ' .primary img').attr('src', settings.items[settings.slide]).attr('data-slide', settings.slide);
activeThumbnail()
}
//Pause Interval
function pauseInterval() {
clearInterval(settings.clock);
settings.clock = 0;
}
You will find it simpler to work with setTimout() exclusively instead of a mix of setTimout() and setInterval().
There's no unique way to write the code. Here's one :
var settings = $.extend({
'delay': {
'long_': 9000, // (4000 + 5000) after swiping
'short_': 5000 // auto
},
'element': this.selector,
'items': ['images/1.jpg', 'images/2.jpg', 'images/3.jpg'], // array of Strings
'slide': 0,
'clock': null
}, options);
$(settings.element + ' .primary img').on('swiperight', nextSlide).on('swipeleft', prevSlide); // nice and simple
// nextSlide() and prevSlide() ...
// in auto: `delay` will be `settings.delay.short_`.
// on manual swipe: `delay` will be `undefined` and will default to `settings.delay.long_`.
function nextSlide(delay) {
delay = (delay === undefined) ? settings.delay.long_ : delay;
settings.slide = (settings.slide + 1) % settings.items.length;
set(nextSlide, delay);
}
function prevSlide(delay) {
delay = (delay === undefined) ? settings.delay.long_ : delay;
settings.slide = (settings.slide + settings.items.length - 1) % settings.items.length;
set(prevSlide, delay);
}
// set() does all the stuff common to nextSlide() and prevSlide().
function set(action, delay) {
$(settings.element + ' .primary img').attr('src', settings.items[settings.slide]).attr('data-slide', settings.slide);
activeThumbnail();
clearTimeout(settings.clock); // relevant only after a manual swipe, but does no harm in auto.
settings.clock = setTimeout(action.bind(null, settings.delay.short_), delay);
}
I know there are a lot of questions on the topic, but it seems none of the answers works for me (or maybe I don't see something obvious). I am building a featured content slider that should pause on hover. However, if I move over with the mouse 5-6 times, it goes through the loop 5-6 times at once and it becomes buggy. The previous recursion doesn't stop and a new one is initiated too.
My function:
gravityFeatured.prototype.loop = function(slide) {
// Begin
var self = this;
if(typeof slide == 'undefined') {
slide = 0;
}
self.slidesWrapper.find('.slide-wrapper').removeClass('current next prev');
self.navigation.find('.navigation-item').removeClass('current');
// Current slide
currentSlide = self.slidesWrapper.find('.slide-wrapper[data-slide="' + slide + '"]');
currentNav = self.navigation.find('.navigation-item[data-slide="' + slide + '"]');
// Next slide
var nextSlide = self.slidesWrapper.find('.slide-wrapper[data-slide="' + (slide + 1) + '"]');
var next = slide+1;
if(!nextSlide.length) {
nextSlide = self.slidesWrapper.find('.slide-wrapper[data-slide="0"]');
next = 0;
}
// Prev slide
var prevSlide = self.slidesWrapper.find('.slide-wrapper[data-slide="' + (slide - 1) + '"]');
if(!prevSlide.length) {
prevSlide = self.slidesWrapper.find('.slide-wrapper[data-slide="' + (self.slides.length - 1) + '"]');
}
// Assign classes
currentSlide.addClass('current');
currentNav.addClass('current');
nextSlide.addClass('next');
prevSlide.addClass('prev');
self.scrollNavigation(slide);
// Loop
var timeout = setTimeout(function(){
self.loop(next);
}, self.options['delay']);
self.slidesWrapper.hover(function(){
clearTimeout(timeout);
}, function(){
timeout = setTimeout(function(){self.loop(next);});
});
}
So with the current setup every time you mouseout you trigger a move. Thus in and out 5 times is going to trigger 5 moves. Perhaps better behavious is to pause the countdown while the mouse is in the hovering. This would look something like:
gravityFeatured.countdown = null,
gravityFeatured.isPaused = false,
gravityFeatured.prototype.loop = function(slide) {
...
self.scrollNavigation(slide);
self.countdown = self.options['delay'] * 1000;
var timeout = null; /* need this in loopCheck */
var loopCheck = function() {
if (!this.isPaused)
this.countdown -= 500;
if (this.countdown <= 0)
this.loop(next);
else
timeout = setTimeout(loopCheck,500); /* check every half sec */
}
timeout = setTimeout(loopCheck,500);
self.slidesWrapper.hover(function(){
self.isPaused = true
}, function(){
self.isPaused = false;
});
}
A half second timeout might be a little long, could probably go down to 250ms if necessary. Should handle lots of mouse movement okay though.
I am using the following as a content slider but unsure as to instead have the click the button to play the slides through but wanting to take that away and for the slides to go go through on load?
DEMO
$(document).ready(function() {
var height = 300,
width = 600,
tabs = 3,
$tabs = $('.tab'),
contentNum = 1,
delay = 2000, // time in milliseconds to pause between tabs
timer;
$('.play').click(function(){
var $t = $(this);
if ($t.hasClass('playing')) {
// stop
clearTimeout(timer);
$t.removeClass('playing').html('play');
} else {
// play
timer = setInterval(function(){
contentNum++; // change to contentNum--; to go left
if (contentNum > tabs){ contentNum = 1; } // loop right
if (contentNum < 1) { contentNum = tabs; } // loop left
$tabs.eq(contentNum-1).find('a').trigger('click');
}, delay);
$t.addClass('playing').html('stop');
}
});
$('.main_inner').css({
width: tabs * width
});
$('a.tab_link').click(function() {
$tabs.filter('.active').removeClass('active');
$(this).parent().addClass('active');
// make sure contentNum is a number and not a string
contentNum = parseInt( $(this).attr('rel'), 10 );
$('.main_inner').animate({
marginLeft: '-' + (width * contentNum - width)
}, 600);
return false;
});
});
No really sure what you mean.
If you wan't to autoplay on page load and keep the Play button use
$('.play').trigger('click');
If you wan't to autoplay without play/stop button replace
$('.play').click(function(){
// you current play/pause button code
});
simply by
// play
setInterval(function(){
contentNum++; // change to contentNum--; to go left
if (contentNum > tabs){ contentNum = 1; } // loop right
if (contentNum < 1) { contentNum = tabs; } // loop left
$tabs.eq(contentNum-1).find('a').trigger('click');
}, delay);
The code (from an old plugin that I am trying to make responsive) slides a set of images across every n seconds. It uses setInterval code as below, and works well on Firefox. On Chrome it runs once only, and debugging indicates that the second setInteral function is just not called. Please help as its diving me mad. Running example at http://lelal.com/test/site10/index.html (sorry about the load time)
play = setInterval(function() {
if (!busy) {
busy = true;
updateCurrent(settings.direction);
slide();
}
}, settings.speed);
The complete plugin code is below (sorry its long)
/*
* jQuery Queue Slider v1.0
* http://danielkorte.com
*
* Free to use and abuse under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
(function($){
var QueueSlider = function(element, options) {
var play = false,
busy = false,
current = 2,
previous = 2,
widths = [],
slider = $(element),
queue = $('ul.queue', slider),
numImages = $('img', queue).size(),
viewportWidth = slider.width(),
settings = $.extend({}, $.fn.queueSlider.defaults, options);
$(window).resize(function(){
if(busy !== false)
clearTimeout(busy);
busy = setTimeout(resizewindow, 200); //200 is time in miliseconds
});
function resizewindow() {
viewportWidth = slider.width();
if (settings.scale > 0) {
slider.css('height',viewportWidth * settings.scale);
computeQueueWidth();
}
queue.css('left', -getQueuePosition());
busy = false;
}
function requeue() {
$('li', queue).each(function(key, value) {
$(this).attr('class', 'slide-' + (key+1));
});
}
function updateCurrent(dir) {
current += dir;
if (current < 1) {
current = numImages;
} else if (current > numImages) {
current = 1;
}
}
function getQueuePosition() {
var i = 0, index = current-1,
queuePosition = (viewportWidth - widths[index]) / -2;
for (i = 0; i < index; i++) { queuePosition += widths[i]; }
return queuePosition;
}
function computeQueueWidth() {
var queueWidth = 0;
// factor = slider.height() / settings.imageheight;
// settings.imageheight = settings.imageheight * factor;
// Get the image widths and set the queue width to their combined value.
$('li', queue).each(function(key, value) {
var slideimg = $("img", this),
slide = $(this),
// width = slide.width() * factor,
width = slideimg.width();
slide.css('width', width+'px');
queueWidth += widths[key] = width;
});
queue.css('width', queueWidth + 500);
}
function slide() {
var animationSettings = {
duration: settings.transitionSpeed,
queue: false
};
// Emulate an infinte loop:
// Bring the first image to the end.
if (current === numImages) {
var firstImage = $('li.slide-1', queue);
widths.push(widths.shift());
queue.css('left', queue.position().left + firstImage.width()).append(firstImage);
requeue();
current--; previous--;
}
// Bring the last image to the beginning.
else if (current === 1) {
var lastImage = $('li:last-child', queue);
widths.unshift(widths.pop());
queue.css('left', queue.position().left + -lastImage.width()).prepend(lastImage);
requeue();
current = 2; previous = 3;
}
// Fade in the current and out the previous images.
if (settings.fade !== -1) {
$('li.slide-'+current, queue).animate({opacity: 1}, animationSettings);
$('li.slide-'+previous, queue).animate({opacity: settings.fade}, animationSettings);
}
// Animate the queue.
animationSettings.complete = function() { busy = false; };
queue.animate({ left: -getQueuePosition() }, animationSettings);
previous = current;
}
//
// Setup the QueueSlider!
//
if (numImages > 2) {
// Move the last slide to the beginning of the queue so there is an image
// on both sides of the current image.
if (settings.scale > 0) {
slider.css('height',viewportWidth * settings.scale);
}
computeQueueWidth();
widths.unshift(widths.pop());
queue.css('left', -getQueuePosition()).prepend($('li:last-child', queue));
requeue();
// Fade out the images we aren't viewing.
if (settings.fade !== -1) { $('li', queue).not('.slide-2').css('opacity', settings.fade); }
// Include the buttons if enabled and assign a click event to them.
if (settings.buttons) {
slider.append('<button class="previous" rel="-1">' + settings.previous + '</button><button class="next" rel="1">' + settings.next + '</button>');
$('button', slider).click(function() {
if (!busy) {
busy = true;
updateCurrent(parseInt($(this).attr('rel'), 10));
clearInterval(play);
slide();
}
return false;
});
}
// Start the slideshow if it is enabled.
if (settings.speed !== 0) {
play = setInterval(function() {
if (!busy) {
busy = true;
updateCurrent(settings.direction);
slide();
}
}, settings.speed);
}
}
else {
// There isn't enough images for the QueueSlider!
// Let's disable the required CSS and show all one or two images ;)
slider.removeClass('queueslider');
}
};
$.fn.queueSlider = function(options) {
return this.each(function(key, value) {
var element = $(this);
// Return early if this element already has a plugin instance.
if (element.data('queueslider')) { return element.data('queueslider'); }
// Pass options to plugin constructor.
var queueslider = new QueueSlider(this, options);
// Store plugin object in this element's data.
element.data('queueslider', queueslider);
});
};
$.fn.queueSlider.defaults = {
scale: 0,
imageheight: 500,
fade: 0.3, // Opacity of images not being viewed, use -1 to disable
transitionSpeed: 700, // in milliseconds, speed for fade and slide motion
speed: 7000, // in milliseconds, use 0 to disable slideshow
direction: 1, // 1 for images to slide to the left, -1 to silde to the right during slideshow
buttons: true, // Display Previous/Next buttons
previous: 'Previous', // Previous button text
next: 'Next' // Next button text
};
}(jQuery));
Have a look here:
http://www.w3schools.com/jsref/met_win_setinterval.asp
The setInterval() method will continue calling the function until clearInterval() is called, or the window is closed.
Looks like you're calling clearInterval after the first usage of play, which makes it stop working.
I have a bug in Javascript where I am animating the margin left property of a parent container to show its child divs in a sort of next/previous fashion. Problem is if clicking 'next' at a high frequency the if statement seems to be ignored (i.e. only works if click, wait for animation, then click again) :
if (marLeft === (-combinedWidth + (regWidth) + "px")) {
//roll margin back to 0
}
An example can be seen on jsFiddle - http://jsfiddle.net/ZQg5V/
Any help would be appreciated.
Try the below code which will basically check if the container is being animated just return from the function.
Working demo
$next.click(function (e) {
e.preventDefault();
if($contain.is(":animated")){
return;
}
var marLeft = $contain.css('margin-left'),
$this = $(this);
if (marLeft === (-combinedWidth + (regWidth) + "px")) {
$contain.animate({
marginLeft: 0
}, function () {
$back.fadeOut('fast');
});
} else {
$back.fadeIn(function () {
$contain.animate({
marginLeft: "-=" + regWidth + "px"
});
});
}
if (marLeft > -combinedWidth) {
$contain.animate({
marginLeft: 0
});
}
});
Sometimes is better if you create a function to take care of the animation, instead of writting animation code on every event handler (next, back). Also, users won't have to wait for the animation to finish in order to go the nth page/box.
Maybe this will help you:
if (jQuery) {
var $next = $(".next"),
$back = $(".back"),
$box = $(".box"),
regWidth = $box.width(),
$contain = $(".wrap")
len = $box.length;
var combinedWidth = regWidth*len;
$contain.width(combinedWidth);
var currentBox = 0; // Keeps track of current box
var goTo = function(n) {
$contain.animate({
marginLeft: -n*regWidth
}, {
queue: false, // We don't want animations to queue
duration: 600
});
if (n == 0) $back.fadeOut('fast');
else $back.fadeIn('fast');
currentBox = n;
};
$next.click(function(e) {
e.preventDefault();
var go = currentBox + 1;
if (go >= len) go = 0; // Index based, instead of margin based...
goTo(go);
});
$back.click(function(e) {
e.preventDefault();
var go = currentBox - 1;
if (go <= 0) go = 0; //In case back is pressed while fading...
goTo(go);
});
}
Here's an updated version of your jsFiddle: http://jsfiddle.net/victmo/ZQg5V/5/
Cheers!
Use a variable to track if the animation is taking place. Pseudocode:
var animating = false;
function myAnimation() {
if (animating) return;
animating = true;
$(this).animate({what:'ever'}, function() {
animating = false;
});
}
Crude, but it should give you the idea.
Edit: Your current code works fine for me as well, even if I jam out on the button. On firefox.