JQuery image slider and CSS transition works only once - javascript

I'm trying to learn jQuery plugins. I'm creating a image sliding plugin. This is how I've developed so far.
(function($){
$.imageSlider = function(selector, settings) {
//settings
var config = {
'delay':2000,
'speed':500
};
if(settings) {
$.extend(config, settings);
}
//vars
var obj = $(selector);
obj.children('img').css('transition','width 2s, height 2s, transform 2s');
var image = obj.children('img');
var count = image.length;
var i = 0;
image.eq(0).show(); //first image showing
//begin the image loop
setInterval ( function() {
image.eq(i).fadeOut(config.speed);
i = (i+1 == count) ? 0 : i+1;
image.eq(i).fadeIn(config.speed);
image.eq(i).css("transform","rotate(360deg)");
}, config.delay
);
return this;
}
})(jQuery);
But my the issue is the rotation happens only once cycle.
JSFiddle http://jsfiddle.net/va45D/1/
After all 3 images loaded as the way I wanted, then It doesn't applies the transition.
Please help me to understand whats happening here.

Hie Shan,
I can reproduce your problem, you're right. After the second time that you rotate your images they will not rotate anymore. I'm using Firefox 25.
To solve you problem I made these updates:
setInterval( function() {
image.eq(i).fadeOut(config.speed);
image.eq(i).css("transform","rotate(0deg)");
i = (i+1 == count) ? 0 : i+1;
image.eq(i).fadeIn(config.speed);
image.eq(i).css("transform","rotate(360deg)");
}, config.delay
);
When your loop over, the element keeps the same value at the end of the loop, so when your run it for the first time your have all your img elements at 0deg, at the end you transform them to 360deg position. The next time that you run your loop (this explain the problem that you have on your 2nd time), all your images starts on 360deg. If you rotate 360deg to 360deg you have the same position because there is no interval between the actual position and the new one. This can be really visible if you update your code to 45deg, as you can see on this fiddle.
Before start the process I defined a transformation that returns your element to 0 degrees. Maybe this solution solves your problem.
Here is the JSFiddle
Thanks!

Related

JSFiddle Slider code not working in browser

I'm a little lost. I have used this JSFiddle code regarding a solution to my problem. http://jsfiddle.net/f8d1js04/2/ (thank you MartinWebb) - and the question(for context) is posed here; Add Fade Effect in Slideshow (Javascript) - MartinWebb's solution is towards the bottom.
Essentially I am rendering a slider onto the page. Now when I put random image links into the imgArray on the JSFiddle, it behaves perfectly. However, when I do it on my page and in my files (in the dom), after the last image, there is a gap - time w/ no image showing, and then it continues to run. How do I get rid of this momentary gap?
Also any ideas on what could be causing this to happen on the browser instead of the JSFiddle?
Sincerely thank you for any help & assistance!
var curIndex = 0,
imgDuration = 3000,
slider = document.getElementById("slider"),
slides = slider.childNodes; //get a hook on all child elements, this is live so anything we add will get listed
imgArray = [
'http://placehold.it/300x200',
'http://placehold.it/200x100',
'http://placehold.it/400x300'];
//
// Dynamically add each image frame into the dom;
//
function buildSlideShow(arr) {
for (i = 0; i < arr.length; i++) {
var img = document.createElement('img');
img.src = arr[i];
slider.appendChild(img);
}
// note the slides reference will now contain the images so we can
access them
}
//
// Our slideshow function, we can call this and it flips the image instantly, once it is called it will roll
// our images at given interval [imgDuration];
//
function slideShow() {
function fadeIn(e) {
e.className = "fadeIn";
};
function fadeOut(e) {
e.className = "";
};
// first we start the existing image fading out;
fadeOut(slides[curIndex]);
// then we start the next image fading in, making sure if we are at the end we restart!
curIndex++;
if (curIndex == slides.length) {
curIndex = 0;
}
fadeIn(slides[curIndex]);
// now we are done we recall this function with a timer, simple.
setTimeout(function () {
slideShow();
}, imgDuration);
};
// first build the slider, then start it rolling!
buildSlideShow(imgArray);
slideShow();

How to evenly time fading transitions?

so I'm trying to create a simple slide show from scratch, and so far I was able to get full screen images to fade out and fade in infinetly, but for some odd reason using setInterval(function(){fade(var)}, 3500);didn't work, maybe someone can explain why the first and last images took way longer than 3,5 seconds to fade. Meanwhile, I was trying to solve that problem by implementing a callback function in the fade(). My example has four images, and they start fading out until it reaches image one, then don't fade out image one and start fading back in image two until image 4, and do this forever, here is my recent attempt to implement a callback function:
var i = 4;
$(document).ready(function(){
fade(i, fade);
});
var fadeIN = false;
function fade(objectID, callbackfn){
var fadeTime = 3500;
if(!fadeIN){
$("#slide-"+objectID).fadeOut(fadeTime);
i--;
if(i === 1) {
fadeIN = true;
}
}
else{
i++;
$("#slide-"+objectID).fadeIn(fadeTime);
if(i === 4){
fadeIN = false;
}
}
if(arguments[1]){
callbackfn(i);
}
}
But that is not working, it fades out image 4, image 3 and stops on image 2. Maybe there is a way to evenly time the fading transitions using the setIntervel(), if so can someone tell me how? Appreciate any help.
Here is a JSFiddle to the code: http://jsfiddle.net/8kgc0chq/ it is not working tho.
Here is the doc for .fadeOut()
There is an optional argument complete:
A function to call once the animation is complete.
Put the next animation in there, they too take a complete (callback) function.
$("#id").fadeOut(fadeTime, function() {
// code to execute after animation complete
});
You need to do it properly with javascript. Easy way fails after last element.
So here is my solution. Can it be improved further, I think yes.. But it does work now. And is future proof to some extent.
I cleaned up css and changed html structure a little.
Demo: http://jsfiddle.net/8kgc0chq/3/
$(document).ready(function () {
$(window).resize(function () {
realTimeHeight();
});
realTimeHeight();
startSlides();
});
function startSlides() {
var fadeTime = 1000,
delay = 1300,
i = 0,
slides = $("#hero-slider > .slide"),
len = slides.length;
slides.hide();
var pF = $('<div class="slide">'), pB = pF.clone();
pF.attr('id', slides.eq(i).attr('id'));
$('#hero-slider').prepend(pF).prepend(pB);
setInterval(fadeThisIn, fadeTime + delay);
function fadeThisIn() {
pB.attr('id', pF.attr('id'));
i = ++i % len;
pF.hide().attr('id', slides.eq(i).attr('id')).fadeIn(fadeTime);
}
}
function realTimeHeight() {
var altura = $(window).height();
$("#hero-slider").css("height", altura);
}

Infinite scrolling div glitching with images

I'm currently using the following javascript as shown below.
It's working well when I place just text within the div .image_scroll_3 but as soon as I insert images the scroll glitches and won't move past the top of the image.
Any advice would be much appreciated
JS
<script>
(function($, undefined) {
$.fn.loopScroll = function(p_options) {
var options = $.extend({
direction: "upwards",
speed: 60
}, p_options);
return this.each(function() {
var obj = $(this).find(".image_scroll_2");
var text_height = obj.find(".image_scroll_3").height();
var start_y, end_y;
if (options.direction == "downwards") {
start_y = -text_height;
end_y = 0;
} else if (options.direction == "upwards") {
start_y = 0;
end_y = -text_height;
}
var animate = function() {
// setup animation of specified block "obj"
// calculate distance of animation
var distance = Math.abs(end_y - parseInt(obj.css("top")));
//alert("animate " + obj.css("top") + "-> " + end_y + " " + distance);
//duration will be distance / speed
obj.animate(
{ top: end_y }, //scroll upwards
1500 * distance / options.speed,
"linear",
function() {
// scroll to start position
obj.css("top", start_y);
animate();
}
);
};
obj.find(".image_scroll_3").clone().appendTo(obj);
$(this).on("mouseout", function() {
obj.stop();
}).on("mouseout", function() {
animate(); // resume animation
});
obj.css("top", start_y);
animate(); // start animation
});
};
}(jQuery));
$("#example4").loopScroll({ speed: 700 });
</script>
I think the problem is that your text_height is calculated before the images are actually loaded inside your .image_scroll_3 elements. So you'll need to wait for the images to load.
Put your loopScroll call inside a $(window).load like so:
$(window).load(function(){
$('#example4').loopScroll({speed:700});
});
That massive glitch should now be gone as the fix above should have helped mitigate it.
However, there is still some unwanted jank / stutter (don't want to use the word glitch again, lets keep it reserved for the initial problem) in movement of all images if you notice and I am guessing that is probably because we are animating the whole thing too fast. Passing in speed like 100 or 200 resolves that but this is not really a solution because, ideally, you should be able to put in any speed value and it should just produce smooth animations out of it.
I am working on exactly the same thing but before that, I want to know if the above fix for the glitch helps you and we are finally done with it? Let me know.
Update:
Here is my version that I spoke of earlier, for your perusal.
Because all you are trying to do is loop images in a very linear fashion, I, for one, do not see the need to rely on animate() function of jQuery. There is requestAnimationFrame API that I have leveraged instead. In fact, in my demonstration below I have completely abandoned jQuery in favour of vanilla JavaScript only because I kept finding alternatives to pretty much everything we needed to do in this demo. But of course, this is also a very subjective matter; a taste thing; so if you want to go with jQuery, then by all means.
Another fundamental change I brought is rather than updating top values, I have resorted to updating translateY values.
Take a look at this jsFiddle and let me know if it fits your needs.
JavaScript code of which is as belows:
// [http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/]
window.requestAnimFrame=(function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(callback){window.setTimeout(callback,1000/60);};})();
var main=null;
var imageScroll2=null;
var imageScroll3=null;
var totalHeight=null;
var initY=null;
var destY=null;
var currY=null;
var increment=null;
var direction=null;
var UP=null;
var DOWN=null;
var isPlaying=null;
function init(){
main=document.getElementById('example4');
imageScroll2=main.getElementsByClassName('image_scroll_2')[0];
imageScroll3=main.getElementsByClassName('image_scroll_3')[0];
totalHeight=imageScroll3.clientHeight;
UP='upwards';
DOWN='downwards';
isPlaying=true;
direction=UP;
increment=10;
if(direction===DOWN){
initY= -totalHeight;
destY=0;
}else{
initY=0;
destY= -totalHeight;
}
currY=initY;
imageScroll2.appendChild(imageScroll3.cloneNode(true));
if(imageScroll2.addEventListener){
imageScroll2.addEventListener('mouseover',function(){isPlaying=false;},false);
imageScroll2.addEventListener('mouseout',function(){isPlaying=true;},false);
}else{
imageScroll2.attachEvent('onmouseover',function(){isPlaying=false;});
imageScroll2.attachEvent('onmouseout',function(){isPlaying=true;});
}
requestAnimFrame(render);
}
function render(){
if(isPlaying){
imageScroll2.style.transform='translate(0px,'+currY+'px)';
if(direction===DOWN){
currY+=increment;
if(currY>=destY){currY=initY;}
}else{
currY-=increment;
if(currY<=destY){currY=initY;}
}
}
requestAnimFrame(render);
}
//
init();

Javascript fade in doesn't visibly animate

So I've created the following function to fade elements in and passed in a div that I want to fade in which in this case is an image gallery popup that I want to show when a user clicks an image thumbnail on my site. I'm also passing in a speed value (iSpeed) which the timeout uses for it's time value. In this case I'm using 25 (25ms).
I've stepped through this function whilst doing so it appears to be functioning as expected. If the current opacity is less than 1, then it is incremented and it will recall itself after the timeout until the opacity reaches 1. When it reaches one it stops fading and returns.
So after stepping through it, I take off my breakpoints and try to see it in action but for some reason my gallery instantly appears without any sense of fading.
var Effects = new function () {
this.Fading = false;
this.FadeIn = function (oElement, iSpeed) {
//set opacity to zero if we haven't started fading yet.
if (this.Fading == false) {
oElement.style.opacity = 0;
}
//if we've reached or passed max opacity, stop fading
if (oElement.style.opacity >= 1) {
oElement.style.opacity = 1;
this.Fading = false;
return;
}
//otherwise, fade
else {
this.Fading = true;
var iCurrentOpacity = parseFloat(oElement.style.opacity);
oElement.style.opacity = iCurrentOpacity + 0.1;
setTimeout(Effects.FadeIn(oElement, iSpeed), iSpeed);
}
}
}
Here's where I'm setting up the gallery.
this.Show = function (sPage, iImagesToDisplay, oSelectedImage) {
//create and show overlay
var oOverlay = document.createElement('div');
oOverlay.id = 'divOverlay';
document.body.appendChild(oOverlay);
//create and show gallery box
var oGallery = document.createElement('div');
oGallery.id = 'divGallery';
oGallery.style.opacity = 0;
document.body.appendChild(oGallery);
//set position of gallery box
oGallery.style.top = (window.innerHeight / 2) - (oGallery.clientHeight / 2) + 'px';
oGallery.style.left = (window.innerWidth / 2) - (oGallery.clientWidth / 2) + 'px';
//call content function
ImageGallery.CreateContent(oGallery, sPage, iImagesToDisplay, oSelectedImage);
//fade in gallery
Effects.FadeIn(oGallery, 25);
}
Could anyone help me out?
Also, I'm using IE10 and I've also tried Chrome, same result.
Thanks,
Andy
This line:
setTimeout(Effects.FadeIn(oElement, iSpeed), iSpeed);
calls Effects.FadeIn with the given arguments, and feeds its return value into setTimeout. This is exactly like foo(bar()), which calls bar immediately, and then feeds its return value into foo.
Since your FadeIn function doesn't return a function, that would be the problem.
Perhaps you meant:
setTimeout(function() {
Effects.FadeIn(oElement, iSpeed);
}, iSpeed);
...although you'd be better off creating that function once and reusing it.
For instance, I think this does what you're looking for, but without recreating functions on each loop:
var Effects = new function () {
this.FadeIn = function (oElement, iSpeed) {
var fading = false;
var timer = setInterval(function() {
//set opacity to zero if we haven't started fading yet.
if (fading == false) { // Consider `if (!this.Fading)`
oElement.style.opacity = 0;
}
//if we've reached or passed max opacity, stop fading
if (oElement.style.opacity >= 1) {
oElement.style.opacity = 1;
clearInterval(timer);
}
//otherwise, fade
else {
fading = true;
var iCurrentOpacity = parseFloat(oElement.style.opacity);
oElement.style.opacity = iCurrentOpacity + 0.1;
}
}, iSpeed);
};
};
Your code has a lot of problems. The one culpable for the element appearing immediately is that you call setTimeout not with a function but with the result of a function, because Effects.FadeIn will be executed immediately.
setTimeout(function(){Effects.FadeIn(oElement, iSpeed)}, iSpeed);
will probably act as you intend.
But seriously, you probably should not re-invent this wheel. jQuery will allow you to fade elements in and out easily and CSS transitions allow you to achieve element fading with as much as adding or removing a CSS class.
T.J. and MoMolog are both right about the bug: you're invoking the Effects.FadeIn function immediately before passing the result to setTimeout—which means that Effects.FadeIn calls itself synchronously again and again until the condition oElement.style.opacity >= 1 is reached.
As you may or may not know, many UI updates that all take place within one turn of the event loop will be batched together on the next repaint (or something like that) so you won't see any sort of transition.
This jsFiddle includes the suggested JS solution, as well as an alternate approach that I think you may find to be better: simply adding a CSS class with the transition property. This will result in a smoother animation. Note that if you go this route, though, you may need to also include some vendor prefixes.

Image Rotation using pure Javascript

PLEASE DO NOT RECOMMEND JQUERY - I AM DOING THIS EXERCISE FOR LEARNING PURPOSES.
I have implemented a JavaScript, which rotates images (_elementSlideChange) on a timer, using a set interval of 10 seconds. Also I have added a slide functionality to this, which is 7 milliseconds (_slideImage).
The image rotates automatically every 10 seconds on page load, and I have also provided next and previous buttons, which allow the user to change the images manually.
_elementSlideChange: function () {
var myString;
var myText;
for (var i = 0; i < this._imgArray.length; i++) {
var imageArr = "url(" + this._imgArray[i].src + ")";
var imageBg = this._imageHolder.style.background + "";
if (imageArr == imageBg) {
if (i == (this._imgArray.length - 1)) {
myString = "url(" + this._imgArray[0].src + ")";
myText = this._infoArray[0];
} else {
myString = "url(" + this._imgArray[(i + 1)].src + ")";
myText = this._infoArray[i + 1];
}
}
}
this._imageNextSlide.style.background = myString;
this._imageNextSlide.style.background);
this._infoElement.innerHTML = myText;
this._myTimer = setInterval(MyProject.Utils.createDelegate(this._slideImage, this), 7);
},
_slideImage: function () {
if (parseInt(this._imageHolder.style.width) >= 0 && parseInt(this._imageNextSlide.style.width) <= 450) {
this._imageHolder.style.backgroundPosition = "right";
this._imageHolder.style.width = (parseInt(this._imageHolder.style.width) - 1) + 'px';
console.log(this._imageNextSlide.style.background);
this._imageNextSlide.style.width = (parseInt(this._imageNextSlide.style.width) + 1) + 'px';
} else {
console.log("reached 0px");
if (parseInt(this._imageHolder.style.width) == 0) {
this._imageHolder.style.background = this._imageNextSlide.style.background;
this._imageHolder.style.width = 450 + 'px';
this._imageHolder === this._imageNextSlide;
this._imageHolder.className = "orginalImage";
this._imageNextSlide.style.width = 0 + "px";
this._imageNextSlide = this._dummyImageNextSlide;
this._imagesElement.appendChild(this._imageHolder);
this._imagesElement.appendChild(this._imageNextSlide);
clearInterval(this._myTimer);
}
clearInterval(this._myTimer);
clearInterval(this._elementSlideChange);
}
}
So when the user clicks on the Next arrow button, the event listener for "click" is triggered. This creates a div for the current image on display, and creates a new div, which will contain the next image. The image slide and rotation works correctly (whether it's onLoad or onClick). The issue I have is if I click the Next button, while the new div image is sliding into position, it causes it to run into an infinite loop, so the same div with the image to be displayed keeps sliding in, and the more you click the Next button, the faster the image starts to rotate.
I have tried putting a clear interval for the image rotation and slider, but I do understand my code is wrong, which causes the infinite loop of the sliding image. And I know I am close to finishing the functionality.
Can anyone please advise where I could be going wrong? Or should I try to implement the sliding DIV in another way?
Once again please don't recommend jQuery.
And thank you for your help in advance.
Kush
To solve the issue, I did re-write the entire code, where I had a next and previous button event listener.
myProject.Utils.addHandler(this._nextImageElement, "click", myProject.Utils.createDelegate(this._changeImage, this));
Both the buttons will call the same function :
_changeImage: function (e)
In this function I check to see if the function is Transition (changing images),
I declare a boolean var forward = e.target == this._nextImageElement;
Then check to see the current index if forward ? Add 1 else minus 1
this._currentImageIndex += forward ? 1 : -1;
If its at the end of the Array and forward is true, assign the this._currentImageIndex to reset to 0 or Array.length – 1 if it’s in reverse
Then call another function which gives the ‘div’ a sliding effect. In this case call it this._transitionImage(forward);
In this function, set the this._inTranstion to true. (Because the div’s are sliding in this case).
The following code solved the issue i was having.
this._slideImageElement.style.backgroundImage = "url(\"" + this._imgArray[this._currentImageIndex].src + "\")";
this._slideImageElement.style.backgroundPosition = forward ? "left" : "right";
this._slideImageElement.style.left = forward ? "auto" : "0px";
this._slideImageElement.style.right = forward ? "0px" : "auto";
The above code is very important as the object is to place the “sliding in div” Left or Right of the current Visible “div” to the user, and this is mainly dependent on if the forward variable is true or false.
var i = 0;
Then start the transition by
setInterval( function() {
this._currentImageElement.style.backgroundPosition = (forward ? -1 : 1) * (i + 1) + "px";
this._slideImageElement.style.width = (i + 1) + "px";
Notice the forward will determine if the bgPosition will go to the left if its forward as we multiple by -1 or +1,
So for example
If the user clicks NEXT BUTTON,
Forward = true
So the first thing we do is set the
this._slideImageElement.style.backgroundPosition = "left"
Then
this._slideImageElement.style.left = "auto"
this._slideImageElement.style.right = "0px"
This means when the sliding image moves in its background position is LEFT but the div is placed on the RIGHT to 0px;
then this._currentImageElement.style.backgroundPosition = -1 * (i + 1)
Which moves the position of the currentImageElement to the left by 1px,
Increase the width of the slideImage which in this case is right of the current div,
and as the current div moves to the left the sliding image starts to appear from the right. (By default set the width of slideImageElement to 0px so the div exists but isn’t visible to the user). This gives it the slide effect of moving forward new image coming from the right.
this._slideImageElement.style.width = (i + 1) + "px";
then declare it to stop when it it’s the image width. In this case it will be 500px.
if ((i = i + 2) == 500) {
In this if statement reset the currentImageElement background and the background position “right” or “left” don’t really matter as long it has been reset.
Clear the interval
Set the transition to false again
Then call a setTimeout for the function changeImage, which will continue until the slide is completed.
The following shows the reset code as this is very important to prevent repeating the same image (This solved my entire issue)
// set the current image to the "new" current image and reset it's background position
this._currentImageElement.style.backgroundImage = "url(\"" + this._imgArray[this._currentImageIndex].src + "\")";
this._currentImageElement.style.backgroundPosition = "right";
// reset the slide image width
this._slideImageElement.style.width = "0px";
// clear the transition interval and mark as not in transition
clearInterval(this._transitionInterval);
this._inTransition = false;
// setup the next image timer
this._nextImageTimeout = setTimeout(myProject.Utils.createDelegate(this._changeImage, this), 2500);
}
I have provided a thorough detail because then it easier to understand the logic of the problem, and even if your not having the same issue, this may help you fingure out any problem.
I couldn't provide a JSfiddle, as i have created my CSS using Javascript, there are different ways of doing this, but i wanted to understand the logic behind the forward and reverse, and having a timer which continuously goes forward.
It seems like you want to cancel the animation on the slide (perhaps have it fade out while the next slide animates in, cancel its animation abruptly or let it finish and ignore the button click)
What I usually do, personally, is check for the animated state (yes, I use jquery, but you should be able to test the CSS or positioning values you are using to animate in the same way) you could even add an "active" class or data type during animation to make testing easier. Global flags work, too. If there is animation, ignore the button. (For my work... Depends on your intention)
Like I said, the problem may be with button behaviour not with the animation routine. It would be useful to see how you are calling this from the button click, and what your intended results are going to be.
How about CSS3 transitions?
transition: all 1s ease 0.5s;
Simple example on JS Fiddle.
This takes care of the animation, so you just need to set the intended destination using JavaScript, i.e.
this.style.left = '100px';
Or
this.style.top = '30px';
And CSS3 transitions will smoothly slide the element.
Cross Browser Note!
The transition property may need a vendor prefix for some browsers, I am using the latest production Firefox and you don't need -moz for that. Same goes for Opera, no '-o' required. Internet Exporer 10 needs no prefix. You may need to use -webkit for Safari / Chrome, but test without first.

Categories

Resources