Creating a sliding image gallery that does not glitch on image change - javascript

I have created a sliding image gallery and when the button is pushed it slides the picture across and updates the image attribute for the relevant sections.
However this works perfectly like 50% of the time. The other times there is a second glitch and the images then go in place as expected.
I have attached the javascript methods for the animate method and the array change method. I have looked elsewhere and cannot see anyone else with a similar issue or where I am going wrong, especially when it doesn't happen often.
imageGallery.leftSelect.onclick = function () {
window.setTimeout(imageGallery.rightClick, 250);
imageGallery.animateImages('.image1', '.imageRight');
imageGallery.animateImages('.imageRight', '.imageNoneRight');
imageGallery.animateImages('.imageLeft', '.image1');
imageGallery.animateImages('.imageNoneLeft', '.imageLeft');
};
animateImages: function (classFrom, classTo) {
var classMoving = $(classFrom);
var classGoingTo = $(classTo);
classMoving.animate({
top: classGoingTo.css('top'),
left: classGoingTo.css('left'),
width: classGoingTo.css('width'),
opacity: classGoingTo.css('opacity'),
}, 258, function () {
console.log('Animated');
classMoving.css({"width":'', "opacity":'', "top":'', "left":'', });
});
},
rightClick: function () {
imageGallery.imagesDisplay.push(imageGallery.imagesDisplay.shift());
imageGallery.imageNoneLeft.setAttribute('src', imageGallery.imagesDisplay[2]);
imageGallery.imageLeft.setAttribute('src', imageGallery.imagesDisplay[1]);
imageGallery.imageMain.setAttribute('src', imageGallery.imagesDisplay[0]);
imageGallery.imageRight.setAttribute('src', imageGallery.imagesDisplay[10]);
imageGallery.imageNoneRight.setAttribute('src', imageGallery.imagesDisplay[9]);
},
Can someone assist, I really need this to work?
If there is anything not clear or you need more code let me know.
Thanks,

First things first, the culprit was the setAttribute of all images i.e. whatever you were doing inside the rightClick and leftClick functions were the reasons why you were seeing a glitch. Changing src of an img tag produces the glitch.
But then we cannot simply remove it because your approach relies heavily on this swapping of images.
I had to breakdown and really understand your approach first. The way it worked was that you would animate, for example, image1 (the centered one) to move to the position of imageLeft upon click on the rightCarousel button. On that same click, you had a setTimeout of almost the duration of the animation to call rightClick function. This rightClick function then swaps the images so that image1 can always remain at the center and only images can come and go after animation. This was the problem.
What I had to change was that all image tags i.e. imageNoneLeft, imageLeft, image1, imageRight & imageNoneRight would change each others classes such that their position remains changed after animations.
Also, I had to add another animateImages line inside your leftSelect and rightSelect callbacks to animate the furthest images i.e. imageNoneLeft & imageNoneRight to animate to each other's positions with respect to the click of the buttons.
Take a look at this jsFiddle. It will help you understand a lot better. And let me know if you have any questions.
JavaScript:
var imageGallery={
prefix:'https://dl.dropboxusercontent.com/u/45891870/Experiments/StackOverflow/1.5/',
imagesDisplay:['JS.jpg','PIXI.jpg','GSAP.jpg','JS.jpg','PIXI.jpg','GSAP.jpg','JS.jpg','PIXI.jpg','GSAP.jpg','JS.jpg','PIXI.jpg'],
rightSelect:document.querySelector('.rightCarousel'),
leftSelect:document.querySelector('.leftCarousel'),
imageMain:document.querySelector('.image1'),
imageLeft:document.querySelector('.imageLeft'),
imageRight:document.querySelector('.imageRight'),
imageNoneLeft:document.querySelector('.imageNoneLeft'),
imageNoneRight:document.querySelector('.imageNoneRight'),
init:function(){
imageGallery.imagesDisplay.push(imageGallery.imagesDisplay.shift());
imageGallery.imageNoneLeft.setAttribute('src',imageGallery.prefix+imageGallery.imagesDisplay[2]);
imageGallery.imageLeft.setAttribute('src',imageGallery.prefix+imageGallery.imagesDisplay[1]);
imageGallery.imageMain.setAttribute('src',imageGallery.prefix+imageGallery.imagesDisplay[0]);
imageGallery.imageRight.setAttribute('src',imageGallery.prefix+imageGallery.imagesDisplay[10]);
imageGallery.imageNoneRight.setAttribute('src',imageGallery.prefix+imageGallery.imagesDisplay[9]);
},
animateImages:function(classFrom,classTo){
var classMoving=$(classFrom);
var classGoingTo=$(classTo);
classMoving.animate({
top:classGoingTo.css('top'),
left:classGoingTo.css('left'),
width:classGoingTo.css('width'),
opacity:classGoingTo.css('opacity')
},258,function(){
$(this).removeClass(classFrom.substr(1));
$(this).addClass(classTo.substr(1));
$(this).removeAttr('style');
});
}
};
imageGallery.init();
imageGallery.leftSelect.onclick=function(){
imageGallery.animateImages('.imageNoneRight','.imageNoneLeft');
imageGallery.animateImages('.imageRight','.imageNoneRight');
imageGallery.animateImages('.image1','.imageRight');
imageGallery.animateImages('.imageLeft','.image1');
imageGallery.animateImages('.imageNoneLeft','.imageLeft');
};
imageGallery.rightSelect.onclick=function(){
imageGallery.animateImages('.imageNoneLeft','.imageNoneRight');
imageGallery.animateImages('.imageLeft','.imageNoneLeft');
imageGallery.animateImages('.image1','.imageLeft');
imageGallery.animateImages('.imageRight','.image1');
imageGallery.animateImages('.imageNoneRight','.imageRight');
};

Related

Pause a function on hover of an element in Javascript

I've been working with some javascript code which is from codepen, I forked it for my own usage and added some code to auto rotate the outer circle.
What I am unable to do is modify the code to pause on the shown element when I hover over the centre element .circle--rotate
The code I am working with for myself is on https://codepen.io/CelticWebs/pen/oNMxdrK. To attempt to do what I wanted, auto animate as well as pass eon hover, I added the following code at line 111 in the javascript:
function movetheone() {
i.is(":animated") || (p.direction = "right", p.step = d, o())
}
let abc = setInterval(movetheone, 3000);
$('.circle--rotate').hover(() => {
clearInterval(abc);
}, () => {
movetheone
})
This spins the circle automatically, then does stop spinning when you hover over it but unfortunately it often moves forward one place before pausing, which make the hover pointless as it moves away from what you wanted to stop on. It also doesn't start back up when you move away.
Does anybody have any suggestions on making this work better please?
I've made the link to the code more obvious above for those asking for more of the code. Thanks.

Problem synchronizing DOM manipulation with style changes in carousel

I built a basic picture carousel a while back, and I'm finally getting around to transferring it from MooTools over to jQuery so I can drop MooTools. I've got the script completely functional, but for whatever reason when the carousel slides in one direction, you can see a "pop" where it resets itself.
I've tried playing around with the order it handles everything, but no matter what it seems to always desync for just a fraction of a section.
Here's a copy of my code: https://jsfiddle.net/Chaosxmk/pf6dzchm/
The offending section of code is this:
styles['position'] = 'absolute';
styles[self.params.axis] = -32768;
$(self.list[0]).css(styles).hide();
$(self.list[0]).appendTo(self.carousel);
$(self.list[conf.mi]).css(self.params.axis, (100-conf.pr)+'%');
styles = {};
styles['position'] = 'relative';
styles[self.params.axis] = 'auto';
$(self.list[conf.mi]).css(styles);
Issue is that $.fadeOut() sets display:none on the element, which causes some strange rendering issues in your setTimeout() callback. Works better if you use $.fadeTo() instead:
if (self.params.direction) {
// Go forward
self.carousel.css(self.params.axis, '-'+conf.pr+'%');
$(self.list[0]).fadeTo(400, 0);
$(self.list[conf.mi]).css(self.params.axis, '100%').fadeTo(400, 1);
} else {
// Go backward
self.carousel.css(self.params.axis, conf.pr+'%');
$(self.list[conf.mi-1]).fadeTo(400, 0);
self.list.last().css(self.params.axis, '-'+conf.pr+'%').fadeTo(400, 1);
}
For simplicity I used a 400ms duration, but you can set this to whatever you need.
JSFiddle

How to resolve mouse hover on overlapping images in jQuery?

This question seems somewhat related to
How do I check if the mouse is over an element in jQuery?
jQuery check hover status before start trigger
but still not quite. Here's the thing: I'm writing a small gallery and at some point the user clicks on a thumbnail. The large version of the image shows as an inside of a full-screen . So far, so good. When the mouse hovers over that I show three images: close, left, right, intended to navigate through an album; when the mouse leaves the image or the navigation images, the three navigation images fade.
These three navigation images partially overlap with the main image, e.g. the close image is a circle in the upper left corner. And that's the tricky part.
The mouseleave triggers whenever the mouse moves from the main image off the side, or from the main image onto one of the three small overlapping images. The mouseenter triggers for each of the small overlapping images as expected.
However, this creates a funky blinking effect because on mouseleave of the main image I hide() the three small images, which immediately triggers a mouseenter for the main image because of the overlap and the mouse still being on top of the main image.
To avoid that, I tried to determine if, upon mouseleave of the main image, the mouse has moved onto one of the small overlapping images. For that, I used this code:
main_img.mouseleave(function() {
if (!hoverButtons()) {
doHideButtons();
}
});
function hoverButtons() {
return (close_img.is(":hover")) || (left_img.is(":hover")) || (right_img.is(":hover"));
}
This works great on Safari and Chrome, but not on FF and IE where the images still blink. Further noodling around posts, it seems that ":hover" is the problem here as it is not a proper selector expression but rather a CSS pseudo class?
I tried to work with switches that I flip on/off as I mouseenter/mouseleave the various images, but that doesn't work either because the events seem to trigger in different orders.
How do I go about this? Thanks!
EDIT: I might have to clarify: before the navigation buttons are shown, I set their respective left and top attributes in order to place them in dependence of the main image's position. That means I need to do some work before I can call .show() on a jQuery selector. I tried to add a new function .placeShow() but that didn't quite work with respect to selectors like $(".nav-button:hidden").placeShow().
You can try with this:
$("#main, #small").mouseenter(function() {
$("#small:hidden").show();
}).mouseleave(function(e) {
if(e.target.id != 'main' || e.target.id != 'small') {
$('#small').hide();
}
});
DEMO
Here is what I ended up doing. There are four images I use in my slide show: the main image, and then left, right, close button images.
main_img = $("<img ... class='main-photo slide-show'/>");
close_img = $("<img ... class='nav-button slide-show'/>");
left_img = $("<img ... class='nav-button slide-show'/>");
right_img = $("<img ... class='nav-button slide-show'/>");
The classes here are essentially empty, but help me to select based on above answers. The main image then shows without navigation buttons, and I attach these event handler functions:
$(".slide-show").mouseenter(function() {
$(".photo-nav:hidden").placeShow();
});
$(".slide-show").mouseleave(function() {
$(".photo-nav").hide();
});
where the placeShow() moves the navigation buttons into their respective places. This function is defined as follows:
$.fn.placeShow = function() {
var pos = main_img.position();
var left = pos.left;
var top = pos.top;
var width = main_img.width();
var height = main_img.height();
close_img.css({ "left":"" + (left-15) + "px", "top":"" + (top-15) + "px" }).show();
left_img.css({ "left":"" + (left+(width/2)-36) + "px" , "top": "" + (top+height-15) + "px" }).show();
right_img.css({ "left":"" + (left+(width/2)+3) + "px", "top":"" + (top+height-15) + "px" }).show();
}
This worked so far on Safari, IE, FF, Chrome (well, the versions I've got here...)
Let me know what you think, if I can trim this code more, or if there are alternative solutions that would be more elegant/fast. The final result of all this is on my website now.
Jens

jQuery image crossfade with pre-loader

I want a simple image crossfade, similar to http://malsup.com/jquery/cycle/, but with a pre-loader. Is there a good jQuery plugin that does both? Also, I'm not looking for a load bar.
This question is close, but not the same => jQuery Crossfade Plugin
It would be great if it was a solution that defaulted to CSS3, but would otherwise fall back to JS to keep the processing native as possible.
Looking for something that..
will autoplay
without controls
will go to the next image based on time setting, ie. 5 seconds, unless the next image isn't loaded in which case it finishes loading the image and then displays it.
crossfade transition, not fade to black or white, but cross-fade. from the start it would fadein.
no thumbnails or galleries, etc. just the image
If images could be CSS background images, that would be best, so users can't drag out the image simply
Each panel needs to be clickable so a user could click the image and go to a part of the website.
Well, here's my poke at it. The preloader is in vanilla js and the slideshow loop is in jQuery. It's very simple to implement and the concept is even simpler.
Demo
a very simple Demo that illustrates the DOM manipulation approach
HTML
<!-- not much here... just a container -->
<div id="content"></div>
CSS
/* just the important stuff here. The demo has example styling. */
#content
{
position:relative;
}
#content img
{
position:absolute;
}
javascript/jQuery
// simple array
var images = [
"http://imaging.nikon.com/lineup/dslr/d90/img/sample/pic_003t.jpg",
"http://imaging.nikon.com/lineup/dslr/d90/img/sample/pic_005t.jpg",
"http://imaging.nikon.com/lineup/dslr/d90/img/sample/pic_001t.jpg"
];
// some adjustable variables
var delay = 2000;
var transition = 1000;
// the preloader
for(var i in images)
{
var img = document.createElement("img");
img.src = images[i];
img.onload = function(){
var parent = document.getElementById("content");
parent.insertBefore(this,parent.childNodes[0]);
if(i == images.length - 1)
{
i = 0;
startSlides();
}
}
}
// and the actual loop
function startSlides()
{
$("#content img:last").delay(delay).fadeTo(transition,0,function(){
$(this).insertBefore($(this).siblings(":first")).fadeTo(0,1);
startSlides();
});
}
The concept in brief is to fade the first image in a container, once complete change it's position in the DOM (effectively hiding it behind equal tree level siblings), and call the function again. The reason why this works is because it only fades the first child of the container, but on callback it changes what node that is constantly looping the nodes. This makes for a very small source file that is quite effective.
EDIT 1:
and 32 minutes tweaking later...
Demo 2
EDIT 2:
My oh so simple script is now very complicated :P I added in some scaling features that only work on modern browsers but are there if needed. This one also has a loading bar as it preloads the images (may or may not be desirable :P)
small images demo
large images demo
I think you can still do this with the jQuery cycle plugin; other than image preloading, even the jQuery cycle lite version does everything you want by default out-of-the-box.
And if you look here, you'll see that it's pretty simple to add a little Javascript that will add images (after the first two) as they load. You would need to modify the code a little (instead of stack.push(this), you'd want something like stack.push("<div style="background-image:url("+img.src+")"></div>"), for example) but I think it's totally doable.
Edit: here's a link to a SO question about how to make a div into a clickable link.
Edit 2: I liked Joseph's idea to just move the elements to a hidden DIV, so I updated my code a bit. It now also preserves the links each div points to as well: http://jsfiddle.net/g4Hmh/9/
Edit 3: Last update! http://jsfiddle.net/g4Hmh/12/
UPDATE Added the ability to load everything asynchronously.
A wrapper for the jQuery cycle plugin should suffice. You really just need something that monitors if the images loaded and then calls $(elem).cycle(/* options */). Here's my take:
$.fn.cycleWhenLoaded = function(options) {
var target = this,
images = options.images,
loaded = 0,
total = 0,
i;
if(images) {
for(i = 0; i < images.length; i ++) {
$('<img/>').attr('src', images[i]).appendTo(target);
}
}
this.find('> img').each(function(index) {
var img = new Image(),
source = this;
total ++;
if(index > 1)
$(this).hide();
img.onload = function() {
loaded ++;
if(loaded == total) {
target.trigger('preloadcomplete');
target.cycle(options);
}
};
setTimeout(function() {img.src = source.src}, 1);
});
return this;
};
This allows you to either do a simple delay load:
$('.slideshow').cycleWhenLoaded({
fx: 'fade'
});
Or you can do something more complicated and load your images in the script and capture the preload complete event:
$('.slideshow2').hide().cycleWhenLoaded({
fx: 'fade',
images: [
"http://cloud.github.com/downloads/malsup/cycle/beach1.jpg",
"http://cloud.github.com/downloads/malsup/cycle/beach2.jpg",
"http://cloud.github.com/downloads/malsup/cycle/beach3.jpg",
"http://cloud.github.com/downloads/malsup/cycle/beach4.jpg",
"http://cloud.github.com/downloads/malsup/cycle/beach5.jpg"
]
}).bind('preloadcomplete', function() { $(this).show(); });
You can see it in action here: http://fiddle.jshell.net/vmAEW/1/
I don't know how close this is to what you are looking for, but I figured since no one else did I would at least try to help. http://galleria.aino.se/
It at least has a preloader and a fade transition.

Complete function before starting next (fadeIn/fadeOut)

UPDATED (see notes at bottom)
I have created an image map and when you hover over a specific section of this image map a description will appear in a designated area (the sidebar) of my website.
Each description is of varying length therefore I have not set any maximum height level for my sidebar area so that the display can grow vertically to accomodate each description.
The problem I am having is that when you rapidly hover over areas of the image map the display produces some weird results; showing blocks up content from another hot spot for a split second in full beneath the newly hovered over area and corresponding description (hope that makes sense)
Is there anyway to complete one function in full before displaying the next to avoid this nasty display/animation?
Here is my code:
$(document).ready(function() {
$("#a-hover").hide();
$("#a").hover(function() {
$("#a-hover").fadeIn();
}).mouseleave(function() {
$("#a-hover").fadeOut();
});
$("#b-hover").hide();
$("#b").hover(function() {
$("#b-hover").fadeIn();
}).mouseleave(function() {
$("#b-hover").fadeOut();
});
$("#c-hover").hide();
$("#c").hover(function() {
$("#c-hover").fadeIn();
}).mouseleave(function() {
$("#c-hover").fadeOut();
});
And my CSS;
#a-hover,#b-hover,#c-hover {
z-index: 2;
float: left;
position: relative;
}
#a-hover,#b-hover,#c-hover,{
position: relative;
top: 0px;
left: 0px;
z-index: 1;
width:326px;
min-height:603px;
background-color:#dedddd;
}
I have shortened my code for readability (I have 9 image map hot spots)
I am a novice when it comes to jQuery but I am making a committment to learn so please go easy as my code may not be up to scratch!
I have tried to solve this myself before posting here, but I am out of my depth and need some expert advice
I appreciate any responses.
Thank You,
Wp.
UPDTAE: I tried the majority of what was provided here as answers and whilst I believe these answers are on the right track I couldn't get the problem to stop however I did notice improvement in the animations overall.
I ended up using a combination .stop(true,true); and **resize font automatically.
**Ultimately not getting the desired result is due to my lack of polish with jQuery but being in a rush I managed to find another way to handle this issue (auto resizable font).****
Thanks to all who took the time out to answer and for those reading this for a similar solution at least know the .stop(true,true); properties did in fact work for me to solve one part of this problem.
Try adding .stop before each fadeIn and fadeOut. You should pass true, true to stop to complete the animating instantly rather than leave it half faded in:
$("#a").hover(function() {
$("#a-hover").stop(true, true).fadeIn();
}).mouseleave(function() {
$("#a-hover").stop(true, true).fadeOut();
});
You can also get rid of all of the repetition by binding on a class instead of id's:
$(".imageMapElement").hover(function() {
$("#" + $(this).attr("id") + "-hover").stop(true, true).fadeIn();
}).mouseleave(function() {
$("#" + $(this).attr("id") + "-hover").stop(true, true).fadeOut();
});
May be you can try Jquery Hover Intent plugin.
try stopping the other functions:
$("#a").hover(function() {
$("#b-hover").stop().hide();
$("#c-hover").stop().hide();
$("#a-hover").fadeIn();
}).mouseleave(function() {
$("#a-hover").fadeOut();
});
Try adding .stop() before each .fadeIn and .fadeOut -- that will cancel any previous animations and immediately begin your new one.
You also have a problem with using .hover() -- that actually encapsulates two actions, mouseover and mouseout. When you assign two functions to it, the first is mouseover and the second is mouseout, but when you assign only one function to it, that one function is used for both mouseover and mouseout. So, in effect, your code is causing the element to fadeIn and fadeOut on mouseout.
Incidentally, you can shorten your code a lot using standard jQuery techniques:
$("#a-hover,#b-hover,#c-hover").hide().hover(function() {
$(this).stop().fadeIn();
}, function() {
$(this).stop().fadeOut();
});
...or even better yet, assign a class to each of those three IDs and select it instead.
You have to chain all the jQuery function calls!

Categories

Resources