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.
Related
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
It seems that Chrome lazy loads hover images only when they are needed. For example, an image for :hover is loaded only when a mouse is hovered over an element. Is this an expected behavior? Does it encompass all images or only those defined for pseudo classes? How do I force it to load all images once the page is loaded?
Chrome do not "preload" images which aren't actally shown and, being this a desiderable effect, all modern browser shouldnt't.
To "force" browser in having all image ready in the cache when needed you may follow different approuches:
1) Preload required images with Javascript, you can use something like this:
var prld = ["one.gif", "two.gif", "three.jpg", "..."];
var img = [];
for (i = 0; i < prld.length; i++) {
img[i] = new Image();
img[i].src = prld[i];
}
but you might written in different ways, of course, that's just a suggestion.
2) The second method has a different approach, it is based on the idea that with a classic Javascript preload you have anyway to make as many "calls" as the images actually are. So 10 images with preload produces 20 calls to the server... not really a desiderable thing cause the "delay" of the calls will slowup all the page load...
So we put more than one image into a single files (like a "puzzle") than we put images as a background (of an empty DIV for instance) passing a different positioning of the background trought CSS.
No need to build a unique file for ALL the images, for instance you would decide for building several mosaics of two images just for the preloading purpose; so the :hover will "replace" the background rather than make a substitution.
The technique is described here: http://www.w3schools.com/css/css_image_sprites.asp
Each of those metods has its downside, it depends on situation.
All in all, as a general consideretion, i'd avoid "hovering" at all, taking in account that, on mobile experience, is, basically, without meaning... and it would lead to additional efforts to avoid mobile "extraload" useless contens...
Use this to force preloading for all img
var prld = document.querySelectorAll('img');
var img = [];
for (i = 0; i < prld.length; i++) {
img[i] = new Image();
img[i].src = prld[i].src;
}
This does NOT include images which are set in a div as background for example.
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');
};
I have a script which generates a random number so that when setImg(); is called a randomly selected image appears:
<img src="" id="imgRand" alt="">
function setImg(){
var numRand = Math.floor(Math.random()*(6 - 1 + 1)) + 1;
document.getElementById("imgRand").src = "images/"+numRand+".jpg";
}
This all works fine, but when the image changes, it just 'appears'. I was wondering if there was any way to get it to fade from one to the other? Everything I've found online talks about setting styles on each individual image, but as Im using this random number script to source my images, I cant think of a way to adapt any of those solutions.
Any help is much appreciated, thanks!
I will provide you with an example using CSS3 transitions. You can adapt and improve it for your specific case.
My specific example works only with Webkit as it is written since the implementation of the transcription end callback is vendor dependent. You can fix this by using the correct vendor event handler names.
/* Fades an element to given opacity */
var fade = function(opacity, fn) {
this.addEventListener("webkitTransitionEnd", function end() {
this.removeEventListener("webkitTransitionEnd", end);
fn && fn.call(this);
}, false);
this.style.opacity = opacity;
};
/* Preloads an image */
var load = function(src, fn) {
var self = this, $img = new Image();
$img.onload = function() {
fn && fn.call(self);
};
$img.src = src;
};
/* Steps:
* 1. Fades out current image.
* 2. Preloads next image.
* 3. When loading of next image is complete, sets next image.
* 4. Fades in.
*/
var $img = document.getElementById("imgRand");
/* Fades out */
fade.call($img, 0, function() {
/* Get random dimensions */
var height = Math.ceil(Math.random() * 100) + 100;
var width = Math.ceil(Math.random() * 200) + 100;
var src = "http://placehold.it/" + width + "x" + height;
/* Preloading */
load.call(this, src, function() {
$img.setAttribute("src", src);
/* Fades in */
fade.call($img, 1);
});
});
You can see it here.
The img element has -webkit-transition-duration style property set to 1s.
The most complicated and overlooked part of this is image preloading. Because, unless you preload all images that you want to use, the animation won't be smooth. But at the same time the detection of when an image has been loaded is not an easy task and the method that I'm using is a naive one that most probably will fail for images in the browser's cache. I won't go into details about that, you can search SO for it.
Disclaimer: It is too freaking late. So, I will just dump the code here and come to it later. If there's doubts.
This can be done most easily using a library such as jQuery but here is a jsFiddle example. I use absolute positioning to have two images placed over the top of each other and give one of them an opacity of 0. Then I just toggle between the two and fade one in and one out using helper functions.
The html looks something like this:
<div id="imageHolder">
<img id="imgRand0" src="http://placehold.it/100x100" />
<img id="imgRand1" src="http://placehold.it/100x100" style="opacity:0;alpha(opacity:0);"/>
</div>
<button onclick="setImg()">New Image</button>
The CSS:
img {
position:absolute;
top:0;
left:0;
}
#imageHolder {
position:relative;
height:100px;
}
And the javascript (I use additional functions from this tutorial):
var counter = 0;
function setImg(){
var numRand = Math.floor(Math.random()*6) + 1;
//document.getElementById("imgRand").src = "images/"+numRand+".jpg";
counter = (counter + 1) % 2;
document.getElementById("imgRand" + counter).src = "http://placehold.it/100&text=" + numRand;
fade('imgRand0');
fade('imgRand1');
}
This was too long to put into a comment, but hopefully it will guide you in the right direction.
So, because you're replacing the src of imgRand with each call to setImg, the two images you're trying to cross-fade will not be present in the DOM at the same time. You will probably need to have two separate image tags that are stacked on top of each other with CSS.
Then you will need to set the src on the top image (the one you want to fade in) and hide this image with CSS or your image will just 'appear' as soon as you set the src.
Then you will want to fade the top image in incrementally by setting the opacity of the image until it's 100...
Sorry for such a crazy description, but it's probably far easier if you have jQuery available. I will go and hunt down an example and update this.
Update: If you have jQuery, this is a rudimentary example of what your script might look like: http://jsfiddle.net/tracyfu/W9wMh/ Some of the other solutions here might be better if you're confined to straight JS.
I have this slider on my website:
http://css-tricks.com/examples/AnythingSlider/
It works fine, but I don't like the way it loads (you can see list of the images with list dots before it is ready).
Is there a universal way of bypassing that? How to load the slider in the background so users don't see it UNTIL it's fully loaded (while it loads in the background I could display preloader.gif for example).
I was thinking about opacity: 0 & fading it after the slider in DOM, but maybe there's other way?
I tend to use the following pattern:
// assumes slider is hidden
var imgCount = $("#slider img").length;
var loadCount = 0;
$("#slider img").one("load", function() {
loadCount++;
if(loadCount === imgCount) {
// show slider once all images have loaded
showSlider();
}
}).each(function() {
if(this.complete) $(this).trigger("load");
});
I would say apply css
.anythingSlider
{
display:none;
}
and then change it with jQuery after the slider is loaded.